From ae11601b80b984859deda1f2d430a23bfabd3bea Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Wed, 15 Oct 2014 12:34:59 +0530 Subject: ASoC: core: Call mute for cpu dais as well We call mute for codec dai only, we should call this for cpu dai as well to allow cpu dais (FEs) in DSPs to be muted/unmuted on shutdown/startup Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 002311a..1cd027a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) codec_dai->rate = 0; } + snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); + if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) for (i = 0; i < rtd->num_codecs; i++) snd_soc_dai_digital_mute(rtd->codec_dais[i], 0, substream->stream); + snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); out: mutex_unlock(&rtd->pcm_mutex); -- cgit v0.10.2 From fd587e320041d42eb21d12bb62da9e8ac08fd6c2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 6 Oct 2014 23:14:23 +0800 Subject: ASoC: cs4265: Remove unused *dev field from struct cs4265_private Signed-off-by: Axel Lin Acked-by: Paul Handrigan Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 4fdd47d..ce60868 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -32,7 +32,6 @@ #include "cs4265.h" struct cs4265_private { - struct device *dev; struct regmap *regmap; struct gpio_desc *reset_gpio; u8 format; @@ -598,7 +597,6 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, GFP_KERNEL); if (cs4265 == NULL) return -ENOMEM; - cs4265->dev = &i2c_client->dev; cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap); if (IS_ERR(cs4265->regmap)) { -- cgit v0.10.2 From c973b8a7dc50ace86393f209b19aa7fd0bfaf66b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 6 Oct 2014 23:09:47 +0800 Subject: ASoC: cs4271: Split SPI and I2C code into different modules Currently the cs4271 driver depends on SND_SOC_I2C_AND_SPI. So the driver cannot be built as built-in if CONFIG_I2C=m. Split SPI and I2C code into different modules to avoid this issue. Signed-off-by: Axel Lin Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index 5477c54..7b7fbcd 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -36,7 +36,8 @@ config SND_EP93XX_SOC_EDB93XX tristate "SoC Audio support for Cirrus Logic EDB93xx boards" depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A) select SND_EP93XX_SOC_I2S - select SND_SOC_CS4271 + select SND_SOC_CS4271_I2C if I2C + select SND_SOC_CS4271_SPI if SPI_MASTER help Say Y or M here if you want to add support for I2S audio on the Cirrus Logic EDB93xx boards. diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..7650625 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -50,7 +50,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS42L73 if I2C select SND_SOC_CS4265 if I2C select SND_SOC_CS4270 if I2C - select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI + select SND_SOC_CS4271_I2C if I2C + select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if I2C @@ -370,8 +371,19 @@ config SND_SOC_CS4270_VD33_ERRATA depends on SND_SOC_CS4270 config SND_SOC_CS4271 - tristate "Cirrus Logic CS4271 CODEC" - depends on SND_SOC_I2C_AND_SPI + tristate + +config SND_SOC_CS4271_I2C + tristate "Cirrus Logic CS4271 CODEC (I2C)" + depends on I2C + select SND_SOC_CS4271 + select REGMAP_I2C + +config SND_SOC_CS4271_SPI + tristate "Cirrus Logic CS4271 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_CS4271 + select REGMAP_SPI config SND_SOC_CS42XX8 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5dce451..ac7ec31 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -41,6 +41,8 @@ snd-soc-cs42l73-objs := cs42l73.o snd-soc-cs4265-objs := cs4265.o snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o +snd-soc-cs4271-i2c-objs := cs4271-i2c.o +snd-soc-cs4271-spi-objs := cs4271-spi.o snd-soc-cs42xx8-objs := cs42xx8.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o snd-soc-cx20442-objs := cx20442.o @@ -217,6 +219,8 @@ obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o +obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o +obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c new file mode 100644 index 0000000..b264da0 --- /dev/null +++ b/sound/soc/codecs/cs4271-i2c.c @@ -0,0 +1,62 @@ +/* + * CS4271 I2C audio driver + * + * Copyright (c) 2010 Alexander Sverdlin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "cs4271.h" + +static int cs4271_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap_config config; + + config = cs4271_regmap_config; + config.reg_bits = 8; + config.val_bits = 8; + + return cs4271_probe(&client->dev, + devm_regmap_init_i2c(client, &config)); +} + +static int cs4271_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id cs4271_i2c_id[] = { + { "cs4271", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cs4271_dt_ids), + }, + .probe = cs4271_i2c_probe, + .remove = cs4271_i2c_remove, + .id_table = cs4271_i2c_id, +}; +module_i2c_driver(cs4271_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS4271 I2C Driver"); +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c new file mode 100644 index 0000000..acd49d8 --- /dev/null +++ b/sound/soc/codecs/cs4271-spi.c @@ -0,0 +1,55 @@ +/* + * CS4271 SPI audio driver + * + * Copyright (c) 2010 Alexander Sverdlin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "cs4271.h" + +static int cs4271_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = cs4271_regmap_config; + config.reg_bits = 16; + config.val_bits = 8; + config.read_flag_mask = 0x21; + config.write_flag_mask = 0x20; + + return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); +} + +static int cs4271_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cs4271_dt_ids), + }, + .probe = cs4271_spi_probe, + .remove = cs4271_spi_remove, +}; +module_spi_driver(cs4271_spi_driver); + +MODULE_DESCRIPTION("ASoC CS4271 SPI Driver"); +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 93cec52..79a4efc 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include #include #include @@ -32,6 +30,7 @@ #include #include #include +#include "cs4271.h" #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ @@ -527,14 +526,15 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec) #endif /* CONFIG_PM */ #ifdef CONFIG_OF -static const struct of_device_id cs4271_dt_ids[] = { +const struct of_device_id cs4271_dt_ids[] = { { .compatible = "cirrus,cs4271", }, { } }; MODULE_DEVICE_TABLE(of, cs4271_dt_ids); +EXPORT_SYMBOL_GPL(cs4271_dt_ids); #endif -static int cs4271_probe(struct snd_soc_codec *codec) +static int cs4271_codec_probe(struct snd_soc_codec *codec) { struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; @@ -587,7 +587,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) return 0; } -static int cs4271_remove(struct snd_soc_codec *codec) +static int cs4271_codec_remove(struct snd_soc_codec *codec) { struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); @@ -599,8 +599,8 @@ static int cs4271_remove(struct snd_soc_codec *codec) }; static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { - .probe = cs4271_probe, - .remove = cs4271_remove, + .probe = cs4271_codec_probe, + .remove = cs4271_codec_remove, .suspend = cs4271_soc_suspend, .resume = cs4271_soc_resume, @@ -642,14 +642,8 @@ static int cs4271_common_probe(struct device *dev, return 0; } -#if defined(CONFIG_SPI_MASTER) - -static const struct regmap_config cs4271_spi_regmap = { - .reg_bits = 16, - .val_bits = 8, +const struct regmap_config cs4271_regmap_config = { .max_register = CS4271_LASTREG, - .read_flag_mask = 0x21, - .write_flag_mask = 0x20, .reg_defaults = cs4271_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults), @@ -657,140 +651,27 @@ static const struct regmap_config cs4271_spi_regmap = { .volatile_reg = cs4271_volatile_reg, }; +EXPORT_SYMBOL_GPL(cs4271_regmap_config); -static int cs4271_spi_probe(struct spi_device *spi) +int cs4271_probe(struct device *dev, struct regmap *regmap) { struct cs4271_private *cs4271; int ret; - ret = cs4271_common_probe(&spi->dev, &cs4271); - if (ret < 0) - return ret; - - spi_set_drvdata(spi, cs4271); - cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap); - if (IS_ERR(cs4271->regmap)) - return PTR_ERR(cs4271->regmap); - - return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271, - &cs4271_dai, 1); -} - -static int cs4271_spi_remove(struct spi_device *spi) -{ - snd_soc_unregister_codec(&spi->dev); - return 0; -} - -static struct spi_driver cs4271_spi_driver = { - .driver = { - .name = "cs4271", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(cs4271_dt_ids), - }, - .probe = cs4271_spi_probe, - .remove = cs4271_spi_remove, -}; -#endif /* defined(CONFIG_SPI_MASTER) */ - -#if IS_ENABLED(CONFIG_I2C) -static const struct i2c_device_id cs4271_i2c_id[] = { - {"cs4271", 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); -static const struct regmap_config cs4271_i2c_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = CS4271_LASTREG, - - .reg_defaults = cs4271_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults), - .cache_type = REGCACHE_RBTREE, - - .volatile_reg = cs4271_volatile_reg, -}; - -static int cs4271_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct cs4271_private *cs4271; - int ret; - - ret = cs4271_common_probe(&client->dev, &cs4271); + ret = cs4271_common_probe(dev, &cs4271); if (ret < 0) return ret; - i2c_set_clientdata(client, cs4271); - cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap); - if (IS_ERR(cs4271->regmap)) - return PTR_ERR(cs4271->regmap); + dev_set_drvdata(dev, cs4271); + cs4271->regmap = regmap; - return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271, - &cs4271_dai, 1); -} - -static int cs4271_i2c_remove(struct i2c_client *client) -{ - snd_soc_unregister_codec(&client->dev); - return 0; -} - -static struct i2c_driver cs4271_i2c_driver = { - .driver = { - .name = "cs4271", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(cs4271_dt_ids), - }, - .id_table = cs4271_i2c_id, - .probe = cs4271_i2c_probe, - .remove = cs4271_i2c_remove, -}; -#endif /* IS_ENABLED(CONFIG_I2C) */ - -/* - * We only register our serial bus driver here without - * assignment to particular chip. So if any of the below - * fails, there is some problem with I2C or SPI subsystem. - * In most cases this module will be compiled with support - * of only one serial bus. - */ -static int __init cs4271_modinit(void) -{ - int ret; - -#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); - return ret; - } -#endif - -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&cs4271_spi_driver); - if (ret) { - pr_err("Failed to register CS4271 SPI driver: %d\n", ret); - return ret; - } -#endif - - return 0; -} -module_init(cs4271_modinit); - -static void __exit cs4271_modexit(void) -{ -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&cs4271_spi_driver); -#endif - -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&cs4271_i2c_driver); -#endif + return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai, + 1); } -module_exit(cs4271_modexit); +EXPORT_SYMBOL_GPL(cs4271_probe); MODULE_AUTHOR("Alexander Sverdlin "); MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h new file mode 100644 index 0000000..9adad8e --- /dev/null +++ b/sound/soc/codecs/cs4271.h @@ -0,0 +1,11 @@ +#ifndef _CS4271_PRIV_H +#define _CS4271_PRIV_H + +#include + +extern const struct of_device_id cs4271_dt_ids[]; +extern const struct regmap_config cs4271_regmap_config; + +int cs4271_probe(struct device *dev, struct regmap *regmap); + +#endif -- cgit v0.10.2 From 051c77e552bf1c80adf3fb2fa5e145ca7c9b0f08 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 Oct 2014 15:13:24 -0300 Subject: ASoC: fsl: imx-wm8962: Delete unneeded test before of_node_put of_node_put() supports NULL as its argument, so the initial test is not necessary. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 3a3d17c..48179ff 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -281,10 +281,8 @@ static int imx_wm8962_probe(struct platform_device *pdev) clk_fail: clk_disable_unprepare(data->codec_clk); fail: - if (ssi_np) - of_node_put(ssi_np); - if (codec_np) - of_node_put(codec_np); + of_node_put(ssi_np); + of_node_put(codec_np); return ret; } -- cgit v0.10.2 From 7e3bc3c1c9017867b88593ecb4c0c1ae9da16fb2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 Oct 2014 15:13:25 -0300 Subject: ASoC: fsl: imx-sgtl5000: Delete unneeded test before of_node_put of_node_put() supports NULL as its argument, so the initial test is not necessary. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 1cb22dd..1dab963 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -175,10 +175,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) fail: if (data && !IS_ERR(data->codec_clk)) clk_put(data->codec_clk); - if (ssi_np) - of_node_put(ssi_np); - if (codec_np) - of_node_put(codec_np); + of_node_put(ssi_np); + of_node_put(codec_np); return ret; } -- cgit v0.10.2 From aa4ac9201676ddd35aa56ae74bdf8e07454f04fc Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 Oct 2014 15:29:56 -0300 Subject: ASoC: fsl: imx-spdif: Delete unneeded test before of_node_put of_node_put() supports NULL as its argument, so the initial test is not necessary. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index e1dc401..0c9068e 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -74,8 +74,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); end: - if (spdif_np) - of_node_put(spdif_np); + of_node_put(spdif_np); return ret; } -- cgit v0.10.2 From 89dea487646831d47b0c56723a7c43c62b094c48 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 Oct 2014 15:29:57 -0300 Subject: ASoC: fsl: eukrea-tlv320: Delete unneeded test before of_node_put of_node_put() supports NULL as its argument, so the initial test is not necessary. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index eb093d5..dd931fa 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -217,8 +217,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) err: if (ret) dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - if (np) - of_node_put(ssi_np); + of_node_put(ssi_np); return ret; } -- cgit v0.10.2 From 077661b6ed24e530dabc9db3ab3ae48fbaf19679 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Oct 2014 20:56:29 +0200 Subject: ASoC: eukrea-tlv320: Fix of_node_put() call with uninitialized object The of_node_put() call in eukrea_tlv320_probe() may take an uninitialized pointer, as compiler spotted out: sound/soc/fsl/eukrea-tlv320.c:221:14: warning: 'ssi_np' may be used uninitialized in this function [-Wuninitialized] This patch adds the proper NULL initializations as a fix. (codec_np is also NULL initialized just for consistency.) Fixes: 66f232908de2 ('ASoC: eukrea-tlv320: Add DT support') Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index dd931fa..b175b01 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -105,7 +105,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) int ret; int int_port = 0, ext_port; struct device_node *np = pdev->dev.of_node; - struct device_node *ssi_np, *codec_np; + struct device_node *ssi_np = NULL, *codec_np = NULL; eukrea_tlv320.dev = &pdev->dev; if (np) { -- cgit v0.10.2 From 73a2cd9193ae50d4cc7c447f8929e13010b589be Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:04 -0700 Subject: ASoC: fsl_esai: Add indentation for binding doc to increase readability This patch simply adds indentations for DT binding doc to increase readability without changing any contents. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,esai.txt b/Documentation/devicetree/bindings/sound/fsl,esai.txt index 52f5b6b..d3b6b5f 100644 --- a/Documentation/devicetree/bindings/sound/fsl,esai.txt +++ b/Documentation/devicetree/bindings/sound/fsl,esai.txt @@ -7,37 +7,39 @@ other DSPs. It has up to six transmitters and four receivers. Required properties: - - compatible : Compatible list, must contain "fsl,imx35-esai" or - "fsl,vf610-esai" + - compatible : Compatible list, must contain "fsl,imx35-esai" or + "fsl,vf610-esai" - - reg : Offset and length of the register set for the device. + - reg : Offset and length of the register set for the device. - - interrupts : Contains the spdif interrupt. + - interrupts : Contains the spdif interrupt. - - dmas : Generic dma devicetree binding as described in - Documentation/devicetree/bindings/dma/dma.txt. + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. - - dma-names : Two dmas have to be defined, "tx" and "rx". + - dma-names : Two dmas have to be defined, "tx" and "rx". - - clocks: Contains an entry for each entry in clock-names. + - clocks : Contains an entry for each entry in clock-names. - - clock-names : Includes the following entries: - "core" The core clock used to access registers - "extal" The esai baud clock for esai controller used to derive - HCK, SCK and FS. - "fsys" The system clock derived from ahb clock used to derive - HCK, SCK and FS. + - clock-names : Includes the following entries: + "core" The core clock used to access registers + "extal" The esai baud clock for esai controller used to + derive HCK, SCK and FS. + "fsys" The system clock derived from ahb clock used to + derive HCK, SCK and FS. - - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. - This number is the maximum allowed value for TFCR[TFWM] or RFCR[RFWM]. + - fsl,fifo-depth : The number of elements in the transmit and receive + FIFOs. This number is the maximum allowed value for + TFCR[TFWM] or RFCR[RFWM]. - fsl,esai-synchronous: This is a boolean property. If present, indicating - that ESAI would work in the synchronous mode, which means all the settings - for Receiving would be duplicated from Transmition related registers. + that ESAI would work in the synchronous mode, which + means all the settings for Receiving would be + duplicated from Transmition related registers. - - big-endian : If this property is absent, the native endian mode will - be in use as default, or the big endian mode will be in use for all the - device registers. + - big-endian : If this property is absent, the native endian mode + will be in use as default, or the big endian mode + will be in use for all the device registers. Example: -- cgit v0.10.2 From 9c4c1045343836b848c3dd546277c955d145d20a Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:05 -0700 Subject: ASoC: fsl_spdif: Add indentation for binding doc to increase readability This patch simply adds indentations for DT binding doc to increase readability without changing any contents. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt index 3e9e82c8..b5ee32e 100644 --- a/Documentation/devicetree/bindings/sound/fsl,spdif.txt +++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt @@ -6,32 +6,31 @@ a fibre cable. Required properties: - - compatible : Compatible list, must contain "fsl,imx35-spdif". + - compatible : Compatible list, must contain "fsl,imx35-spdif". - - reg : Offset and length of the register set for the device. + - reg : Offset and length of the register set for the device. - - interrupts : Contains the spdif interrupt. + - interrupts : Contains the spdif interrupt. - - dmas : Generic dma devicetree binding as described in - Documentation/devicetree/bindings/dma/dma.txt. + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. - - dma-names : Two dmas have to be defined, "tx" and "rx". + - dma-names : Two dmas have to be defined, "tx" and "rx". - - clocks : Contains an entry for each entry in clock-names. + - clocks : Contains an entry for each entry in clock-names. - - clock-names : Includes the following entries: - "core" The core clock of spdif controller - "rxtx<0-7>" Clock source list for tx and rx clock. - This clock list should be identical to - the source list connecting to the spdif - clock mux in "SPDIF Transceiver Clock - Diagram" of SoC reference manual. It - can also be referred to TxClk_Source - bit of register SPDIF_STC. + - clock-names : Includes the following entries: + "core" The core clock of spdif controller. + "rxtx<0-7>" Clock source list for tx and rx clock. + This clock list should be identical to the source + list connecting to the spdif clock mux in "SPDIF + Transceiver Clock Diagram" of SoC reference manual. + It can also be referred to TxClk_Source bit of + register SPDIF_STC. - - big-endian : If this property is absent, the native endian mode will - be in use as default, or the big endian mode will be in use for all the - device registers. + - big-endian : If this property is absent, the native endian mode + will be in use as default, or the big endian mode + will be in use for all the device registers. Example: -- cgit v0.10.2 From 0b9938b264d1a76458cf06aab6de7b9cec68efca Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:06 -0700 Subject: ASoC: fsl_sai: Add indentation for binding doc to increase readability This patch refines the DT binding doc for more readability by adding extra blank lines and indentations. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt index 4956b14..044e5d7 100644 --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt @@ -5,32 +5,48 @@ which provides a synchronous audio interface that supports fullduplex serial interfaces with frame synchronization such as I2S, AC97, TDM, and codec/DSP interfaces. - Required properties: -- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai". -- reg: Offset and length of the register set for the device. -- clocks: Must contain an entry for each entry in clock-names. -- clock-names : Must include the "bus" for register access and "mclk1" "mclk2" - "mclk3" for bit clock and frame clock providing. -- dmas : Generic dma devicetree binding as described in - Documentation/devicetree/bindings/dma/dma.txt. -- dma-names : Two dmas have to be defined, "tx" and "rx". -- pinctrl-names: Must contain a "default" entry. -- pinctrl-NNN: One property must exist for each entry in pinctrl-names. - See ../pinctrl/pinctrl-bindings.txt for details of the property values. -- big-endian: Boolean property, required if all the FTM_PWM registers - are big-endian rather than little-endian. -- lsb-first: Configures whether the LSB or the MSB is transmitted first for - the fifo data. If this property is absent, the MSB is transmitted first as - default, or the LSB is transmitted first. -- fsl,sai-synchronous-rx: This is a boolean property. If present, indicating - that SAI will work in the synchronous mode (sync Tx with Rx) which means - both the transimitter and receiver will send and receive data by following - receiver's bit clocks and frame sync clocks. -- fsl,sai-asynchronous: This is a boolean property. If present, indicating - that SAI will work in the asynchronous mode, which means both transimitter - and receiver will send and receive data by following their own bit clocks - and frame sync clocks separately. + + - compatible : Compatible list, contains "fsl,vf610-sai" or + "fsl,imx6sx-sai". + + - reg : Offset and length of the register set for the device. + + - clocks : Must contain an entry for each entry in clock-names. + + - clock-names : Must include the "bus" for register access and + "mclk1", "mclk2", "mclk3" for bit clock and frame + clock providing. + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. + + - dma-names : Two dmas have to be defined, "tx" and "rx". + + - pinctrl-names : Must contain a "default" entry. + + - pinctrl-NNN : One property must exist for each entry in + pinctrl-names. See ../pinctrl/pinctrl-bindings.txt + for details of the property values. + + - big-endian : Boolean property, required if all the FTM_PWM + registers are big-endian rather than little-endian. + + - lsb-first : Configures whether the LSB or the MSB is transmitted + first for the fifo data. If this property is absent, + the MSB is transmitted first as default, or the LSB + is transmitted first. + + - fsl,sai-synchronous-rx: This is a boolean property. If present, indicating + that SAI will work in the synchronous mode (sync Tx + with Rx) which means both the transimitter and the + receiver will send and receive data by following + receiver's bit clocks and frame sync clocks. + + - fsl,sai-asynchronous: This is a boolean property. If present, indicating + that SAI will work in the asynchronous mode, which + means both transimitter and receiver will send and + receive data by following their own bit clocks and + frame sync clocks separately. Note: - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the -- cgit v0.10.2 From d29ae41edde680c00f9f74d448b7d59c91ca0474 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:07 -0700 Subject: ASoC: eukrea-tlv320: Add indentation for binding doc to increase readability This patch refines the DT binding doc for more readability by adding extra blank lines and indentations. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt b/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt index 0d7985c..6dfa88c 100644 --- a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt +++ b/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt @@ -1,11 +1,16 @@ Audio complex for Eukrea boards with tlv320aic23 codec. Required properties: -- compatible : "eukrea,asoc-tlv320" -- eukrea,model : The user-visible name of this sound complex. -- ssi-controller : The phandle of the SSI controller. -- fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX). -- fsl,mux-ext-port : The external port of the i.MX audio muxer. + + - compatible : "eukrea,asoc-tlv320" + + - eukrea,model : The user-visible name of this sound complex. + + - ssi-controller : The phandle of the SSI controller. + + - fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX). + + - fsl,mux-ext-port : The external port of the i.MX audio muxer. Note: The AUDMUX port numbering should start at 1, which is consistent with hardware manual. -- cgit v0.10.2 From 5463c709ddeeacc73ae6844fd8aebc0a217b98d4 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:08 -0700 Subject: ASoC: imx-audmux: Add indentation for binding doc to increase readability This patch refines the DT binding doc for more readability by adding extra blank lines and indentations. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.txt b/Documentation/devicetree/bindings/sound/imx-audmux.txt index f88a00e..b30a737 100644 --- a/Documentation/devicetree/bindings/sound/imx-audmux.txt +++ b/Documentation/devicetree/bindings/sound/imx-audmux.txt @@ -1,18 +1,24 @@ Freescale Digital Audio Mux (AUDMUX) device Required properties: -- compatible : "fsl,imx21-audmux" for AUDMUX version firstly used on i.MX21, - or "fsl,imx31-audmux" for the version firstly used on i.MX31. -- reg : Should contain AUDMUX registers location and length + + - compatible : "fsl,imx21-audmux" for AUDMUX version firstly used + on i.MX21, or "fsl,imx31-audmux" for the version + firstly used on i.MX31. + + - reg : Should contain AUDMUX registers location and length. An initial configuration can be setup using child nodes. Required properties of optional child nodes: -- fsl,audmux-port : Integer of the audmux port that is configured by this - child node. -- fsl,port-config : List of configuration options for the specific port. For - imx31-audmux and above, it is a list of tuples . For - imx21-audmux it is a list of pcr values. + + - fsl,audmux-port : Integer of the audmux port that is configured by this + child node. + + - fsl,port-config : List of configuration options for the specific port. + For imx31-audmux and above, it is a list of tuples + . For imx21-audmux it is a list of pcr + values. Example: -- cgit v0.10.2 From afa1fde676592b9c40e893f4f652f2c842e31683 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:09 -0700 Subject: ASoC: imx-sgtl5000: Add indentation for binding doc to increase readability This patch refines the DT binding doc for more readability by adding extra blank lines and indentations. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt index e4acdd8..2f89db8 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt @@ -1,33 +1,40 @@ Freescale i.MX audio complex with SGTL5000 codec Required properties: -- compatible : "fsl,imx-audio-sgtl5000" -- model : The user-visible name of this sound complex -- ssi-controller : The phandle of the i.MX SSI controller -- audio-codec : The phandle of the SGTL5000 audio codec -- audio-routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the connection's sink, - the second being the connection's source. Valid names could be power - supplies, SGTL5000 pins, and the jacks on the board: - - Power supplies: - * Mic Bias - - SGTL5000 pins: - * MIC_IN - * LINE_IN - * HP_OUT - * LINE_OUT - - Board connectors: - * Mic Jack - * Line In Jack - * Headphone Jack - * Line Out Jack - * Ext Spk - -- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) -- mux-ext-port : The external port of the i.MX audio muxer + + - compatible : "fsl,imx-audio-sgtl5000" + + - model : The user-visible name of this sound complex + + - ssi-controller : The phandle of the i.MX SSI controller + + - audio-codec : The phandle of the SGTL5000 audio codec + + - audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the + connection's sink, the second being the connection's + source. Valid names could be power supplies, SGTL5000 + pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + SGTL5000 pins: + * MIC_IN + * LINE_IN + * HP_OUT + * LINE_OUT + + Board connectors: + * Mic Jack + * Line In Jack + * Headphone Jack + * Line Out Jack + * Ext Spk + + - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) + + - mux-ext-port : The external port of the i.MX audio muxer Note: The AUDMUX port numbering should start at 1, which is consistent with hardware manual. -- cgit v0.10.2 From 6219b082b3099668c32be99bd31216379fc3d97a Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:10 -0700 Subject: ASoC: imx-spdif: Add indentation for binding doc to increase readability This patch simply adds indentations for DT binding doc to increase readability without changing any contents. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt index 7d13479..da84a44 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt +++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt @@ -2,23 +2,25 @@ Freescale i.MX audio complex with S/PDIF transceiver Required properties: - - compatible : "fsl,imx-audio-spdif" + - compatible : "fsl,imx-audio-spdif" - - model : The user-visible name of this sound complex + - model : The user-visible name of this sound complex - - spdif-controller : The phandle of the i.MX S/PDIF controller + - spdif-controller : The phandle of the i.MX S/PDIF controller Optional properties: - - spdif-out : This is a boolean property. If present, the transmitting - function of S/PDIF will be enabled, indicating there's a physical - S/PDIF out connector/jack on the board or it's connecting to some - other IP block, such as an HDMI encoder/display-controller. + - spdif-out : This is a boolean property. If present, the + transmitting function of S/PDIF will be enabled, + indicating there's a physical S/PDIF out connector + or jack on the board or it's connecting to some + other IP block, such as an HDMI encoder or + display-controller. - - spdif-in : This is a boolean property. If present, the receiving - function of S/PDIF will be enabled, indicating there's a physical - S/PDIF in connector/jack on the board. + - spdif-in : This is a boolean property. If present, the receiving + function of S/PDIF will be enabled, indicating there + is a physical S/PDIF in connector/jack on the board. * Note: At least one of these two properties should be set in the DT binding. -- cgit v0.10.2 From 6a6dec83e5abbc5003dac234970b51afa142defd Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 7 Oct 2014 12:29:11 -0700 Subject: ASoC: imx-wm8962: Add indentation for binding doc to increase readability This patch simply adds indentations for DT binding doc to increase readability without changing any contents. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt b/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt index f49450a..acea71b 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt +++ b/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt @@ -1,25 +1,32 @@ Freescale i.MX audio complex with WM8962 codec Required properties: -- compatible : "fsl,imx-audio-wm8962" -- model : The user-visible name of this sound complex -- ssi-controller : The phandle of the i.MX SSI controller -- audio-codec : The phandle of the WM8962 audio codec -- audio-routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the connection's sink, - the second being the connection's source. Valid names could be power - supplies, WM8962 pins, and the jacks on the board: - - Power supplies: - * Mic Bias - - Board connectors: - * Mic Jack - * Headphone Jack - * Ext Spk - -- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) -- mux-ext-port : The external port of the i.MX audio muxer + + - compatible : "fsl,imx-audio-wm8962" + + - model : The user-visible name of this sound complex + + - ssi-controller : The phandle of the i.MX SSI controller + + - audio-codec : The phandle of the WM8962 audio codec + + - audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the + connection's sink, the second being the connection's + source. Valid names could be power supplies, WM8962 + pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + Board connectors: + * Mic Jack + * Headphone Jack + * Ext Spk + + - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) + + - mux-ext-port : The external port of the i.MX audio muxer Note: The AUDMUX port numbering should start at 1, which is consistent with hardware manual. -- cgit v0.10.2 From 74d813cf37c210e94d155b0c19598fe269b8f78c Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 14 Oct 2014 20:29:26 +0300 Subject: ASoC: hdmi: Mark the maximum significant bits to HDMI codec HDMI audio can not have more than 24 bits even if on i2s bus there would be 32 bit samples. Mark this by adding .sig_bits = 24 to playback stream definition. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c index 1087fd5..2a52b90 100644 --- a/sound/soc/codecs/hdmi.c +++ b/sound/soc/codecs/hdmi.c @@ -47,6 +47,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = { SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, }, .capture = { .stream_name = "Capture", -- cgit v0.10.2 From 69434097916bc52a4d6d495a0d719eb02e0cff9e Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 14 Oct 2014 20:29:27 +0300 Subject: ASoC: hdmi: HDMI codec doesn't benefit from pmdown delay Adds .ignore_pmdown_time = true to codec driver struct. HDMI codec is currently a dummy codec and doesn't benefit from pmdown delay. Even if in the future the codec would controll HDMI encoder, it would still be a digital to digital interface that should have no need for pmdown delay. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c index 2a52b90..1391ad5 100644 --- a/sound/soc/codecs/hdmi.c +++ b/sound/soc/codecs/hdmi.c @@ -76,6 +76,7 @@ static struct snd_soc_codec_driver hdmi_codec = { .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), .dapm_routes = hdmi_routes, .num_dapm_routes = ARRAY_SIZE(hdmi_routes), + .ignore_pmdown_time = true, }; static int hdmi_codec_probe(struct platform_device *pdev) -- cgit v0.10.2 From 4fa805738e497c6f5bad53fcdc76b9759bc9dc80 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 15 Oct 2014 20:12:56 +0530 Subject: ASoC: Intel: mrfld: add the gain controls The DSP has various gain modules in the path, add these as ALSA gain controls Signed-off-by: Vinod Koul Signed-off-by: Subhransu S. Prusty Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index f5b4a9c7..726f7d8 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,6 +1,6 @@ config SND_MFLD_MACHINE tristate "SOC Machine Audio driver for Intel Medfield MID platform" - depends on INTEL_SCU_IPC + depends on INTEL_SCU_IPC || COMPILE_TEST select SND_SOC_SN95031 select SND_SST_MFLD_PLATFORM help diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 7104a34..a00d506 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -162,6 +162,190 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, return ret; } +static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = mc->stereo ? 2 : 1; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + + return 0; +} + +/** + * sst_send_gain_cmd - send the gain algorithm IPC to the FW + * @gv: the stored value of gain (also contains rampduration) + * @mute: flag that indicates whether this was called from the + * digital_mute callback or directly. If called from the + * digital_mute callback, module will be muted/unmuted based on this + * flag. The flag is always 0 if called directly. + * + * Called with sst_data.lock held + * + * The user-set gain value is sent only if the user-controllable 'mute' control + * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is + * sent. + */ +static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv, + u16 task_id, u16 loc_id, u16 module_id, int mute) +{ + struct sst_cmd_set_gain_dual cmd; + + dev_dbg(&drv->pdev->dev, "Enter\n"); + + cmd.header.command_id = MMX_SET_GAIN; + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.gain_cell_num = 1; + + if (mute || gv->mute) { + cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE; + cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE; + } else { + cmd.cell_gains[0].cell_gain_left = gv->l_gain; + cmd.cell_gains[0].cell_gain_right = gv->r_gain; + } + + SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest, + loc_id, module_id); + cmd.cell_gains[0].gain_time_constant = gv->ramp_duration; + + cmd.header.length = sizeof(struct sst_cmd_set_gain_dual) + - sizeof(struct sst_dsp_header); + + /* we are with lock held, so call the unlocked api to send */ + return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, + SST_FLAG_BLOCKED, task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static int sst_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + + switch (mc->type) { + case SST_GAIN_TLV: + ucontrol->value.integer.value[0] = gv->l_gain; + ucontrol->value.integer.value[1] = gv->r_gain; + break; + + case SST_GAIN_MUTE: + ucontrol->value.integer.value[0] = gv->mute ? 1 : 0; + break; + + case SST_GAIN_RAMP_DURATION: + ucontrol->value.integer.value[0] = gv->ramp_duration; + break; + + default: + dev_err(component->dev, "Invalid Input- gain type:%d\n", + mc->type); + return -EINVAL; + }; + + return 0; +} + +static int sst_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt); + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + + mutex_lock(&drv->lock); + + switch (mc->type) { + case SST_GAIN_TLV: + gv->l_gain = ucontrol->value.integer.value[0]; + gv->r_gain = ucontrol->value.integer.value[1]; + dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n", + mc->pname, gv->l_gain, gv->r_gain); + break; + + case SST_GAIN_MUTE: + gv->mute = !!ucontrol->value.integer.value[0]; + dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute); + break; + + case SST_GAIN_RAMP_DURATION: + gv->ramp_duration = ucontrol->value.integer.value[0]; + dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n", + mc->pname, gv->ramp_duration); + break; + + default: + mutex_unlock(&drv->lock); + dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", + mc->type); + return -EINVAL; + }; + + if (mc->w && mc->w->power) + ret = sst_send_gain_cmd(drv, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, 0); + mutex_unlock(&drv->lock); + + return ret; +} + +static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); + +/* Gain helper with min/max set */ +#define SST_GAIN(name, path_id, task_id, instance, gain_var) \ + SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ + SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ + sst_gain_get, sst_gain_put, \ + SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \ + sst_gain_tlv_common, gain_var) + +#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \ + SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ + SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ + sst_gain_get, sst_gain_put, \ + SST_MODULE_ID_VOLUME, path_id, instance, task_id, \ + sst_gain_tlv_common, gain_var) + +static struct sst_gain_value sst_gains[]; + +static const struct snd_kcontrol_new sst_gain_controls[] = { + SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]), + SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]), + SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]), + SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]), + + SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]), + SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]), + SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]), + SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]), + + SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]), + SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]), + SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]), + SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]), + SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]), + SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]), + SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]), + SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]), +}; + +#define SST_GAIN_NUM_CONTROLS 3 +/* the SST_GAIN macro above will create three alsa controls for each + * instance invoked, gain, mute and ramp duration, which use the same gain + * cell sst_gain to keep track of data + * To calculate number of gain cell instances we need to device by 3 in + * below caulcation for gain cell memory. + * This gets rid of static number and issues while adding new controls + */ +static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS]; + static const struct snd_kcontrol_new sst_algo_controls[] = { SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24, SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), @@ -200,19 +384,33 @@ static int sst_algo_control_init(struct device *dev) int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) { - int ret = 0; + int i, ret = 0; struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3; drv->byte_stream = devm_kzalloc(platform->dev, SST_MAX_BIN_BYTES, GFP_KERNEL); if (!drv->byte_stream) return -ENOMEM; - /*Initialize algo control params*/ + for (i = 0; i < gains; i++) { + sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT; + sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT; + sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT; + sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT; + } + + ret = snd_soc_add_platform_controls(platform, sst_gain_controls, + ARRAY_SIZE(sst_gain_controls)); + if (ret) + return ret; + + /* Initialize algo control params */ ret = sst_algo_control_init(platform->dev); if (ret) return ret; ret = snd_soc_add_platform_controls(platform, sst_algo_controls, ARRAY_SIZE(sst_algo_controls)); + return ret; } diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index a73e894..e530002 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -23,6 +23,9 @@ #ifndef __SST_ATOM_CONTROLS_H__ #define __SST_ATOM_CONTROLS_H__ +#include +#include + enum { MERR_DPCM_AUDIO = 0, MERR_DPCM_COMPR, @@ -360,16 +363,134 @@ struct sst_dsp_header { struct sst_cmd_generic { struct sst_dsp_header header; } __packed; + +struct gain_cell { + struct sst_destination_id dest; + s16 cell_gain_left; + s16 cell_gain_right; + u16 gain_time_constant; +} __packed; + +#define NUM_GAIN_CELLS 1 +struct sst_cmd_set_gain_dual { + struct sst_dsp_header header; + u16 gain_cell_num; + struct gain_cell cell_gains[NUM_GAIN_CELLS]; +} __packed; struct sst_cmd_set_params { struct sst_destination_id dst; u16 command_id; char params[0]; } __packed; + +/**** widget defines *****/ + +struct sst_ids { + u16 location_id; + u16 module_id; + u8 task_id; + u8 format; + u8 reg; + const char *parent_wname; + struct snd_soc_dapm_widget *parent_w; + struct list_head algo_list; + struct list_head gain_list; + const struct sst_pcm_format *pcm_fmt; +}; +enum sst_gain_kcontrol_type { + SST_GAIN_TLV, + SST_GAIN_MUTE, + SST_GAIN_RAMP_DURATION, +}; + +struct sst_gain_mixer_control { + bool stereo; + enum sst_gain_kcontrol_type type; + struct sst_gain_value *gain_val; + int max; + int min; + u16 instance_id; + u16 module_id; + u16 pipe_id; + u16 task_id; + char pname[44]; + struct snd_soc_dapm_widget *w; +}; + +struct sst_gain_value { + u16 ramp_duration; + s16 l_gain; + s16 r_gain; + bool mute; +}; +#define SST_GAIN_VOLUME_DEFAULT (-1440) +#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */ +#define SST_GAIN_MUTE_DEFAULT true + +#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = sst_gain_ctl_info,\ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, xtype, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sst_gain_ctl_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\ + xmod, xpipe, xinstance, xtask, xgain_val, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_bool_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .type = SST_GAIN_MUTE, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} #define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ xpname " " xmname " " #xinstance " " xtype #define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ xpname " " xmname " " #xinstance " " xtype " " xsubmodule + +/* + * 3 Controls for each Gain module + * e.g. - pcm0_in Gain 0 Volume + * - pcm0_in Gain 0 Ramp Delay + * - pcm0_in Gain 0 Switch + */ +#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \ + xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \ + { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \ + xgain_val, xmin_tc, xmax_tc, xpname) }, \ + { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \ + xgain_val, xpname) } ,\ + { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \ + xgain_val, xmin_gain, xmax_gain, xpname) } + +#define SST_GAIN_TC_MIN 5 +#define SST_GAIN_TC_MAX 5000 +#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */ +#define SST_GAIN_MAX_VALUE 360 + enum sst_algo_kcontrol_type { SST_ALGO_PARAMS, SST_ALGO_BYPASS, -- cgit v0.10.2 From 24c8d14192cc63661ca049b423d7baaa0bbafeb3 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 15 Oct 2014 20:12:57 +0530 Subject: ASoC: Intel: mrfld: add DSP core controls This patch adds core controls like interleavers, SSP BEs, and also logic of sending pipeline and module commands to the DSP. Signed-off-by: Vinod Koul Signed-off-by: Subhransu S. Prusty Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index a00d506..9239eff2 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -15,6 +15,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * + * In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active + * we forward the settings and parameters, rest we keep the values in + * driver and forward when DAPM enables them * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -81,6 +84,183 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, return ret; } +/** + * tx map value is a bitfield where each bit represents a FW channel + * + * 3 2 1 0 # 0 = codec0, 1 = codec1 + * RLRLRLRL # 3, 4 = reserved + * + * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R + */ +static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = { + 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */ +}; + +/** + * rx map value is a bitfield where each bit represents a slot + * + * 76543210 # 0 = slot 0, 1 = slot 1 + * + * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2 + */ +static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = { + 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */ +}; + +/** + * NOTE: this is invoked with lock held + */ +static int sst_send_slot_map(struct sst_data *drv) +{ + struct sst_param_sba_ssp_slot_map cmd; + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.command_id = SBA_SET_SSP_SLOT_MAP; + cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map) + - sizeof(struct sst_dsp_header); + + cmd.param_id = SBA_SET_SSP_SLOT_MAP; + cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map) + + sizeof(cmd.ssp_index); + cmd.ssp_index = SSP_CODEC; + + memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map)); + memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map)); + + return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, + SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +int sst_slot_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_enum *e = (struct sst_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->max; + + if (uinfo->value.enumerated.item > e->max - 1) + uinfo->value.enumerated.item = e->max - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + + return 0; +} + +/** + * sst_slot_get - get the status of the interleaver/deinterleaver control + * + * Searches the map where the control status is stored, and gets the + * channel/slot which is currently set for this enumerated control. Since it is + * an enumerated control, there is only one possible value. + */ +static int sst_slot_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_enum *e = (void *)kcontrol->private_value; + struct snd_soc_component *c = snd_kcontrol_chip(kcontrol); + struct sst_data *drv = snd_soc_component_get_drvdata(c); + unsigned int ctl_no = e->reg; + unsigned int is_tx = e->tx; + unsigned int val, mux; + u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; + + mutex_lock(&drv->lock); + val = 1 << ctl_no; + /* search which slot/channel has this bit set - there should be only one */ + for (mux = e->max; mux > 0; mux--) + if (map[mux - 1] & val) + break; + + ucontrol->value.enumerated.item[0] = mux; + mutex_unlock(&drv->lock); + + dev_dbg(c->dev, "%s - %s map = %#x\n", + is_tx ? "tx channel" : "rx slot", + e->texts[mux], mux ? map[mux - 1] : -1); + return 0; +} + +/* sst_check_and_send_slot_map - helper for checking power state and sending + * slot map cmd + * + * called with lock held + */ +static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol) +{ + struct sst_enum *e = (void *)kcontrol->private_value; + int ret = 0; + + if (e->w && e->w->power) + ret = sst_send_slot_map(drv); + else + dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n", + kcontrol->id.name); + return ret; +} + +/** + * sst_slot_put - set the status of interleaver/deinterleaver control + * + * (de)interleaver controls are defined in opposite sense to be user-friendly + * + * Instead of the enum value being the value written to the register, it is the + * register address; and the kcontrol number (register num) is the value written + * to the register. This is so that there can be only one value for each + * slot/channel since there is only one control for each slot/channel. + * + * This means that whenever an enum is set, we need to clear the bit + * for that kcontrol_no for all the interleaver OR deinterleaver registers + */ +static int sst_slot_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct sst_data *drv = snd_soc_component_get_drvdata(c); + struct sst_enum *e = (void *)kcontrol->private_value; + int i, ret = 0; + unsigned int ctl_no = e->reg; + unsigned int is_tx = e->tx; + unsigned int slot_channel_no; + unsigned int val, mux; + u8 *map; + + map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; + + val = 1 << ctl_no; + mux = ucontrol->value.enumerated.item[0]; + if (mux > e->max - 1) + return -EINVAL; + + mutex_lock(&drv->lock); + /* first clear all registers of this bit */ + for (i = 0; i < e->max; i++) + map[i] &= ~val; + + if (mux == 0) { + /* kctl set to 'none' and we reset the bits so send IPC */ + ret = sst_check_and_send_slot_map(drv, kcontrol); + + mutex_unlock(&drv->lock); + return ret; + } + + /* offset by one to take "None" into account */ + slot_channel_no = mux - 1; + map[slot_channel_no] |= val; + + dev_dbg(c->dev, "%s %s map = %#x\n", + is_tx ? "tx channel" : "rx slot", + e->texts[mux], map[slot_channel_no]); + + ret = sst_check_and_send_slot_map(drv, kcontrol); + + mutex_unlock(&drv->lock); + return ret; +} + static int sst_send_algo_cmd(struct sst_data *drv, struct sst_algo_control *bc) { @@ -104,6 +284,34 @@ static int sst_send_algo_cmd(struct sst_data *drv, return ret; } +/** + * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe + * + * The algos which are in each pipeline are sent to the firmware one by one + * + * Called with lock held + */ +static int sst_find_and_send_pipe_algo(struct sst_data *drv, + const char *pipe, struct sst_ids *ids) +{ + int ret = 0; + struct sst_algo_control *bc; + struct sst_module *algo = NULL; + + dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe); + + list_for_each_entry(algo, &ids->algo_list, node) { + bc = (void *)algo->kctl->private_value; + + dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n", + algo->kctl->id.name, pipe); + ret = sst_send_algo_cmd(drv, bc); + if (ret) + return ret; + } + return ret; +} + static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -296,8 +504,317 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, return ret; } +static int sst_set_pipe_gain(struct sst_ids *ids, + struct sst_data *drv, int mute); + +static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct sst_data *drv = snd_soc_component_get_drvdata(c); + struct sst_ids *ids = w->priv; + + mutex_lock(&drv->lock); + sst_find_and_send_pipe_algo(drv, w->name, ids); + sst_set_pipe_gain(ids, drv, 0); + mutex_unlock(&drv->lock); + + return 0; +} + +static int sst_generic_modules_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + return sst_send_pipe_module_params(w, k); + return 0; +} + static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); +/* Look up table to convert MIXER SW bit regs to SWM inputs */ +static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { + [SST_IP_CODEC0] = SST_SWM_IN_CODEC0, + [SST_IP_CODEC1] = SST_SWM_IN_CODEC1, + [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP, + [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1, + [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2, + [SST_IP_PCM0] = SST_SWM_IN_PCM0, + [SST_IP_PCM1] = SST_SWM_IN_PCM1, + [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0, + [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1, + [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2, + [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3, +}; + +/** + * called with lock held + */ +static int sst_set_pipe_gain(struct sst_ids *ids, + struct sst_data *drv, int mute) +{ + int ret = 0; + struct sst_gain_mixer_control *mc; + struct sst_gain_value *gv; + struct sst_module *gain = NULL; + + list_for_each_entry(gain, &ids->gain_list, node) { + struct snd_kcontrol *kctl = gain->kctl; + + dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name); + mc = (void *)kctl->private_value; + gv = mc->gain_val; + + ret = sst_send_gain_cmd(drv, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, mute); + if (ret) + return ret; + } + return ret; +} + +/* + * sst_handle_vb_timer - Start/Stop the DSP scheduler + * + * The DSP expects first cmd to be SBA_VB_START, so at first startup send + * that. + * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that. + * + * Do refcount internally so that we send command only at first start + * and last end. Since SST driver does its own ref count, invoke sst's + * power ops always! + */ +int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) +{ + int ret = 0; + struct sst_cmd_generic cmd; + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + static int timer_usage; + + if (enable) + cmd.header.command_id = SBA_VB_START; + else + cmd.header.command_id = SBA_IDLE; + dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.length = 0; + + if (enable) { + ret = sst->ops->power(sst->dev, true); + if (ret < 0) + return ret; + } + + mutex_lock(&drv->lock); + if (enable) + timer_usage++; + else + timer_usage--; + + /* + * Send the command only if this call is the first enable or last + * disable + */ + if ((enable && (timer_usage == 1)) || + (!enable && (timer_usage == 0))) { + ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, + SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + if (ret && enable) { + timer_usage--; + enable = false; + } + } + mutex_unlock(&drv->lock); + + if (!enable) + sst->ops->power(sst->dev, false); + return ret; +} + +/** + * sst_ssp_config - contains SSP configuration for media UC + */ +static const struct sst_ssp_config sst_ssp_configs = { + .ssp_id = SSP_CODEC, + .bits_per_slot = 24, + .slots = 4, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NETWORK, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_PCM, + .fs_width = 1, + .fs_frequency = SSP_FS_48_KHZ, + .active_slot_map = 0xF, + .start_delay = 0, +}; + +int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) +{ + struct sst_cmd_sba_hw_set_ssp cmd; + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + const struct sst_ssp_config *config; + + dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.command_id = SBA_HW_SET_SSP; + cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) + - sizeof(struct sst_dsp_header); + + config = &sst_ssp_configs; + dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); + + if (enable) + cmd.switch_state = SST_SWITCH_ON; + else + cmd.switch_state = SST_SWITCH_OFF; + + cmd.selection = config->ssp_id; + cmd.nb_bits_per_slots = config->bits_per_slot; + cmd.nb_slots = config->slots; + cmd.mode = config->ssp_mode | (config->pcm_mode << 1); + cmd.duplex = config->duplex; + cmd.active_tx_slot_map = config->active_slot_map; + cmd.active_rx_slot_map = config->active_slot_map; + cmd.frame_sync_frequency = config->fs_frequency; + cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; + cmd.data_polarity = 1; + cmd.frame_sync_width = config->fs_width; + cmd.ssp_protocol = config->ssp_protocol; + cmd.start_delay = config->start_delay; + cmd.reserved1 = cmd.reserved2 = 0xFF; + + return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static int sst_set_be_modules(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + int ret = 0; + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct sst_data *drv = snd_soc_component_get_drvdata(c); + + dev_dbg(c->dev, "Enter: widget=%s\n", w->name); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = sst_send_slot_map(drv); + if (ret) + return ret; + ret = sst_send_pipe_module_params(w, k); + } + return ret; +} + +static int sst_set_media_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + int ret = 0; + struct sst_cmd_set_media_path cmd; + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct sst_data *drv = snd_soc_component_get_drvdata(c); + struct sst_ids *ids = w->priv; + + dev_dbg(c->dev, "widget=%s\n", w->name); + dev_dbg(c->dev, "task=%u, location=%#x\n", + ids->task_id, ids->location_id); + + if (SND_SOC_DAPM_EVENT_ON(event)) + cmd.switch_state = SST_PATH_ON; + else + cmd.switch_state = SST_PATH_OFF; + + SST_FILL_DESTINATION(2, cmd.header.dst, + ids->location_id, SST_DEFAULT_MODULE_ID); + + /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */ + cmd.header.command_id = MMX_SET_MEDIA_PATH; + cmd.header.length = sizeof(struct sst_cmd_set_media_path) + - sizeof(struct sst_dsp_header); + + ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + ids->task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + if (ret) + return ret; + + if (SND_SOC_DAPM_EVENT_ON(event)) + ret = sst_send_pipe_module_params(w, k); + return ret; +} + +static int sst_set_media_loop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + int ret = 0; + struct sst_cmd_sba_set_media_loop_map cmd; + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct sst_data *drv = snd_soc_component_get_drvdata(c); + struct sst_ids *ids = w->priv; + + dev_dbg(c->dev, "Enter:widget=%s\n", w->name); + if (SND_SOC_DAPM_EVENT_ON(event)) + cmd.switch_state = SST_SWITCH_ON; + else + cmd.switch_state = SST_SWITCH_OFF; + + SST_FILL_DESTINATION(2, cmd.header.dst, + ids->location_id, SST_DEFAULT_MODULE_ID); + + cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP; + cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map) + - sizeof(struct sst_dsp_header); + cmd.param.part.cfg.rate = 2; /* 48khz */ + + cmd.param.part.cfg.format = ids->format; /* stereo/Mono */ + cmd.param.part.cfg.s_length = 1; /* 24bit left justified */ + cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */ + + ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + if (ret) + return ret; + + if (SND_SOC_DAPM_EVENT_ON(event)) + ret = sst_send_pipe_module_params(w, k); + return ret; +} + +static const char * const slot_names[] = { + "none", + "slot 0", "slot 1", "slot 2", "slot 3", + "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */ +}; + +static const char * const channel_names[] = { + "none", + "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1", + "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */ +}; + +#define SST_INTERLEAVER(xpname, slot_name, slotno) \ + SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \ + channel_names, sst_slot_get, sst_slot_put) + +#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \ + SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \ + slot_names, sst_slot_get, sst_slot_put) + +static const struct snd_kcontrol_new sst_slot_controls[] = { + SST_INTERLEAVER("codec_out", "slot 0", 0), + SST_INTERLEAVER("codec_out", "slot 1", 1), + SST_INTERLEAVER("codec_out", "slot 2", 2), + SST_INTERLEAVER("codec_out", "slot 3", 3), + SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0), + SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1), + SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2), + SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3), +}; + /* Gain helper with min/max set */ #define SST_GAIN(name, path_id, task_id, instance, gain_var) \ SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ @@ -382,6 +899,234 @@ static int sst_algo_control_init(struct device *dev) return 0; } +static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) +{ + switch (w->id) { + case snd_soc_dapm_pga: + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_input: + case snd_soc_dapm_output: + case snd_soc_dapm_mixer: + return true; + default: + return false; + } +} + +/** + * sst_send_pipe_gains - send gains for the front-end DAIs + * + * The gains in the pipes connected to the front-ends are muted/unmuted + * automatically via the digital_mute() DAPM callback. This function sends the + * gains for the front-end pipes. + */ +int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) +{ + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_dbg(dai->dev, "Stream name=%s\n", + dai->playback_widget->name); + w = dai->playback_widget; + list_for_each_entry(p, &w->sinks, list_source) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->sink->power && + is_sst_dapm_widget(p->sink)) { + struct sst_ids *ids = p->sink->priv; + + dev_dbg(dai->dev, "send gains for widget=%s\n", + p->sink->name); + mutex_lock(&drv->lock); + sst_set_pipe_gain(ids, drv, mute); + mutex_unlock(&drv->lock); + } + } + } else { + dev_dbg(dai->dev, "Stream name=%s\n", + dai->capture_widget->name); + w = dai->capture_widget; + list_for_each_entry(p, &w->sources, list_sink) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->source->power && + is_sst_dapm_widget(p->source)) { + struct sst_ids *ids = p->source->priv; + + dev_dbg(dai->dev, "send gain for widget=%s\n", + p->source->name); + mutex_lock(&drv->lock); + sst_set_pipe_gain(ids, drv, mute); + mutex_unlock(&drv->lock); + } + } + } + return 0; +} + +/** + * sst_fill_module_list - populate the list of modules/gains for a pipe + * + * + * Fills the widget pointer in the kcontrol private data, and also fills the + * kcontrol pointer in the widget private data. + * + * Widget pointer is used to send the algo/gain in the .put() handler if the + * widget is powerd on. + * + * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF + * event handler. Each widget (pipe) has multiple algos stored in the algo_list. + */ +static int sst_fill_module_list(struct snd_kcontrol *kctl, + struct snd_soc_dapm_widget *w, int type) +{ + struct sst_module *module = NULL; + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct sst_ids *ids = w->priv; + int ret = 0; + + module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL); + if (!module) + return -ENOMEM; + + if (type == SST_MODULE_GAIN) { + struct sst_gain_mixer_control *mc = (void *)kctl->private_value; + + mc->w = w; + module->kctl = kctl; + list_add_tail(&module->node, &ids->gain_list); + } else if (type == SST_MODULE_ALGO) { + struct sst_algo_control *bc = (void *)kctl->private_value; + + bc->w = w; + module->kctl = kctl; + list_add_tail(&module->node, &ids->algo_list); + } else { + dev_err(c->dev, "invoked for unknown type %d module %s", + type, kctl->id.name); + ret = -EINVAL; + } + + return ret; +} + +/** + * sst_fill_widget_module_info - fill list of gains/algos for the pipe + * @widget: pipe modelled as a DAPM widget + * + * Fill the list of gains/algos for the widget by looking at all the card + * controls and comparing the name of the widget with the first part of control + * name. First part of control name contains the pipe name (widget name). + */ +static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, + struct snd_soc_platform *platform) +{ + struct snd_kcontrol *kctl; + int index, ret = 0; + struct snd_card *card = platform->component.card->snd_card; + char *idx; + + down_read(&card->controls_rwsem); + + list_for_each_entry(kctl, &card->controls, list) { + idx = strstr(kctl->id.name, " "); + if (idx == NULL) + continue; + index = strlen(kctl->id.name) - strlen(idx); + + if (strstr(kctl->id.name, "Volume") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); + + else if (strstr(kctl->id.name, "params") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); + + else if (strstr(kctl->id.name, "Switch") && + !strncmp(kctl->id.name, w->name, index) && + strstr(kctl->id.name, "Gain")) { + struct sst_gain_mixer_control *mc = + (void *)kctl->private_value; + + mc->w = w; + + } else if (strstr(kctl->id.name, "interleaver") && + !strncmp(kctl->id.name, w->name, index)) { + struct sst_enum *e = (void *)kctl->private_value; + + e->w = w; + + } else if (strstr(kctl->id.name, "deinterleaver") && + !strncmp(kctl->id.name, w->name, index)) { + + struct sst_enum *e = (void *)kctl->private_value; + + e->w = w; + } + + if (ret < 0) { + up_read(&card->controls_rwsem); + return ret; + } + } + + up_read(&card->controls_rwsem); + return 0; +} + +/** + * sst_fill_linked_widgets - fill the parent pointer for the linked widget + */ +static void sst_fill_linked_widgets(struct snd_soc_platform *platform, + struct sst_ids *ids) +{ + struct snd_soc_dapm_widget *w; + unsigned int len = strlen(ids->parent_wname); + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (!strncmp(ids->parent_wname, w->name, len)) { + ids->parent_w = w; + break; + } + } +} + +/** + * sst_map_modules_to_pipe - fill algo/gains list for all pipes + */ +static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) +{ + struct snd_soc_dapm_widget *w; + int ret = 0; + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (platform && is_sst_dapm_widget(w) && (w->priv)) { + struct sst_ids *ids = w->priv; + + dev_dbg(platform->dev, "widget type=%d name=%s\n", + w->id, w->name); + INIT_LIST_HEAD(&ids->algo_list); + INIT_LIST_HEAD(&ids->gain_list); + ret = sst_fill_widget_module_info(w, platform); + + if (ret < 0) + return ret; + + /* fill linked widgets */ + if (ids->parent_wname != NULL) + sst_fill_linked_widgets(platform, ids); + } + } + return 0; +} + int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) { int i, ret = 0; @@ -411,6 +1156,15 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) return ret; ret = snd_soc_add_platform_controls(platform, sst_algo_controls, ARRAY_SIZE(sst_algo_controls)); + if (ret) + return ret; + + ret = snd_soc_add_platform_controls(platform, sst_slot_controls, + ARRAY_SIZE(sst_slot_controls)); + if (ret) + return ret; + + ret = sst_map_modules_to_pipe(platform); return ret; } diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index e530002..dfebfdd 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -364,6 +364,38 @@ struct sst_cmd_generic { struct sst_dsp_header header; } __packed; +struct swm_input_ids { + struct sst_destination_id input_id; +} __packed; + +struct sst_cmd_set_swm { + struct sst_dsp_header header; + struct sst_destination_id output_id; + u16 switch_state; + u16 nb_inputs; + struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS]; +} __packed; + +struct sst_cmd_set_media_path { + struct sst_dsp_header header; + u16 switch_state; +} __packed; + +struct pcm_cfg { + u8 s_length:2; + u8 rate:3; + u8 format:3; +} __packed; + +struct sst_cmd_set_speech_path { + struct sst_dsp_header header; + u16 switch_state; + struct { + u16 rsvd:8; + struct pcm_cfg cfg; + } config; +} __packed; + struct gain_cell { struct sst_destination_id dest; s16 cell_gain_left; @@ -383,8 +415,162 @@ struct sst_cmd_set_params { char params[0]; } __packed; + +struct sst_cmd_sba_vb_start { + struct sst_dsp_header header; +} __packed; + +union sba_media_loop_params { + struct { + u16 rsvd:8; + struct pcm_cfg cfg; + } part; + u16 full; +} __packed; + +struct sst_cmd_sba_set_media_loop_map { + struct sst_dsp_header header; + u16 switch_state; + union sba_media_loop_params param; + u16 map; +} __packed; + +struct sst_cmd_tone_stop { + struct sst_dsp_header header; + u16 switch_state; +} __packed; + +enum sst_ssp_mode { + SSP_MODE_MASTER = 0, + SSP_MODE_SLAVE = 1, +}; + +enum sst_ssp_pcm_mode { + SSP_PCM_MODE_NORMAL = 0, + SSP_PCM_MODE_NETWORK = 1, +}; + +enum sst_ssp_duplex { + SSP_DUPLEX = 0, + SSP_RX = 1, + SSP_TX = 2, +}; + +enum sst_ssp_fs_frequency { + SSP_FS_8_KHZ = 0, + SSP_FS_16_KHZ = 1, + SSP_FS_44_1_KHZ = 2, + SSP_FS_48_KHZ = 3, +}; + +enum sst_ssp_fs_polarity { + SSP_FS_ACTIVE_LOW = 0, + SSP_FS_ACTIVE_HIGH = 1, +}; + +enum sst_ssp_protocol { + SSP_MODE_PCM = 0, + SSP_MODE_I2S = 1, +}; + +enum sst_ssp_port_id { + SSP_MODEM = 0, + SSP_BT = 1, + SSP_FM = 2, + SSP_CODEC = 3, +}; + +struct sst_cmd_sba_hw_set_ssp { + struct sst_dsp_header header; + u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */ + + u16 switch_state; + + u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */ + u16 nb_slots:4; /* 0-8: slots per frame */ + u16 mode:3; /* 0:Master, 1: Slave */ + u16 duplex:3; + + u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */ + u16 reserved1:8; + + u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */ + u16 reserved2:8; + + u16 frame_sync_frequency; + + u16 frame_sync_polarity:8; + u16 data_polarity:8; + + u16 frame_sync_width; /* 1 to N clocks */ + u16 ssp_protocol:8; + u16 start_delay:8; /* Start delay in terms of clock ticks */ +} __packed; + +#define SST_MAX_TDM_SLOTS 8 + +struct sst_param_sba_ssp_slot_map { + struct sst_dsp_header header; + + u16 param_id; + u16 param_len; + u16 ssp_index; + + u8 rx_slot_map[SST_MAX_TDM_SLOTS]; + u8 tx_slot_map[SST_MAX_TDM_SLOTS]; +} __packed; + +enum { + SST_PROBE_EXTRACTOR = 0, + SST_PROBE_INJECTOR = 1, +}; + /**** widget defines *****/ +#define SST_MODULE_GAIN 1 +#define SST_MODULE_ALGO 2 + +#define SST_FMT_MONO 0 +#define SST_FMT_STEREO 3 + +/* physical SSP numbers */ +enum { + SST_SSP0 = 0, + SST_SSP1, + SST_SSP2, + SST_SSP_LAST = SST_SSP2, +}; + +#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */ +#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */ +#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */ + +struct sst_module { + struct snd_kcontrol *kctl; + struct list_head node; +}; + +struct sst_ssp_config { + u8 ssp_id; + u8 bits_per_slot; + u8 slots; + u8 ssp_mode; + u8 pcm_mode; + u8 duplex; + u8 ssp_protocol; + u8 fs_frequency; + u8 active_slot_map; + u8 start_delay; + u16 fs_width; +}; + +struct sst_ssp_cfg { + const u8 ssp_number; + const int *mux_shift; + const int (*domain_shift)[SST_MAX_SSP_MUX]; + const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS]; +}; + struct sst_ids { u16 location_id; u16 module_id; @@ -397,6 +583,102 @@ struct sst_ids { struct list_head gain_list; const struct sst_pcm_format *pcm_fmt; }; + + +#define SST_AIF_IN(wname, wevent) \ +{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_AIF_OUT(wname, wevent) \ +{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_INPUT(wname, wevent) \ +{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_OUTPUT(wname, wevent) \ +{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent) \ +{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\ + .pcm_fmt = wformat, } \ +} + +#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = NULL, .num_kcontrols = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \ +} + +#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = NULL, .num_kcontrols = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .parent_wname = linked_wname} \ +} + +#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = NULL, .num_kcontrols = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .format = wformat,} \ +} + +/* output is triggered before input */ +#define SST_PATH_INPUT(name, task_id, loc_id, event) \ + SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) + +#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event) \ + SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \ + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) + +#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \ + SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + +#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event) \ + SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \ + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + +#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \ + SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + + +#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \ +{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \ + SND_SOC_DAPM_POST_REG, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .reg = wreg } \ +} + enum sst_gain_kcontrol_type { SST_GAIN_TLV, SST_GAIN_MUTE, @@ -560,4 +842,29 @@ struct sst_enum { struct snd_soc_dapm_widget *w; }; +/* only 4 slots/channels supported atm */ +#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \ + (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, } + +#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \ + xpname " " xmname " " s_ch_name + +#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \ + .info = sst_slot_enum_info, \ + .get = xget, .put = xput, \ + .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \ +} + +#define SST_MUX_CTL_NAME(xpname, xinstance) \ + xpname " " #xinstance + +#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \ + (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts) + +#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \ + SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \ + SST_SSP_MUX_ENUM(xreg, xshift, xtexts)) + #endif -- cgit v0.10.2 From e4f5ccd050e5c366ee1301b5b318bdc2780ce94a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 15 Oct 2014 20:12:58 +0530 Subject: ASoC: Intel: mrfld: add the DSP DAPM widgets This patch adds all DAPM widgets and the event handlers for DSP except the mixers. Signed-off-by: Vinod Koul Signed-off-by: Subhransu S. Prusty Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 9239eff2..9aa09db 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -548,6 +548,41 @@ static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { }; /** + * fill_swm_input - fill in the SWM input ids given the register + * + * The register value is a bit-field inicated which mixer inputs are ON. Use the + * lookup table to get the input-id and fill it in the structure. + */ +static int fill_swm_input(struct snd_soc_component *cmpnt, + struct swm_input_ids *swm_input, unsigned int reg) +{ + uint i, is_set, nb_inputs = 0; + u16 input_loc_id; + + dev_dbg(cmpnt->dev, "reg: %#x\n", reg); + for (i = 0; i < SST_SWM_INPUT_COUNT; i++) { + is_set = reg & BIT(i); + if (!is_set) + continue; + + input_loc_id = swm_mixer_input_ids[i]; + SST_FILL_DESTINATION(2, swm_input->input_id, + input_loc_id, SST_DEFAULT_MODULE_ID); + nb_inputs++; + swm_input++; + dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n", + input_loc_id, nb_inputs); + + if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) { + dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached"); + break; + } + } + return nb_inputs; +} + + +/** * called with lock held */ static int sst_set_pipe_gain(struct sst_ids *ids, @@ -573,6 +608,112 @@ static int sst_set_pipe_gain(struct sst_ids *ids, return ret; } +static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_set_swm cmd; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt); + struct sst_ids *ids = w->priv; + bool set_mixer = false; + struct soc_mixer_control *mc; + int val = 0; + int i = 0; + + dev_dbg(cmpnt->dev, "widget = %s\n", w->name); + /* + * Identify which mixer input is on and send the bitmap of the + * inputs as an IPC to the DSP. + */ + for (i = 0; i < w->num_kcontrols; i++) { + if (dapm_kcontrol_get_value(w->kcontrols[i])) { + mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value; + val |= 1 << mc->shift; + } + } + dev_dbg(cmpnt->dev, "val = %#x\n", val); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + set_mixer = true; + break; + case SND_SOC_DAPM_POST_REG: + if (w->power) + set_mixer = true; + break; + default: + set_mixer = false; + } + + if (set_mixer == false) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event) || + event == SND_SOC_DAPM_POST_REG) + cmd.switch_state = SST_SWM_ON; + else + cmd.switch_state = SST_SWM_OFF; + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + /* MMX_SET_SWM == SBA_SET_SWM */ + cmd.header.command_id = SBA_SET_SWM; + + SST_FILL_DESTINATION(2, cmd.output_id, + ids->location_id, SST_DEFAULT_MODULE_ID); + cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val); + cmd.header.length = offsetof(struct sst_cmd_set_swm, input) + - sizeof(struct sst_dsp_header) + + (cmd.nb_inputs * sizeof(cmd.input[0])); + + return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + ids->task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +/* SBA mixers - 16 inputs */ +#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \ + SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \ + SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \ + SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0), \ + SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0), \ + SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0), \ + SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0), \ + } + +#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \ + { mix_name, "codec_in0 Switch", "codec_in0" }, \ + { mix_name, "codec_in1 Switch", "codec_in1" }, \ + { mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \ + { mix_name, "media_loop1_in Switch", "media_loop1_in" }, \ + { mix_name, "media_loop2_in Switch", "media_loop2_in" }, \ + { mix_name, "pcm0_in Switch", "pcm0_in" }, \ + { mix_name, "pcm1_in Switch", "pcm1_in" } + +#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \ + SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \ + SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \ + SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \ + } + +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls); +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls); + +/* 18 SBA mixers */ +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls); + /* * sst_handle_vb_timer - Start/Stop the DSP scheduler * @@ -784,6 +925,83 @@ static int sst_set_media_loop(struct snd_soc_dapm_widget *w, return ret; } +static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { + SST_AIF_IN("codec_in0", sst_set_be_modules), + SST_AIF_IN("codec_in1", sst_set_be_modules), + SST_AIF_OUT("codec_out0", sst_set_be_modules), + SST_AIF_OUT("codec_out1", sst_set_be_modules), + + /* Media Paths */ + /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */ + SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event), + SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL), + SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path), + SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL), + SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path), + SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path), + + /* SBA PCM Paths */ + SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path), + SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path), + SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path), + + /* SBA Loops */ + SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL), + SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL), + SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL), + SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop), + + /* Media Mixers */ +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"media0_in", NULL, "Compress Playback"}, + {"media1_in", NULL, "Headset Playback"}, + {"media2_in", NULL, "pcm0_out"}, + + {"media0_out mix 0", "media0_in Switch", "media0_in"}, + {"media0_out mix 0", "media1_in Switch", "media1_in"}, + {"media0_out mix 0", "media2_in Switch", "media2_in"}, + {"media0_out mix 0", "media3_in Switch", "media3_in"}, + {"media1_out mix 0", "media0_in Switch", "media0_in"}, + {"media1_out mix 0", "media1_in Switch", "media1_in"}, + {"media1_out mix 0", "media2_in Switch", "media2_in"}, + {"media1_out mix 0", "media3_in Switch", "media3_in"}, + + {"media0_out", NULL, "media0_out mix 0"}, + {"media1_out", NULL, "media1_out mix 0"}, + {"pcm0_in", NULL, "media0_out"}, + {"pcm1_in", NULL, "media1_out"}, + + {"Headset Capture", NULL, "pcm1_out"}, + {"Headset Capture", NULL, "pcm2_out"}, + {"pcm0_out", NULL, "pcm0_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"), + {"pcm1_out", NULL, "pcm1_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"), + {"pcm2_out", NULL, "pcm2_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"), + + {"media_loop1_in", NULL, "media_loop1_out"}, + {"media_loop1_out", NULL, "media_loop1_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"), + {"media_loop2_in", NULL, "media_loop2_out"}, + {"media_loop2_out", NULL, "media_loop2_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"), + {"sprot_loop_in", NULL, "sprot_loop_out"}, + {"sprot_loop_out", NULL, "sprot_loop_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"), + + {"codec_out0", NULL, "codec_out0 mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"), + {"codec_out1", NULL, "codec_out1 mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"), + +}; static const char * const slot_names[] = { "none", "slot 0", "slot 1", "slot 2", "slot 3", @@ -1130,6 +1348,8 @@ static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) { int i, ret = 0; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(&platform->component); struct sst_data *drv = snd_soc_platform_get_drvdata(platform); unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3; @@ -1138,6 +1358,12 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) if (!drv->byte_stream) return -ENOMEM; + snd_soc_dapm_new_controls(dapm, sst_dapm_widgets, + ARRAY_SIZE(sst_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, + ARRAY_SIZE(intercon)); + snd_soc_dapm_new_widgets(dapm->card); + for (i = 0; i < gains; i++) { sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT; sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT; diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 19f83ec..d41d1c3 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -153,6 +153,10 @@ struct sst_device { struct sst_data; int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform); +int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute); +int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable); +int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable); + void sst_set_stream_status(struct sst_runtime_stream *stream, int state); int sst_fill_stream_params(void *substream, const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress); -- cgit v0.10.2 From c82351da2e9f2b14d5664e41b021ec1fd948b932 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 15 Oct 2014 20:12:59 +0530 Subject: ASoC: Intel: mfld-pcm: add FE and BE ops Now that we have added code for managing DSP pipelines we need to add the code for DSPs FrontEnd and Backend dai. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index aa9b600..e7cf18d 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -101,35 +101,11 @@ static struct sst_dev_stream_map dpcm_strm_map[] = { {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0}, }; -/* MFLD - MSIC */ -static struct snd_soc_dai_driver sst_platform_dai[] = { +static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) { - .name = "Headset-cpu-dai", - .id = 0, - .playback = { - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE, - }, - .capture = { - .channels_min = 1, - .channels_max = 5, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE, - }, -}, -{ - .name = "Compress-cpu-dai", - .compress_dai = 1, - .playback = { - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -}; + + return sst_send_pipe_gains(dai, stream, mute); +} /* helper functions */ void sst_set_stream_status(struct sst_runtime_stream *stream, @@ -451,12 +427,133 @@ static int sst_media_hw_free(struct snd_pcm_substream *substream, return snd_pcm_lib_free_pages(substream); } +static int sst_enable_ssp(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + + if (!dai->active) { + ret = sst_handle_vb_timer(dai, true); + if (ret) + return ret; + ret = send_ssp_cmd(dai, dai->name, 1); + } + return ret; +} + +static void sst_disable_ssp(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!dai->active) { + send_ssp_cmd(dai, dai->name, 0); + sst_handle_vb_timer(dai, false); + } +} + static struct snd_soc_dai_ops sst_media_dai_ops = { .startup = sst_media_open, .shutdown = sst_media_close, .prepare = sst_media_prepare, .hw_params = sst_media_hw_params, .hw_free = sst_media_hw_free, + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_compr_dai_ops = { + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_be_dai_ops = { + .startup = sst_enable_ssp, + .shutdown = sst_disable_ssp, +}; + +static struct snd_soc_dai_driver sst_platform_dai[] = { +{ + .name = "media-cpu-dai", + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Headset Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "compress-cpu-dai", + .compress_dai = 1, + .ops = &sst_compr_dai_ops, + .playback = { + .stream_name = "Compress Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +/* BE CPU Dais */ +{ + .name = "ssp0-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp0 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp0 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "ssp1-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp1 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp1 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "ssp2-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp2 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp2 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, }; static int sst_platform_open(struct snd_pcm_substream *substream) -- cgit v0.10.2 From f2b3a93973ca7cda6e6365c0a8ff7c4438778a6f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 15 Oct 2014 20:13:00 +0530 Subject: ASoC: Intel: mrfld: add the DSP mixers Signed-off-by: Vinod Koul Signed-off-by: Subhransu S. Prusty Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 9aa09db..dcdeb28 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -956,6 +956,32 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop), /* Media Mixers */ + SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0, + sst_mix_media0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1, + sst_mix_media1_controls, sst_swm_mixer_event), + + /* SBA PCM mixers */ + SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0, + sst_mix_pcm0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1, + sst_mix_pcm1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2, + sst_mix_pcm2_controls, sst_swm_mixer_event), + + /* SBA Loop mixers */ + SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, + sst_mix_sprot_l0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, + sst_mix_media_l1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, + sst_mix_media_l2_controls, sst_swm_mixer_event), + + /* SBA Backend mixers */ + SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0, + sst_mix_codec0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1, + sst_mix_codec1_controls, sst_swm_mixer_event), }; static const struct snd_soc_dapm_route intercon[] = { -- cgit v0.10.2 From 5914ccf47bb0954210d64a92396632442c4f2a80 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 13:54:29 +0530 Subject: ASoC: intel: turn off COMPILE_TEST for medfield Since medfield machine uses SCU_IPC which is not availble for all archs, so compile test fails on these Reported-by: kbuild test robot Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 726f7d8..f5b4a9c7 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,6 +1,6 @@ config SND_MFLD_MACHINE tristate "SOC Machine Audio driver for Intel Medfield MID platform" - depends on INTEL_SCU_IPC || COMPILE_TEST + depends on INTEL_SCU_IPC select SND_SOC_SN95031 select SND_SST_MFLD_PLATFORM help -- cgit v0.10.2 From f07e51c51e44a6e4e6d003f3bccbbf8a1b2cda0d Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 16 Oct 2014 15:29:15 +0100 Subject: ASoC: Intel: Add TDM support to HSW/BDW SSP port Add TDM support to SSP port via DSP IPC SetDeviceFormat message. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index b629151..92d625a 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1630,6 +1630,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, config.clock_frequency = mclk; config.mode = mode; config.clock_divider = clock_divider; + if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER) + config.channels = 4; + else + config.channels = 2; trace_hsw_device_config_req(&config); diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 2ac194a..063dd6b 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -84,6 +84,7 @@ enum sst_hsw_device_mclk { enum sst_hsw_device_mode { SST_HSW_DEVICE_CLOCK_SLAVE = 0, SST_HSW_DEVICE_CLOCK_MASTER = 1, + SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2, }; /* DX Power State */ @@ -295,7 +296,8 @@ struct sst_hsw_ipc_device_config_req { u32 clock_frequency; u32 mode; u16 clock_divider; - u16 reserved; + u8 channels; + u8 reserved; } __attribute__((packed)); /* Audio Data formats */ -- cgit v0.10.2 From 48dc326f6ba71ba0ee5b1bbfc128a6577ba98608 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 16 Oct 2014 15:29:16 +0100 Subject: ASoC: Intel: Add 4 channel support to DSP. DSP can now support 4 channels in certain use cases. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 92d625a..4799768 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1256,10 +1256,6 @@ int sst_hsw_stream_set_channels(struct sst_hsw *hsw, return -EINVAL; } - /* stereo is only supported atm */ - if (channels != 2) - return -EINVAL; - stream->request.format.ch_num = channels; return 0; } diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 33fc5c3..32a33b9 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -421,13 +421,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, return ret; } - /* we only support stereo atm */ channels = params_channels(params); - if (channels != 2) { - dev_err(rtd->dev, "error: invalid channels %d\n", channels); - return -EINVAL; - } - map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO); sst_hsw_stream_set_map_config(hsw, pcm_data->stream, map, SST_HSW_CHANNEL_CONFIG_STEREO); @@ -743,7 +737,7 @@ static struct snd_soc_dai_driver hsw_dais[] = { .capture = { .stream_name = "Analog Capture", .channels_min = 2, - .channels_max = 2, + .channels_max = 4, .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, }, -- cgit v0.10.2 From 8046249d3ef18a1093fbee9ab8eb16c05c13edc7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 16 Oct 2014 15:29:17 +0100 Subject: ASoC: Intel: Make HSW/BDW pointer debug verbose Improve the debug SNR by making the positional pointer debug more verbose. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 32a33b9..32a6470 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -552,7 +552,7 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data) pos = frames_to_bytes(runtime, (runtime->control->appl_ptr % runtime->buffer_size)); - dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); + dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); /* let alsa know we have play a period */ snd_pcm_period_elapsed(substream); @@ -574,7 +574,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream) offset = bytes_to_frames(runtime, position); ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream); - dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n", + dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n", position, ppos); return offset; } -- cgit v0.10.2 From 3750a8f7d1df5e6442e0fc537d1d37b1b48d3712 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Fri, 17 Oct 2014 00:14:19 +0800 Subject: ASoC: Intel: mrfld: fix semicolon.cocci warnings sound/soc/intel/sst-atom-controls.c:249:2-3: Unneeded semicolon sound/soc/intel/sst-atom-controls.c:289:2-3: Unneeded semicolon Removes unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Signed-off-by: Fengguang Wu Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index dcdeb28..309a8f3 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -454,7 +454,7 @@ static int sst_gain_get(struct snd_kcontrol *kcontrol, dev_err(component->dev, "Invalid Input- gain type:%d\n", mc->type); return -EINVAL; - }; + } return 0; } @@ -494,7 +494,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", mc->type); return -EINVAL; - }; + } if (mc->w && mc->w->power) ret = sst_send_gain_cmd(drv, gv, mc->task_id, -- cgit v0.10.2 From 163d2089d226ab184469f53561f1a63f151757c3 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 20:00:13 +0530 Subject: ASoC: Intel: mrfld - add the dsp sst driver The SST driver is the missing piece in our driver stack not upstreamed, so push it now :) This driver currently supports PCI device on Merrifield. Future updates will bring support for ACPI device as well as future update to PCI devices as well In subsequent patches support is added for DSP loading using memcpy, pcm operations and compressed ops. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h index 0a4e140..268a96ae 100644 --- a/arch/x86/include/asm/platform_sst_audio.h +++ b/arch/x86/include/asm/platform_sst_audio.h @@ -16,6 +16,9 @@ #include +#define MAX_NUM_STREAMS_MRFLD 25 +#define MAX_NUM_STREAMS MAX_NUM_STREAMS_MRFLD + enum sst_audio_task_id_mrfld { SST_TASK_ID_NONE = 0, SST_TASK_ID_SBA = 1, @@ -73,6 +76,37 @@ struct sst_platform_data { unsigned int strm_map_size; }; +struct sst_info { + u32 iram_start; + u32 iram_end; + bool iram_use; + u32 dram_start; + u32 dram_end; + bool dram_use; + u32 imr_start; + u32 imr_end; + bool imr_use; + u32 mailbox_start; + bool use_elf; + bool lpe_viewpt_rqd; + unsigned int max_streams; + u32 dma_max_len; + u8 num_probes; +}; + +struct sst_lib_dnld_info { + unsigned int mod_base; + unsigned int mod_end; + unsigned int mod_table_offset; + unsigned int mod_table_size; + bool mod_ddr_dnld; +}; + +struct sst_platform_info { + const struct sst_info *probe_data; + const struct sst_ipc_info *ipc_info; + const struct sst_lib_dnld_info *lib_info; +}; int add_sst_platform_device(void); #endif diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c new file mode 100644 index 0000000..d88cdd9 --- /dev/null +++ b/sound/soc/intel/sst/sst.c @@ -0,0 +1,434 @@ +/* + * sst.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../sst-dsp.h" + +MODULE_AUTHOR("Vinod Koul "); +MODULE_AUTHOR("Harsha Priya "); +MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver"); +MODULE_LICENSE("GPL v2"); + +static inline bool sst_is_process_reply(u32 msg_id) +{ + return ((msg_id & PROCESS_MSG) ? true : false); +} + +static inline bool sst_validate_mailbox_size(unsigned int size) +{ + return ((size <= SST_MAILBOX_SIZE) ? true : false); +} + +static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context) +{ + union interrupt_reg_mrfld isr; + union ipc_header_mrfld header; + union sst_imr_reg_mrfld imr; + struct ipc_post *msg = NULL; + unsigned int size = 0; + struct intel_sst_drv *drv = (struct intel_sst_drv *) context; + irqreturn_t retval = IRQ_HANDLED; + + /* Interrupt arrived, check src */ + isr.full = sst_shim_read64(drv->shim, SST_ISRX); + + if (isr.part.done_interrupt) { + /* Clear done bit */ + spin_lock(&drv->ipc_spin_lock); + header.full = sst_shim_read64(drv->shim, + drv->ipc_reg.ipcx); + header.p.header_high.part.done = 0; + sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full); + + /* write 1 to clear status register */; + isr.part.done_interrupt = 1; + sst_shim_write64(drv->shim, SST_ISRX, isr.full); + spin_unlock(&drv->ipc_spin_lock); + + /* we can send more messages to DSP so trigger work */ + queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq); + retval = IRQ_HANDLED; + } + + if (isr.part.busy_interrupt) { + /* message from dsp so copy that */ + spin_lock(&drv->ipc_spin_lock); + imr.full = sst_shim_read64(drv->shim, SST_IMRX); + imr.part.busy_interrupt = 1; + sst_shim_write64(drv->shim, SST_IMRX, imr.full); + spin_unlock(&drv->ipc_spin_lock); + header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd); + + if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) { + drv->ops->clear_interrupt(drv); + return IRQ_HANDLED; + } + + if (header.p.header_high.part.large) { + size = header.p.header_low_payload; + if (sst_validate_mailbox_size(size)) { + memcpy_fromio(msg->mailbox_data, + drv->mailbox + drv->mailbox_recv_offset, size); + } else { + dev_err(drv->dev, + "Mailbox not copied, payload size is: %u\n", size); + header.p.header_low_payload = 0; + } + } + + msg->mrfld_header = header; + msg->is_process_reply = + sst_is_process_reply(header.p.header_high.part.msg_id); + spin_lock(&drv->rx_msg_lock); + list_add_tail(&msg->node, &drv->rx_list); + spin_unlock(&drv->rx_msg_lock); + drv->ops->clear_interrupt(drv); + retval = IRQ_WAKE_THREAD; + } + return retval; +} + +static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context) +{ + struct intel_sst_drv *drv = (struct intel_sst_drv *) context; + struct ipc_post *__msg, *msg = NULL; + unsigned long irq_flags; + + spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); + if (list_empty(&drv->rx_list)) { + spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); + return IRQ_HANDLED; + } + + list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) { + list_del(&msg->node); + spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); + if (msg->is_process_reply) + drv->ops->process_message(msg); + else + drv->ops->process_reply(drv, msg); + + if (msg->is_large) + kfree(msg->mailbox_data); + kfree(msg); + spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); + } + spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); + return IRQ_HANDLED; +} + +static struct intel_sst_ops mrfld_ops = { + .interrupt = intel_sst_interrupt_mrfld, + .irq_thread = intel_sst_irq_thread_mrfld, + .clear_interrupt = intel_sst_clear_intr_mrfld, + .start = sst_start_mrfld, + .reset = intel_sst_reset_dsp_mrfld, + .post_message = sst_post_message_mrfld, + .process_reply = sst_process_reply_mrfld, + .alloc_stream = sst_alloc_stream_mrfld, + .post_download = sst_post_download_mrfld, +}; + +int sst_driver_ops(struct intel_sst_drv *sst) +{ + + switch (sst->pci_id) { + case SST_MRFLD_PCI_ID: + sst->tstamp = SST_TIME_STAMP_MRFLD; + sst->ops = &mrfld_ops; + return 0; + + default: + dev_err(sst->dev, + "SST Driver capablities missing for pci_id: %x", sst->pci_id); + return -EINVAL; + }; +} + +void sst_process_pending_msg(struct work_struct *work) +{ + struct intel_sst_drv *ctx = container_of(work, + struct intel_sst_drv, ipc_post_msg_wq); + + ctx->ops->post_message(ctx, NULL, false); +} + +/* +* intel_sst_probe - PCI probe function +* +* @pci: PCI device structure +* @pci_id: PCI device ID structure +* +*/ +static int intel_sst_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + int i, ret = 0; + struct intel_sst_drv *sst_drv_ctx; + struct intel_sst_ops *ops; + struct sst_platform_info *sst_pdata = pci->dev.platform_data; + int ddr_base; + + dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); + sst_drv_ctx = devm_kzalloc(&pci->dev, sizeof(*sst_drv_ctx), GFP_KERNEL); + if (!sst_drv_ctx) + return -ENOMEM; + + sst_drv_ctx->dev = &pci->dev; + sst_drv_ctx->pci_id = pci->device; + if (!sst_pdata) + return -EINVAL; + + sst_drv_ctx->pdata = sst_pdata; + if (!sst_drv_ctx->pdata->probe_data) + return -EINVAL; + + memcpy(&sst_drv_ctx->info, sst_drv_ctx->pdata->probe_data, + sizeof(sst_drv_ctx->info)); + + if (0 != sst_driver_ops(sst_drv_ctx)) + return -EINVAL; + + ops = sst_drv_ctx->ops; + mutex_init(&sst_drv_ctx->sst_lock); + + /* pvt_id 0 reserved for async messages */ + sst_drv_ctx->pvt_id = 1; + sst_drv_ctx->stream_cnt = 0; + sst_drv_ctx->fw_in_mem = NULL; + + /* we use memcpy, so set to 0 */ + sst_drv_ctx->use_dma = 0; + sst_drv_ctx->use_lli = 0; + + INIT_LIST_HEAD(&sst_drv_ctx->memcpy_list); + INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list); + INIT_LIST_HEAD(&sst_drv_ctx->block_list); + INIT_LIST_HEAD(&sst_drv_ctx->rx_list); + + sst_drv_ctx->post_msg_wq = + create_singlethread_workqueue("sst_post_msg_wq"); + if (!sst_drv_ctx->post_msg_wq) { + ret = -EINVAL; + goto do_free_drv_ctx; + } + INIT_WORK(&sst_drv_ctx->ipc_post_msg_wq, sst_process_pending_msg); + init_waitqueue_head(&sst_drv_ctx->wait_queue); + + spin_lock_init(&sst_drv_ctx->ipc_spin_lock); + spin_lock_init(&sst_drv_ctx->block_lock); + spin_lock_init(&sst_drv_ctx->rx_msg_lock); + + dev_info(sst_drv_ctx->dev, "Got drv data max stream %d\n", + sst_drv_ctx->info.max_streams); + for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) { + struct stream_info *stream = &sst_drv_ctx->streams[i]; + + memset(stream, 0, sizeof(*stream)); + stream->pipe_id = PIPE_RSVD; + mutex_init(&stream->lock); + } + + /* Init the device */ + ret = pcim_enable_device(pci); + if (ret) { + dev_err(sst_drv_ctx->dev, + "device can't be enabled. Returned err: %d\n", ret); + goto do_free_mem; + } + sst_drv_ctx->pci = pci_dev_get(pci); + ret = pci_request_regions(pci, SST_DRV_NAME); + if (ret) + goto do_free_mem; + + /* map registers */ + /* DDR base */ + if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) { + sst_drv_ctx->ddr_base = pci_resource_start(pci, 0); + /* check that the relocated IMR base matches with FW Binary */ + ddr_base = relocate_imr_addr_mrfld(sst_drv_ctx->ddr_base); + if (!sst_drv_ctx->pdata->lib_info) { + dev_err(sst_drv_ctx->dev, "lib_info pointer NULL\n"); + ret = -EINVAL; + goto do_release_regions; + } + if (ddr_base != sst_drv_ctx->pdata->lib_info->mod_base) { + dev_err(sst_drv_ctx->dev, + "FW LSP DDR BASE does not match with IFWI\n"); + ret = -EINVAL; + goto do_release_regions; + } + sst_drv_ctx->ddr_end = pci_resource_end(pci, 0); + + sst_drv_ctx->ddr = pcim_iomap(pci, 0, + pci_resource_len(pci, 0)); + if (!sst_drv_ctx->ddr) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(sst_drv_ctx->dev, "sst: DDR Ptr %p\n", sst_drv_ctx->ddr); + } else { + sst_drv_ctx->ddr = NULL; + } + + /* SHIM */ + sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1); + sst_drv_ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); + if (!sst_drv_ctx->shim) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(sst_drv_ctx->dev, "SST Shim Ptr %p\n", sst_drv_ctx->shim); + + /* Shared SRAM */ + sst_drv_ctx->mailbox_add = pci_resource_start(pci, 2); + sst_drv_ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); + if (!sst_drv_ctx->mailbox) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(sst_drv_ctx->dev, "SRAM Ptr %p\n", sst_drv_ctx->mailbox); + + /* IRAM */ + sst_drv_ctx->iram_end = pci_resource_end(pci, 3); + sst_drv_ctx->iram_base = pci_resource_start(pci, 3); + sst_drv_ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); + if (!sst_drv_ctx->iram) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(sst_drv_ctx->dev, "IRAM Ptr %p\n", sst_drv_ctx->iram); + + /* DRAM */ + sst_drv_ctx->dram_end = pci_resource_end(pci, 4); + sst_drv_ctx->dram_base = pci_resource_start(pci, 4); + sst_drv_ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); + if (!sst_drv_ctx->dram) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(sst_drv_ctx->dev, "DRAM Ptr %p\n", sst_drv_ctx->dram); + + + sst_set_fw_state_locked(sst_drv_ctx, SST_RESET); + sst_drv_ctx->irq_num = pci->irq; + /* Register the ISR */ + ret = devm_request_threaded_irq(&pci->dev, pci->irq, + sst_drv_ctx->ops->interrupt, + sst_drv_ctx->ops->irq_thread, 0, SST_DRV_NAME, + sst_drv_ctx); + if (ret) + goto do_release_regions; + dev_dbg(sst_drv_ctx->dev, "Registered IRQ 0x%x\n", pci->irq); + + /* default intr are unmasked so set this as masked */ + if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) + sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, 0xFFFF0038); + + pci_set_drvdata(pci, sst_drv_ctx); + pm_runtime_set_autosuspend_delay(sst_drv_ctx->dev, SST_SUSPEND_DELAY); + pm_runtime_use_autosuspend(sst_drv_ctx->dev); + pm_runtime_allow(sst_drv_ctx->dev); + pm_runtime_put_noidle(sst_drv_ctx->dev); + sst_register(sst_drv_ctx->dev); + sst_drv_ctx->qos = devm_kzalloc(&pci->dev, + sizeof(struct pm_qos_request), GFP_KERNEL); + if (!sst_drv_ctx->qos) { + ret = -ENOMEM; + goto do_release_regions; + } + pm_qos_add_request(sst_drv_ctx->qos, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + return ret; + +do_release_regions: + pci_release_regions(pci); +do_free_mem: + destroy_workqueue(sst_drv_ctx->post_msg_wq); +do_free_drv_ctx: + dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); + return ret; +} + +/** +* intel_sst_remove - PCI remove function +* +* @pci: PCI device structure +* +* This function is called by OS when a device is unloaded +* This frees the interrupt etc +*/ +static void intel_sst_remove(struct pci_dev *pci) +{ + struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); + + pm_runtime_get_noresume(sst_drv_ctx->dev); + pm_runtime_forbid(sst_drv_ctx->dev); + sst_unregister(sst_drv_ctx->dev); + pci_dev_put(sst_drv_ctx->pci); + sst_set_fw_state_locked(sst_drv_ctx, SST_SHUTDOWN); + + flush_scheduled_work(); + destroy_workqueue(sst_drv_ctx->post_msg_wq); + pm_qos_remove_request(sst_drv_ctx->qos); + kfree(sst_drv_ctx->fw_sg_list.src); + kfree(sst_drv_ctx->fw_sg_list.dst); + sst_drv_ctx->fw_sg_list.list_len = 0; + kfree(sst_drv_ctx->fw_in_mem); + sst_drv_ctx->fw_in_mem = NULL; + sst_memcpy_free_resources(sst_drv_ctx); + sst_drv_ctx = NULL; + pci_release_regions(pci); + pci_set_drvdata(pci, NULL); +} + +/* PCI Routines */ +static struct pci_device_id intel_sst_ids[] = { + { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, + { 0, } +}; + +static struct pci_driver sst_driver = { + .name = SST_DRV_NAME, + .id_table = intel_sst_ids, + .probe = intel_sst_probe, + .remove = intel_sst_remove, +}; + +module_pci_driver(sst_driver); diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h new file mode 100644 index 0000000..bfcf51a --- /dev/null +++ b/sound/soc/intel/sst/sst.h @@ -0,0 +1,539 @@ +/* + * sst.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corporation + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Common private declarations for SST + */ +#ifndef __SST_H__ +#define __SST_H__ + +#include + +/* driver names */ +#define SST_DRV_NAME "intel_sst_driver" +#define SST_MRFLD_PCI_ID 0x119A + +#define SST_SUSPEND_DELAY 2000 +#define FW_CONTEXT_MEM (64*1024) +#define SST_ICCM_BOUNDARY 4 +#define SST_CONFIG_SSP_SIGN 0x7ffe8001 + +#define MRFLD_FW_VIRTUAL_BASE 0xC0000000 +#define MRFLD_FW_DDR_BASE_OFFSET 0x0 +#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4 +#define MRFLD_FW_BSS_RESET_BIT 0 + +enum sst_states { + SST_FW_LOADING = 1, + SST_FW_RUNNING, + SST_RESET, + SST_SHUTDOWN, +}; + +enum sst_algo_ops { + SST_SET_ALGO = 0, + SST_GET_ALGO = 1, +}; + +#define SST_BLOCK_TIMEOUT 1000 + +#define FW_SIGNATURE_SIZE 4 + +/* stream states */ +enum sst_stream_states { + STREAM_UN_INIT = 0, /* Freed/Not used stream */ + STREAM_RUNNING = 1, /* Running */ + STREAM_PAUSED = 2, /* Paused stream */ + STREAM_DECODE = 3, /* stream is in decoding only state */ + STREAM_INIT = 4, /* stream init, waiting for data */ + STREAM_RESET = 5, /* force reset on recovery */ +}; + +enum sst_ram_type { + SST_IRAM = 1, + SST_DRAM = 2, + SST_DDR = 5, + SST_CUSTOM_INFO = 7, /* consists of FW binary information */ +}; + +/* SST shim registers to structure mapping */ +union interrupt_reg { + struct { + u64 done_interrupt:1; + u64 busy_interrupt:1; + u64 rsvd:62; + } part; + u64 full; +}; + +union sst_pisr_reg { + struct { + u32 pssp0:1; + u32 pssp1:1; + u32 rsvd0:3; + u32 dmac:1; + u32 rsvd1:26; + } part; + u32 full; +}; + +union sst_pimr_reg { + struct { + u32 ssp0:1; + u32 ssp1:1; + u32 rsvd0:3; + u32 dmac:1; + u32 rsvd1:10; + u32 ssp0_sc:1; + u32 ssp1_sc:1; + u32 rsvd2:3; + u32 dmac_sc:1; + u32 rsvd3:10; + } part; + u32 full; +}; + +union config_status_reg_mrfld { + struct { + u64 lpe_reset:1; + u64 lpe_reset_vector:1; + u64 runstall:1; + u64 pwaitmode:1; + u64 clk_sel:3; + u64 rsvd2:1; + u64 sst_clk:3; + u64 xt_snoop:1; + u64 rsvd3:4; + u64 clk_sel1:6; + u64 clk_enable:3; + u64 rsvd4:6; + u64 slim0baseclk:1; + u64 rsvd:32; + } part; + u64 full; +}; + +union interrupt_reg_mrfld { + struct { + u64 done_interrupt:1; + u64 busy_interrupt:1; + u64 rsvd:62; + } part; + u64 full; +}; + +union sst_imr_reg_mrfld { + struct { + u64 done_interrupt:1; + u64 busy_interrupt:1; + u64 rsvd:62; + } part; + u64 full; +}; + +/** + * struct sst_block - This structure is used to block a user/fw data call to another + * fw/user call + * + * @condition: condition for blocking check + * @ret_code: ret code when block is released + * @data: data ptr + * @size: size of data + * @on: block condition + * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL + * @drv_id: str_id in mfld/ctp, = drv_id in mrfld + * @node: list head node + */ +struct sst_block { + bool condition; + int ret_code; + void *data; + u32 size; + bool on; + u32 msg_id; + u32 drv_id; + struct list_head node; +}; + +/** + * struct stream_info - structure that holds the stream information + * + * @status : stream current state + * @prev : stream prev state + * @ops : stream operation pb/cp/drm... + * @bufs: stream buffer list + * @lock : stream mutex for protecting state + * @pcm_substream : PCM substream + * @period_elapsed : PCM period elapsed callback + * @sfreq : stream sampling freq + * @str_type : stream type + * @cumm_bytes : cummulative bytes decoded + * @str_type : stream type + * @src : stream source + */ +struct stream_info { + unsigned int status; + unsigned int prev; + unsigned int ops; + struct mutex lock; + + void *pcm_substream; + void (*period_elapsed)(void *pcm_substream); + + unsigned int sfreq; + u32 cumm_bytes; + + void *compr_cb_param; + void (*compr_cb)(void *compr_cb_param); + + void *drain_cb_param; + void (*drain_notify)(void *drain_cb_param); + + unsigned int num_ch; + unsigned int pipe_id; + unsigned int str_id; + unsigned int task_id; +}; + +#define SST_FW_SIGN "$SST" +#define SST_FW_LIB_SIGN "$LIB" + +/** + * struct sst_fw_header - FW file headers + * + * @signature : FW signature + * @file_size: size of fw image + * @modules : # of modules + * @file_format : version of header format + * @reserved : reserved fields + */ +struct sst_fw_header { + unsigned char signature[FW_SIGNATURE_SIZE]; + u32 file_size; + u32 modules; + u32 file_format; + u32 reserved[4]; +}; + +/** + * struct fw_module_header - module header in FW + * + * @signature: module signature + * @mod_size: size of module + * @blocks: block count + * @type: block type + * @entry_point: module netry point + */ +struct fw_module_header { + unsigned char signature[FW_SIGNATURE_SIZE]; + u32 mod_size; + u32 blocks; + u32 type; + u32 entry_point; +}; + +/** + * struct fw_block_info - block header for FW + * + * @type: block ram type I/D + * @size: size of block + * @ram_offset: offset in ram + */ +struct fw_block_info { + enum sst_ram_type type; + u32 size; + u32 ram_offset; + u32 rsvd; +}; + +struct sst_runtime_param { + struct snd_sst_runtime_params param; +}; + +struct sst_sg_list { + struct scatterlist *src; + struct scatterlist *dst; + int list_len; + unsigned int sg_idx; +}; + +struct sst_memcpy_list { + struct list_head memcpylist; + void *dstn; + const void *src; + u32 size; + bool is_io; +}; + +/*Firmware Module Information*/ +enum sst_lib_dwnld_status { + SST_LIB_NOT_FOUND = 0, + SST_LIB_FOUND, + SST_LIB_DOWNLOADED, +}; + +struct sst_module_info { + const char *name; /*Library name*/ + u32 id; /*Module ID*/ + u32 entry_pt; /*Module entry point*/ + u8 status; /*module status*/ + u8 rsvd1; + u16 rsvd2; +}; + +/* + * Structure for managing the Library Region(1.5MB) + * in DDR in Merrifield + */ +struct sst_mem_mgr { + phys_addr_t current_base; + int avail; + unsigned int count; +}; + +struct sst_ipc_reg { + int ipcx; + int ipcd; +}; + +struct sst_shim_regs64 { + u64 csr; + u64 pisr; + u64 pimr; + u64 isrx; + u64 isrd; + u64 imrx; + u64 imrd; + u64 ipcx; + u64 ipcd; + u64 isrsc; + u64 isrlpesc; + u64 imrsc; + u64 imrlpesc; + u64 ipcsc; + u64 ipclpesc; + u64 clkctl; + u64 csr2; +}; + +/** + * struct intel_sst_drv - driver ops + * + * @sst_state : current sst device state + * @pci_id : PCI device id loaded + * @shim : SST shim pointer + * @mailbox : SST mailbox pointer + * @iram : SST IRAM pointer + * @dram : SST DRAM pointer + * @pdata : SST info passed as a part of pci platform data + * @shim_phy_add : SST shim phy addr + * @shim_regs64: Struct to save shim registers + * @ipc_dispatch_list : ipc messages dispatched + * @rx_list : to copy the process_reply/process_msg from DSP + * @ipc_post_msg_wq : wq to post IPC messages context + * @mad_ops : MAD driver operations registered + * @mad_wq : MAD driver wq + * @post_msg_wq : wq to post IPC messages + * @streams : sst stream contexts + * @list_lock : sst driver list lock (deprecated) + * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue + * @block_lock : spin lock to add block to block_list and assign pvt_id + * @rx_msg_lock : spin lock to handle the rx messages from the DSP + * @scard_ops : sst card ops + * @pci : sst pci device struture + * @dev : pointer to current device struct + * @sst_lock : sst device lock + * @pvt_id : sst private id + * @stream_cnt : total sst active stream count + * @pb_streams : total active pb streams + * @cp_streams : total active cp streams + * @audio_start : audio status + * @qos : PM Qos struct + * firmware_name : Firmware / Library name + */ +struct intel_sst_drv { + int sst_state; + int irq_num; + unsigned int pci_id; + void __iomem *ddr; + void __iomem *shim; + void __iomem *mailbox; + void __iomem *iram; + void __iomem *dram; + unsigned int mailbox_add; + unsigned int iram_base; + unsigned int dram_base; + unsigned int shim_phy_add; + unsigned int iram_end; + unsigned int dram_end; + unsigned int ddr_end; + unsigned int ddr_base; + unsigned int mailbox_recv_offset; + struct sst_shim_regs64 *shim_regs64; + struct list_head block_list; + struct list_head ipc_dispatch_list; + struct sst_platform_info *pdata; + struct list_head rx_list; + struct work_struct ipc_post_msg_wq; + wait_queue_head_t wait_queue; + struct workqueue_struct *post_msg_wq; + unsigned int tstamp; + /* str_id 0 is not used */ + struct stream_info streams[MAX_NUM_STREAMS+1]; + spinlock_t ipc_spin_lock; + spinlock_t block_lock; + spinlock_t rx_msg_lock; + struct pci_dev *pci; + struct device *dev; + volatile long unsigned pvt_id; + struct mutex sst_lock; + unsigned int stream_cnt; + unsigned int csr_value; + void *fw_in_mem; + struct sst_sg_list fw_sg_list, library_list; + struct intel_sst_ops *ops; + struct sst_info info; + struct pm_qos_request *qos; + unsigned int use_dma; + unsigned int use_lli; + atomic_t fw_clear_context; + bool lib_dwnld_reqd; + struct list_head memcpy_list; + struct sst_ipc_reg ipc_reg; + struct sst_mem_mgr lib_mem_mgr; + /* + * Holder for firmware name. Due to async call it needs to be + * persistent till worker thread gets called + */ + char firmware_name[20]; +}; + +/* misc definitions */ +#define FW_DWNL_ID 0x01 + +struct intel_sst_ops { + irqreturn_t (*interrupt)(int, void *); + irqreturn_t (*irq_thread)(int, void *); + void (*clear_interrupt)(struct intel_sst_drv *ctx); + int (*start)(struct intel_sst_drv *ctx); + int (*reset)(struct intel_sst_drv *ctx); + void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg); + int (*post_message)(struct intel_sst_drv *ctx, + struct ipc_post *msg, bool sync); + void (*process_message)(struct ipc_post *msg); + void (*set_bypass)(bool set); + int (*save_dsp_context)(struct intel_sst_drv *sst); + void (*restore_dsp_context)(void); + int (*alloc_stream)(struct intel_sst_drv *ctx, void *params); + void (*post_download)(struct intel_sst_drv *sst); +}; + +int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); +int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx, + struct snd_sst_bytes_v2 *sbytes); +int sst_set_stream_param(int str_id, struct snd_sst_params *str_param); +int sst_set_metadata(int str_id, char *params); +int sst_get_stream(struct intel_sst_drv *sst_drv_ctx, + struct snd_sst_params *str_param); +int sst_get_stream_allocated(struct intel_sst_drv *ctx, + struct snd_sst_params *str_param, + struct snd_sst_lib_download **lib_dnld); +int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx, + int str_id, bool partial_drain); +int sst_post_message_mrfld(struct intel_sst_drv *ctx, + struct ipc_post *msg, bool sync); +void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg); +int sst_start_mrfld(struct intel_sst_drv *ctx); +int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx); +void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx); + +int sst_load_fw(struct intel_sst_drv *ctx); +int sst_load_library(struct snd_sst_lib_download *lib, u8 ops); +void sst_post_download_mrfld(struct intel_sst_drv *ctx); +int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx); +void sst_memcpy_free_resources(struct intel_sst_drv *ctx); + +int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block); +int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block); +int sst_create_ipc_msg(struct ipc_post **arg, bool large); +int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id); +void sst_clean_stream(struct stream_info *stream); +int intel_sst_register_compress(struct intel_sst_drv *sst); +int intel_sst_remove_compress(struct intel_sst_drv *sst); +void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id); +int sst_send_sync_msg(int ipc, int str_id); +int sst_get_num_channel(struct snd_sst_params *str_param); +int sst_get_sfreq(struct snd_sst_params *str_param); +int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params); +void sst_restore_fw_context(void); +struct sst_block *sst_create_block(struct intel_sst_drv *ctx, + u32 msg_id, u32 drv_id); +int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, + struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, + u32 msg_id, u32 drv_id); +int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed); +int sst_wake_up_block(struct intel_sst_drv *ctx, int result, + u32 drv_id, u32 ipc, void *data, u32 size); +int sst_request_firmware_async(struct intel_sst_drv *ctx); +int sst_driver_ops(struct intel_sst_drv *sst); +struct sst_platform_info *sst_get_acpi_driver_data(const char *hid); +void sst_firmware_load_cb(const struct firmware *fw, void *context); +int sst_prepare_and_post_msg(struct intel_sst_drv *sst, + int task_id, int ipc_msg, int cmd_id, int pipe_id, + size_t mbox_data_len, const void *mbox_data, void **data, + bool large, bool fill_dsp, bool sync, bool response); + +void sst_save_shim64(struct intel_sst_drv *ctx, void __iomem *shim, + struct sst_shim_regs64 *shim_regs); +void sst_process_pending_msg(struct work_struct *work); +int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx); +void sst_init_stream(struct stream_info *stream, + int codec, int sst_id, int ops, u8 slot); +int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id); +struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx, + int str_id); +int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx, + u32 pipe_id); +u32 relocate_imr_addr_mrfld(u32 base_addr); +void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, + struct ipc_post *msg); +int sst_pm_runtime_put(struct intel_sst_drv *sst_drv); +int sst_shim_write(void __iomem *addr, int offset, int value); +u32 sst_shim_read(void __iomem *addr, int offset); +u64 sst_reg_read64(void __iomem *addr, int offset); +int sst_shim_write64(void __iomem *addr, int offset, u64 value); +u64 sst_shim_read64(void __iomem *addr, int offset); +void sst_set_fw_state_locked( + struct intel_sst_drv *sst_drv_ctx, int sst_state); +void sst_fill_header_mrfld(union ipc_header_mrfld *header, + int msg, int task_id, int large, int drv_id); +void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, + int pipe_id, int len); + +int sst_register(struct device *); +int sst_unregister(struct device *); + +#endif -- cgit v0.10.2 From 9012c9544eeac485b2193fea721233907f0847fa Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 20:00:14 +0530 Subject: ASoC: Intel: mrfld - Add DSP load and management This patch contains all dsp controlling functions like firmware download, setting/resetting dsp cores, etc. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c new file mode 100644 index 0000000..b6d27c1 --- /dev/null +++ b/sound/soc/intel/sst/sst_loader.c @@ -0,0 +1,461 @@ +/* + * sst_dsp.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains all dsp controlling functions like firmware download, + * setting/resetting dsp cores, etc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../sst-dsp.h" + +static void memcpy32_toio(void __iomem *dst, const void *src, int count) +{ + int i; + const u32 *src_32 = src; + u32 *dst_32 = dst; + + for (i = 0; i < count/sizeof(u32); i++) + writel(*src_32++, dst_32++); +} + +/** + * intel_sst_reset_dsp_mrfld - Resetting SST DSP + * + * This resets DSP in case of MRFLD platfroms + */ +int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx) +{ + union config_status_reg_mrfld csr; + + dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n"); + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + + dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); + + csr.full |= 0x7; + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + + dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); + + csr.full &= ~(0x1); + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); + return 0; +} + +/** + * sst_start_merrifield - Start the SST DSP processor + * + * This starts the DSP in MERRIFIELD platfroms + */ +int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx) +{ + union config_status_reg_mrfld csr; + + dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n"); + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); + + csr.full |= 0x7; + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full); + + csr.part.xt_snoop = 1; + csr.full &= ~(0x5); + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n", + csr.full); + return 0; +} + +static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size, + struct fw_module_header **module, u32 *num_modules) +{ + struct sst_fw_header *header; + const void *sst_fw_in_mem = ctx->fw_in_mem; + + dev_dbg(ctx->dev, "Enter\n"); + + /* Read the header information from the data pointer */ + header = (struct sst_fw_header *)sst_fw_in_mem; + dev_dbg(ctx->dev, + "header sign=%s size=%x modules=%x fmt=%x size=%zx\n", + header->signature, header->file_size, header->modules, + header->file_format, sizeof(*header)); + + /* verify FW */ + if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) || + (size != header->file_size + sizeof(*header))) { + /* Invalid FW signature */ + dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n"); + return -EINVAL; + } + *num_modules = header->modules; + *module = (void *)sst_fw_in_mem + sizeof(*header); + + return 0; +} + +/* + * sst_fill_memcpy_list - Fill the memcpy list + * + * @memcpy_list: List to be filled + * @destn: Destination addr to be filled in the list + * @src: Source addr to be filled in the list + * @size: Size to be filled in the list + * + * Adds the node to the list after required fields + * are populated in the node + */ +static int sst_fill_memcpy_list(struct list_head *memcpy_list, + void *destn, const void *src, u32 size, bool is_io) +{ + struct sst_memcpy_list *listnode; + + listnode = kzalloc(sizeof(*listnode), GFP_KERNEL); + if (listnode == NULL) + return -ENOMEM; + listnode->dstn = destn; + listnode->src = src; + listnode->size = size; + listnode->is_io = is_io; + list_add_tail(&listnode->memcpylist, memcpy_list); + + return 0; +} + +/** + * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list + * + * @sst_drv_ctx : driver context + * @module : FW module header + * @memcpy_list : Pointer to the list to be populated + * Create the memcpy list as the number of block to be copied + * returns error or 0 if module sizes are proper + */ +static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx, + struct fw_module_header *module, struct list_head *memcpy_list) +{ + struct fw_block_info *block; + u32 count; + int ret_val = 0; + void __iomem *ram_iomem; + + dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n", + module->signature, module->mod_size, + module->blocks, module->type); + dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point); + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->blocks; count++) { + if (block->size <= 0) { + dev_err(sst_drv_ctx->dev, "block size invalid\n"); + return -EINVAL; + } + switch (block->type) { + case SST_IRAM: + ram_iomem = sst_drv_ctx->iram; + break; + case SST_DRAM: + ram_iomem = sst_drv_ctx->dram; + break; + case SST_DDR: + ram_iomem = sst_drv_ctx->ddr; + break; + case SST_CUSTOM_INFO: + block = (void *)block + sizeof(*block) + block->size; + continue; + default: + dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n", + block->type, count); + return -EINVAL; + } + + ret_val = sst_fill_memcpy_list(memcpy_list, + ram_iomem + block->ram_offset, + (void *)block + sizeof(*block), block->size, 1); + if (ret_val) + return ret_val; + + block = (void *)block + sizeof(*block) + block->size; + } + return 0; +} + +/** + * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy + * + * @ctx : pointer to drv context + * @size : size of the firmware + * @fw_list : pointer to list_head to be populated + * This function parses the FW image and saves the parsed image in the list + * for memcpy + */ +static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size, + struct list_head *fw_list) +{ + struct fw_module_header *module; + u32 count, num_modules; + int ret_val; + + ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules); + if (ret_val) + return ret_val; + + for (count = 0; count < num_modules; count++) { + ret_val = sst_parse_module_memcpy(ctx, module, fw_list); + if (ret_val) + return ret_val; + module = (void *)module + sizeof(*module) + module->mod_size; + } + + return 0; +} + +/** + * sst_do_memcpy - function initiates the memcpy + * + * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated + * + * Triggers the memcpy + */ +static void sst_do_memcpy(struct list_head *memcpy_list) +{ + struct sst_memcpy_list *listnode; + + list_for_each_entry(listnode, memcpy_list, memcpylist) { + if (listnode->is_io == true) + memcpy32_toio((void __iomem *)listnode->dstn, + listnode->src, listnode->size); + else + memcpy(listnode->dstn, listnode->src, listnode->size); + } +} + +void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx) +{ + struct sst_memcpy_list *listnode, *tmplistnode; + + /* Free the list */ + if (!list_empty(&sst_drv_ctx->memcpy_list)) { + list_for_each_entry_safe(listnode, tmplistnode, + &sst_drv_ctx->memcpy_list, memcpylist) { + list_del(&listnode->memcpylist); + kfree(listnode); + } + } +} + +static int sst_cache_and_parse_fw(struct intel_sst_drv *sst, + const struct firmware *fw) +{ + int retval = 0; + + sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL); + if (!sst->fw_in_mem) { + retval = -ENOMEM; + goto end_release; + } + dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem); + dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem)); + memcpy(sst->fw_in_mem, fw->data, fw->size); + retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list); + if (retval) { + dev_err(sst->dev, "Failed to parse fw\n"); + kfree(sst->fw_in_mem); + sst->fw_in_mem = NULL; + } + +end_release: + release_firmware(fw); + return retval; + +} + +void sst_firmware_load_cb(const struct firmware *fw, void *context) +{ + struct intel_sst_drv *ctx = context; + + dev_dbg(ctx->dev, "Enter\n"); + + if (fw == NULL) { + dev_err(ctx->dev, "request fw failed\n"); + return; + } + + mutex_lock(&ctx->sst_lock); + + if (ctx->sst_state != SST_RESET || + ctx->fw_in_mem != NULL) { + if (fw != NULL) + release_firmware(fw); + mutex_unlock(&ctx->sst_lock); + return; + } + + dev_dbg(ctx->dev, "Request Fw completed\n"); + sst_cache_and_parse_fw(ctx, fw); + mutex_unlock(&ctx->sst_lock); +} + +/* + * sst_request_fw - requests audio fw from kernel and saves a copy + * + * This function requests the SST FW from the kernel, parses it and + * saves a copy in the driver context + */ +static int sst_request_fw(struct intel_sst_drv *sst) +{ + int retval = 0; + char name[20]; + const struct firmware *fw; + + dev_dbg(sst->dev, "Requesting FW %s now...\n", name); + + retval = request_firmware(&fw, name, sst->dev); + if (fw == NULL) { + dev_err(sst->dev, "fw is returning as null\n"); + return -EINVAL; + } + if (retval) { + dev_err(sst->dev, "request fw failed %d\n", retval); + return retval; + } + mutex_lock(&sst->sst_lock); + retval = sst_cache_and_parse_fw(sst, fw); + mutex_unlock(&sst->sst_lock); + + return retval; +} + +/* + * Writing the DDR physical base to DCCM offset + * so that FW can use it to setup TLB + */ +static void sst_dccm_config_write(void __iomem *dram_base, + unsigned int ddr_base) +{ + void __iomem *addr; + u32 bss_reset = 0; + + addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET); + memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32)); + bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT); + addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET); + memcpy32_toio(addr, &bss_reset, sizeof(u32)); + +} + +void sst_post_download_mrfld(struct intel_sst_drv *ctx) +{ + sst_dccm_config_write(ctx->dram, ctx->ddr_base); + dev_dbg(ctx->dev, "config written to DCCM\n"); +} + +/** + * sst_load_fw - function to load FW into DSP + * Transfers the FW to DSP using dma/memcpy + */ +int sst_load_fw(struct intel_sst_drv *sst_drv_ctx) +{ + int ret_val = 0; + struct sst_block *block; + + dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n"); + + if (sst_drv_ctx->sst_state != SST_RESET || + sst_drv_ctx->sst_state == SST_SHUTDOWN) + return -EAGAIN; + + if (!sst_drv_ctx->fw_in_mem) { + dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n"); + ret_val = sst_request_fw(sst_drv_ctx); + if (ret_val) + return ret_val; + } + + BUG_ON(!sst_drv_ctx->fw_in_mem); + block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); + if (block == NULL) + return -ENOMEM; + + /* Prevent C-states beyond C6 */ + pm_qos_update_request(sst_drv_ctx->qos, 0); + + sst_drv_ctx->sst_state = SST_FW_LOADING; + + ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx); + if (ret_val) + goto restore; + + sst_do_memcpy(&sst_drv_ctx->memcpy_list); + + /* Write the DRAM/DCCM config before enabling FW */ + if (sst_drv_ctx->ops->post_download) + sst_drv_ctx->ops->post_download(sst_drv_ctx); + + /* bring sst out of reset */ + ret_val = sst_drv_ctx->ops->start(sst_drv_ctx); + if (ret_val) + goto restore; + + ret_val = sst_wait_timeout(sst_drv_ctx, block); + if (ret_val) { + dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val); + /* FW download failed due to timeout */ + ret_val = -EBUSY; + + } + + +restore: + /* Re-enable Deeper C-states beyond C6 */ + pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); + sst_free_block(sst_drv_ctx, block); + dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n"); + + if (sst_drv_ctx->ops->restore_dsp_context) + sst_drv_ctx->ops->restore_dsp_context(); + sst_drv_ctx->sst_state = SST_FW_RUNNING; + return ret_val; +} + -- cgit v0.10.2 From cc547054d3122646cb9be5d4fc80699bf3e281d8 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 20:00:15 +0530 Subject: ASoC: Intel: sst - add pcm ops handling This patch adds low level IPC handling for pcm stream operations Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c new file mode 100644 index 0000000..aadb0db --- /dev/null +++ b/sound/soc/intel/sst/sst_drv_interface.c @@ -0,0 +1,403 @@ +/* + * sst_drv_interface.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../sst-dsp.h" + + + +#define NUM_CODEC 2 +#define MIN_FRAGMENT 2 +#define MAX_FRAGMENT 4 +#define MIN_FRAGMENT_SIZE (50 * 1024) +#define MAX_FRAGMENT_SIZE (1024 * 1024) +#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1) + +int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id) +{ + struct stream_info *stream; + int ret = 0; + + stream = get_stream_info(ctx, str_id); + if (stream) { + /* str_id is valid, so stream is alloacted */ + ret = sst_free_stream(ctx, str_id); + if (ret) + sst_clean_stream(&ctx->streams[str_id]); + return ret; + } + return ret; +} + +int sst_get_stream_allocated(struct intel_sst_drv *ctx, + struct snd_sst_params *str_param, + struct snd_sst_lib_download **lib_dnld) +{ + int retval; + + retval = ctx->ops->alloc_stream(ctx, str_param); + if (retval > 0) + dev_dbg(ctx->dev, "Stream allocated %d\n", retval); + return retval; + +} + +/* + * sst_get_sfreq - this function returns the frequency of the stream + * + * @str_param : stream params + */ +int sst_get_sfreq(struct snd_sst_params *str_param) +{ + switch (str_param->codec) { + case SST_CODEC_TYPE_PCM: + return str_param->sparams.uc.pcm_params.sfreq; + case SST_CODEC_TYPE_AAC: + return str_param->sparams.uc.aac_params.externalsr; + case SST_CODEC_TYPE_MP3: + return 0; + default: + return -EINVAL; + } +} + +/* + * sst_get_sfreq - this function returns the frequency of the stream + * + * @str_param : stream params + */ +int sst_get_num_channel(struct snd_sst_params *str_param) +{ + switch (str_param->codec) { + case SST_CODEC_TYPE_PCM: + return str_param->sparams.uc.pcm_params.num_chan; + case SST_CODEC_TYPE_MP3: + return str_param->sparams.uc.mp3_params.num_chan; + case SST_CODEC_TYPE_AAC: + return str_param->sparams.uc.aac_params.num_chan; + default: + return -EINVAL; + } +} + +/* + * sst_get_stream - this function prepares for stream allocation + * + * @str_param : stream param + */ +int sst_get_stream(struct intel_sst_drv *ctx, + struct snd_sst_params *str_param) +{ + int retval; + struct stream_info *str_info; + + /* stream is not allocated, we are allocating */ + retval = ctx->ops->alloc_stream(ctx, str_param); + if (retval <= 0) { + return -EIO; + } + /* store sampling freq */ + str_info = &ctx->streams[retval]; + str_info->sfreq = sst_get_sfreq(str_param); + + return retval; +} + +static int sst_power_control(struct device *dev, bool state) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + dev_dbg(ctx->dev, "state:%d", state); + if (state == true) + return pm_runtime_get_sync(dev); + else + return sst_pm_runtime_put(ctx); +} + +/* + * sst_open_pcm_stream - Open PCM interface + * + * @str_param: parameters of pcm stream + * + * This function is called by MID sound card driver to open + * a new pcm interface + */ +static int sst_open_pcm_stream(struct device *dev, + struct snd_sst_params *str_param) +{ + int retval; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (!str_param) + return -EINVAL; + + retval = pm_runtime_get_sync(ctx->dev); + if (retval < 0) + return retval; + retval = sst_get_stream(ctx, str_param); + if (retval > 0) { + ctx->stream_cnt++; + } else { + dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval); + sst_pm_runtime_put(ctx); + } + + return retval; +} + +/* + * sst_close_pcm_stream - Close PCM interface + * + * @str_id: stream id to be closed + * + * This function is called by MID sound card driver to close + * an existing pcm interface + */ +static int sst_close_pcm_stream(struct device *dev, unsigned int str_id) +{ + struct stream_info *stream; + int retval = 0; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + stream = get_stream_info(ctx, str_id); + if (!stream) { + dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id); + return -EINVAL; + } + + if (stream->status == STREAM_RESET) { + /* silently fail here as we have cleaned the stream earlier */ + dev_dbg(ctx->dev, "stream in reset state...\n"); + + retval = 0; + goto put; + } + + retval = free_stream_context(ctx, str_id); +put: + stream->pcm_substream = NULL; + stream->status = STREAM_UN_INIT; + stream->period_elapsed = NULL; + ctx->stream_cnt--; + + sst_pm_runtime_put(ctx); + + dev_dbg(ctx->dev, "Exit\n"); + return 0; +} + +static inline int sst_calc_tstamp(struct intel_sst_drv *ctx, + struct pcm_stream_info *info, + struct snd_pcm_substream *substream, + struct snd_sst_tstamp *fw_tstamp) +{ + size_t delay_bytes, delay_frames; + size_t buffer_sz; + u32 pointer_bytes, pointer_samples; + + dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n", + fw_tstamp->ring_buffer_counter); + dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n", + fw_tstamp->hardware_counter); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter - + fw_tstamp->hardware_counter); + else + delay_bytes = (size_t) (fw_tstamp->hardware_counter - + fw_tstamp->ring_buffer_counter); + delay_frames = bytes_to_frames(substream->runtime, delay_bytes); + buffer_sz = snd_pcm_lib_buffer_bytes(substream); + div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes); + pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes); + + dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes); + + info->buffer_ptr = pointer_samples / substream->runtime->channels; + + info->pcm_delay = delay_frames / substream->runtime->channels; + dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n", + info->buffer_ptr, info->pcm_delay); + return 0; +} + +static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) +{ + struct stream_info *stream; + struct snd_pcm_substream *substream; + struct snd_sst_tstamp fw_tstamp; + unsigned int str_id; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + str_id = info->str_id; + stream = get_stream_info(ctx, str_id); + if (!stream) + return -EINVAL; + + if (!stream->pcm_substream) + return -EINVAL; + substream = stream->pcm_substream; + + memcpy_fromio(&fw_tstamp, + ((void *)(ctx->mailbox + ctx->tstamp) + + (str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + return sst_calc_tstamp(ctx, info, substream, &fw_tstamp); +} + +static int sst_stream_start(struct device *dev, int str_id) +{ + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->sst_state != SST_FW_RUNNING) + return 0; + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + str_info->prev = str_info->status; + str_info->status = STREAM_RUNNING; + sst_start_stream(ctx, str_id); + + return 0; +} + +static int sst_stream_drop(struct device *dev, int str_id) +{ + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->sst_state != SST_FW_RUNNING) + return 0; + + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + str_info->prev = STREAM_UN_INIT; + str_info->status = STREAM_INIT; + return sst_drop_stream(ctx, str_id); +} + +static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info) +{ + int str_id = 0; + struct stream_info *stream; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + str_id = str_info->str_id; + + if (ctx->sst_state != SST_FW_RUNNING) + return 0; + + stream = get_stream_info(ctx, str_id); + if (!stream) + return -EINVAL; + + dev_dbg(ctx->dev, "setting the period ptrs\n"); + stream->pcm_substream = str_info->arg; + stream->period_elapsed = str_info->period_elapsed; + stream->sfreq = str_info->sfreq; + stream->prev = stream->status; + stream->status = STREAM_INIT; + dev_dbg(ctx->dev, + "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n", + stream->pcm_substream, stream->period_elapsed, + stream->sfreq, stream->status); + + return 0; +} + +/* + * sst_set_byte_stream - Set generic params + * + * @cmd: control cmd to be set + * @arg: command argument + * + * This function is called by MID sound card driver to configure + * SST runtime params. + */ +static int sst_send_byte_stream(struct device *dev, + struct snd_sst_bytes_v2 *bytes) +{ + int ret_val = 0; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (NULL == bytes) + return -EINVAL; + ret_val = pm_runtime_get_sync(ctx->dev); + if (ret_val < 0) + return ret_val; + + ret_val = sst_send_byte_stream_mrfld(ctx, bytes); + sst_pm_runtime_put(ctx); + + return ret_val; +} + +static struct sst_ops pcm_ops = { + .open = sst_open_pcm_stream, + .stream_init = sst_stream_init, + .stream_start = sst_stream_start, + .stream_drop = sst_stream_drop, + .stream_read_tstamp = sst_read_timestamp, + .send_byte_stream = sst_send_byte_stream, + .close = sst_close_pcm_stream, + .power = sst_power_control, +}; + +static struct sst_device sst_dsp_device = { + .name = "Intel(R) SST LPE", + .dev = NULL, + .ops = &pcm_ops, +}; + +/* + * sst_register - function to register DSP + * + * This functions registers DSP with the platform driver + */ +int sst_register(struct device *dev) +{ + int ret_val; + + sst_dsp_device.dev = dev; + ret_val = sst_register_dsp(&sst_dsp_device); + if (ret_val) + dev_err(dev, "Unable to register DSP with platform driver\n"); + + return ret_val; +} + +int sst_unregister(struct device *dev) +{ + return sst_unregister_dsp(&sst_dsp_device); +} -- cgit v0.10.2 From ea12aa4acd703b507a20354b7af378b1497369e4 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 20:00:16 +0530 Subject: ASoC: Intel: sst: Add IPC handling This patch adds APIs to post IPCs and process reply messages. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c new file mode 100644 index 0000000..41a2b41 --- /dev/null +++ b/sound/soc/intel/sst/sst_ipc.c @@ -0,0 +1,358 @@ +/* + * sst_ipc.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corporation + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../sst-dsp.h" + +struct sst_block *sst_create_block(struct intel_sst_drv *ctx, + u32 msg_id, u32 drv_id) +{ + struct sst_block *msg = NULL; + + dev_dbg(ctx->dev, "Enter\n"); + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return NULL; + msg->condition = false; + msg->on = true; + msg->msg_id = msg_id; + msg->drv_id = drv_id; + spin_lock_bh(&ctx->block_lock); + list_add_tail(&msg->node, &ctx->block_list); + spin_unlock_bh(&ctx->block_lock); + + return msg; +} + +int sst_wake_up_block(struct intel_sst_drv *ctx, int result, + u32 drv_id, u32 ipc, void *data, u32 size) +{ + struct sst_block *block = NULL; + + dev_dbg(ctx->dev, "Enter\n"); + + spin_lock_bh(&ctx->block_lock); + list_for_each_entry(block, &ctx->block_list, node) { + dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id, + block->drv_id); + if (block->msg_id == ipc && block->drv_id == drv_id) { + dev_dbg(ctx->dev, "free up the block\n"); + block->ret_code = result; + block->data = data; + block->size = size; + block->condition = true; + spin_unlock_bh(&ctx->block_lock); + wake_up(&ctx->wait_queue); + return 0; + } + } + spin_unlock_bh(&ctx->block_lock); + dev_dbg(ctx->dev, + "Block not found or a response received for a short msg for ipc %d, drv_id %d\n", + ipc, drv_id); + return -EINVAL; +} + +int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed) +{ + struct sst_block *block = NULL, *__block; + + dev_dbg(ctx->dev, "Enter\n"); + spin_lock_bh(&ctx->block_lock); + list_for_each_entry_safe(block, __block, &ctx->block_list, node) { + if (block == freed) { + pr_debug("pvt_id freed --> %d\n", freed->drv_id); + /* toggle the index position of pvt_id */ + list_del(&freed->node); + spin_unlock_bh(&ctx->block_lock); + kfree(freed->data); + freed->data = NULL; + kfree(freed); + return 0; + } + } + spin_unlock_bh(&ctx->block_lock); + dev_err(ctx->dev, "block is already freed!!!\n"); + return -EINVAL; +} + +int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx, + struct ipc_post *ipc_msg, bool sync) +{ + struct ipc_post *msg = ipc_msg; + union ipc_header_mrfld header; + unsigned int loop_count = 0; + int retval = 0; + unsigned long irq_flags; + + dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync); + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); + if (sync) { + while (header.p.header_high.part.busy) { + if (loop_count > 25) { + dev_err(sst_drv_ctx->dev, + "sst: Busy wait failed, cant send this msg\n"); + retval = -EBUSY; + goto out; + } + cpu_relax(); + loop_count++; + header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); + } + } else { + if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { + /* queue is empty, nothing to send */ + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + dev_dbg(sst_drv_ctx->dev, + "Empty msg queue... NO Action\n"); + return 0; + } + + if (header.p.header_high.part.busy) { + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n"); + return 0; + } + + /* copy msg from list */ + msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, + struct ipc_post, node); + list_del(&msg->node); + } + dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n", + msg->mrfld_header.p.header_high.full); + dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n", + msg->mrfld_header.p.header_low_payload); + + if (msg->mrfld_header.p.header_high.part.large) + memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, + msg->mailbox_data, + msg->mrfld_header.p.header_low_payload); + + sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full); + +out: + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + kfree(msg->mailbox_data); + kfree(msg); + return retval; +} + +void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) +{ + union interrupt_reg_mrfld isr; + union interrupt_reg_mrfld imr; + union ipc_header_mrfld clear_ipc; + unsigned long irq_flags; + + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); + isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); + + /* write 1 to clear*/ + isr.part.busy_interrupt = 1; + sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full); + + /* Set IA done bit */ + clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD); + + clear_ipc.p.header_high.part.busy = 0; + clear_ipc.p.header_high.part.done = 1; + clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS; + sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full); + /* un mask busy interrupt */ + imr.part.busy_interrupt = 0; + sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); +} + + +/* + * process_fw_init - process the FW init msg + * + * @msg: IPC message mailbox data from FW + * + * This function processes the FW init msg from FW + * marks FW state and prints debug info of loaded FW + */ +static void process_fw_init(struct intel_sst_drv *sst_drv_ctx, + void *msg) +{ + struct ipc_header_fw_init *init = + (struct ipc_header_fw_init *)msg; + int retval = 0; + + dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n"); + if (init->result) { + sst_drv_ctx->sst_state = SST_RESET; + dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n", + init->result); + retval = init->result; + goto ret; + } + +ret: + sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0); +} + +static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx, + struct ipc_post *msg) +{ + u32 msg_id; + int str_id; + u32 data_size, i; + void *data_offset; + struct stream_info *stream; + union ipc_header_high msg_high; + u32 msg_low, pipe_id; + + msg_high = msg->mrfld_header.p.header_high; + msg_low = msg->mrfld_header.p.header_low_payload; + msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id; + data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr)); + data_size = msg_low - (sizeof(struct ipc_dsp_hdr)); + + switch (msg_id) { + case IPC_SST_PERIOD_ELAPSED_MRFLD: + pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; + str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id); + if (str_id > 0) { + dev_dbg(sst_drv_ctx->dev, + "Period elapsed rcvd for pipe id 0x%x\n", + pipe_id); + stream = &sst_drv_ctx->streams[str_id]; + if (stream->period_elapsed) + stream->period_elapsed(stream->pcm_substream); + if (stream->compr_cb) + stream->compr_cb(stream->compr_cb_param); + } + break; + + case IPC_IA_DRAIN_STREAM_MRFLD: + pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; + str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id); + if (str_id > 0) { + stream = &sst_drv_ctx->streams[str_id]; + if (stream->drain_notify) + stream->drain_notify(stream->drain_cb_param); + } + break; + + case IPC_IA_FW_ASYNC_ERR_MRFLD: + dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n"); + for (i = 0; i < (data_size/4); i++) + print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE, + 16, 4, data_offset, data_size, false); + break; + + case IPC_IA_FW_INIT_CMPLT_MRFLD: + process_fw_init(sst_drv_ctx, data_offset); + break; + + case IPC_IA_BUF_UNDER_RUN_MRFLD: + pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; + str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id); + if (str_id > 0) + dev_err(sst_drv_ctx->dev, + "Buffer under-run for pipe:%#x str_id:%d\n", + pipe_id, str_id); + break; + + default: + dev_err(sst_drv_ctx->dev, + "Unrecognized async msg from FW msg_id %#x\n", msg_id); + } +} + +void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, + struct ipc_post *msg) +{ + unsigned int drv_id; + void *data; + union ipc_header_high msg_high; + u32 msg_low; + struct ipc_dsp_hdr *dsp_hdr; + unsigned int cmd_id; + + msg_high = msg->mrfld_header.p.header_high; + msg_low = msg->mrfld_header.p.header_low_payload; + + dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n", + msg->mrfld_header.p.header_high.full, + msg->mrfld_header.p.header_low_payload); + + drv_id = msg_high.part.drv_id; + + /* Check for async messages first */ + if (drv_id == SST_ASYNC_DRV_ID) { + /*FW sent async large message*/ + process_fw_async_msg(sst_drv_ctx, msg); + return; + } + + /* FW sent short error response for an IPC */ + if (msg_high.part.result && drv_id && !msg_high.part.large) { + /* 32-bit FW error code in msg_low */ + dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low); + sst_wake_up_block(sst_drv_ctx, msg_high.part.result, + msg_high.part.drv_id, + msg_high.part.msg_id, NULL, 0); + return; + } + + /* + * Process all valid responses + * if it is a large message, the payload contains the size to + * copy from mailbox + **/ + if (msg_high.part.large) { + data = kzalloc(msg_low, GFP_KERNEL); + if (!data) + return; + memcpy(data, (void *) msg->mailbox_data, msg_low); + /* Copy command id so that we can use to put sst to reset */ + dsp_hdr = (struct ipc_dsp_hdr *)data; + cmd_id = dsp_hdr->cmd_id; + dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id); + if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result, + msg_high.part.drv_id, + msg_high.part.msg_id, data, msg_low)) + kfree(data); + } else { + sst_wake_up_block(sst_drv_ctx, msg_high.part.result, + msg_high.part.drv_id, + msg_high.part.msg_id, NULL, 0); + } + +} -- cgit v0.10.2 From 3d9ff34622badd65543430a784f7af9838c5c3fc Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 20:00:17 +0530 Subject: ASoC: Intel: sst: add stream operations This patch adds pcm and compressed stream control operations. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c new file mode 100644 index 0000000..dae2a41 --- /dev/null +++ b/sound/soc/intel/sst/sst_stream.c @@ -0,0 +1,437 @@ +/* + * sst_stream.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../sst-dsp.h" + +int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) +{ + struct snd_sst_alloc_mrfld alloc_param; + struct snd_sst_params *str_params; + struct snd_sst_tstamp fw_tstamp; + struct stream_info *str_info; + struct snd_sst_alloc_response *response; + unsigned int str_id, pipe_id, task_id; + int i, num_ch, ret = 0; + void *data = NULL; + + dev_dbg(sst_drv_ctx->dev, "Enter\n"); + BUG_ON(!params); + + str_params = (struct snd_sst_params *)params; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.operation = str_params->ops; + alloc_param.codec_type = str_params->codec; + alloc_param.sg_count = str_params->aparams.sg_count; + alloc_param.ring_buf_info[0].addr = + str_params->aparams.ring_buf_info[0].addr; + alloc_param.ring_buf_info[0].size = + str_params->aparams.ring_buf_info[0].size; + alloc_param.frag_size = str_params->aparams.frag_size; + + memcpy(&alloc_param.codec_params, &str_params->sparams, + sizeof(struct snd_sst_stream_params)); + + /* + * fill channel map params for multichannel support. + * Ideally channel map should be received from upper layers + * for multichannel support. + * Currently hardcoding as per FW reqm. + */ + num_ch = sst_get_num_channel(str_params); + for (i = 0; i < 8; i++) { + if (i < num_ch) + alloc_param.codec_params.uc.pcm_params.channel_map[i] = i; + else + alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF; + } + + str_id = str_params->stream_id; + str_info = get_stream_info(sst_drv_ctx, str_id); + if (str_info == NULL) { + dev_err(sst_drv_ctx->dev, "get stream info returned null\n"); + return -EINVAL; + } + + pipe_id = str_params->device_type; + task_id = str_params->task; + sst_drv_ctx->streams[str_id].pipe_id = pipe_id; + sst_drv_ctx->streams[str_id].task_id = task_id; + sst_drv_ctx->streams[str_id].num_ch = num_ch; + + if (sst_drv_ctx->info.lpe_viewpt_rqd) + alloc_param.ts = sst_drv_ctx->info.mailbox_start + + sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); + else + alloc_param.ts = sst_drv_ctx->mailbox_add + + sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); + + dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n", + alloc_param.ts); + dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n", + pipe_id, task_id); + + /* allocate device type context */ + sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type, + str_id, alloc_param.operation, 0); + + dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n", + str_id, pipe_id); + ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD, + IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param), + &alloc_param, data, true, true, false, true); + + if (ret < 0) { + dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret); + /* alloc failed, so reset the state to uninit */ + str_info->status = STREAM_UN_INIT; + str_id = ret; + } else if (data) { + response = (struct snd_sst_alloc_response *)data; + ret = response->str_type.result; + if (!ret) + goto out; + dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret); + if (ret == SST_ERR_STREAM_IN_USE) { + dev_err(sst_drv_ctx->dev, + "FW not in clean state, send free for:%d\n", str_id); + sst_free_stream(sst_drv_ctx, str_id); + } + str_id = -ret; + } +out: + kfree(data); + return str_id; +} + +/** +* sst_start_stream - Send msg for a starting stream +* @str_id: stream ID +* +* This function is called by any function which wants to start +* a stream. +*/ +int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) +{ + int retval = 0; + struct stream_info *str_info; + u16 data = 0; + + dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id); + str_info = get_stream_info(sst_drv_ctx, str_id); + if (!str_info) + return -EINVAL; + if (str_info->status != STREAM_RUNNING) + return -EBADRQC; + + retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, + IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id, + sizeof(u16), &data, NULL, true, true, true, false); + + return retval; +} + +int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, + struct snd_sst_bytes_v2 *bytes) +{ struct ipc_post *msg = NULL; + u32 length; + int pvt_id, ret = 0; + struct sst_block *block = NULL; + + dev_dbg(sst_drv_ctx->dev, + "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n", + bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id, + bytes->pipe_id, bytes->len); + + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg, + bytes->task_id, 1, pvt_id); + msg->mrfld_header.p.header_high.part.res_rqd = bytes->block; + length = bytes->len; + msg->mrfld_header.p.header_low_payload = length; + dev_dbg(sst_drv_ctx->dev, "length is %d\n", length); + memcpy(msg->mailbox_data, &bytes->bytes, bytes->len); + if (bytes->block) { + block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id); + if (block == NULL) { + kfree(msg); + ret = -ENOMEM; + goto out; + } + } + + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d", + msg->mrfld_header.p.header_low_payload); + + if (bytes->block) { + ret = sst_wait_timeout(sst_drv_ctx, block); + if (ret) { + dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret); + sst_free_block(sst_drv_ctx, block); + goto out; + } + } + if (bytes->type == SND_SST_BYTES_GET) { + /* + * copy the reply and send back + * we need to update only sz and payload + */ + if (bytes->block) { + unsigned char *r = block->data; + + dev_dbg(sst_drv_ctx->dev, "read back %d bytes", + bytes->len); + memcpy(bytes->bytes, r, bytes->len); + } + } + if (bytes->block) + sst_free_block(sst_drv_ctx, block); +out: + test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id); + return 0; +} + +/* + * sst_pause_stream - Send msg for a pausing stream + * @str_id: stream ID + * + * This function is called by any function which wants to pause + * an already running stream. + */ +int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) +{ + int retval = 0; + struct stream_info *str_info; + + dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id); + str_info = get_stream_info(sst_drv_ctx, str_id); + if (!str_info) + return -EINVAL; + if (str_info->status == STREAM_PAUSED) + return 0; + if (str_info->status == STREAM_RUNNING || + str_info->status == STREAM_INIT) { + if (str_info->prev == STREAM_UN_INIT) + return -EBADRQC; + + retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, + IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id, + 0, NULL, NULL, true, true, false, true); + + if (retval == 0) { + str_info->prev = str_info->status; + str_info->status = STREAM_PAUSED; + } else if (retval == SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + mutex_lock(&sst_drv_ctx->sst_lock); + sst_clean_stream(str_info); + mutex_unlock(&sst_drv_ctx->sst_lock); + } + } else { + retval = -EBADRQC; + dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n "); + } + + return retval; +} + +/** + * sst_resume_stream - Send msg for resuming stream + * @str_id: stream ID + * + * This function is called by any function which wants to resume + * an already paused stream. + */ +int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) +{ + int retval = 0; + struct stream_info *str_info; + + dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id); + str_info = get_stream_info(sst_drv_ctx, str_id); + if (!str_info) + return -EINVAL; + if (str_info->status == STREAM_RUNNING) + return 0; + if (str_info->status == STREAM_PAUSED) { + retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, + IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD, + str_info->pipe_id, 0, NULL, NULL, + true, true, false, true); + + if (!retval) { + if (str_info->prev == STREAM_RUNNING) + str_info->status = STREAM_RUNNING; + else + str_info->status = STREAM_INIT; + str_info->prev = STREAM_PAUSED; + } else if (retval == -SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + mutex_lock(&sst_drv_ctx->sst_lock); + sst_clean_stream(str_info); + mutex_unlock(&sst_drv_ctx->sst_lock); + } + } else { + retval = -EBADRQC; + dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n"); + } + + return retval; +} + + +/** + * sst_drop_stream - Send msg for stopping stream + * @str_id: stream ID + * + * This function is called by any function which wants to stop + * a stream. + */ +int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) +{ + int retval = 0; + struct stream_info *str_info; + + dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id); + str_info = get_stream_info(sst_drv_ctx, str_id); + if (!str_info) + return -EINVAL; + + if (str_info->status != STREAM_UN_INIT) { + str_info->prev = STREAM_UN_INIT; + str_info->status = STREAM_INIT; + str_info->cumm_bytes = 0; + retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, + IPC_CMD, IPC_IA_DROP_STREAM_MRFLD, + str_info->pipe_id, 0, NULL, NULL, + true, true, true, false); + } else { + retval = -EBADRQC; + dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n", + str_info->status); + } + return retval; +} + +/** +* sst_drain_stream - Send msg for draining stream +* @str_id: stream ID +* +* This function is called by any function which wants to drain +* a stream. +*/ +int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx, + int str_id, bool partial_drain) +{ + int retval = 0; + struct stream_info *str_info; + + dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id); + str_info = get_stream_info(sst_drv_ctx, str_id); + if (!str_info) + return -EINVAL; + if (str_info->status != STREAM_RUNNING && + str_info->status != STREAM_INIT && + str_info->status != STREAM_PAUSED) { + dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n", + str_info->status); + return -EBADRQC; + } + + retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, + IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id, + sizeof(u8), &partial_drain, NULL, true, true, false, false); + /* + * with new non blocked drain implementation in core we dont need to + * wait for respsonse, and need to only invoke callback for drain + * complete + */ + + return retval; +} + +/** + * sst_free_stream - Frees a stream + * @str_id: stream ID + * + * This function is called by any function which wants to free + * a stream. + */ +int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) +{ + int retval = 0; + struct stream_info *str_info; + struct intel_sst_ops *ops; + + dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id); + + mutex_lock(&sst_drv_ctx->sst_lock); + if (sst_drv_ctx->sst_state == SST_RESET) { + mutex_unlock(&sst_drv_ctx->sst_lock); + return -ENODEV; + } + mutex_unlock(&sst_drv_ctx->sst_lock); + str_info = get_stream_info(sst_drv_ctx, str_id); + if (!str_info) + return -EINVAL; + ops = sst_drv_ctx->ops; + + mutex_lock(&str_info->lock); + if (str_info->status != STREAM_UN_INIT) { + str_info->prev = str_info->status; + str_info->status = STREAM_UN_INIT; + mutex_unlock(&str_info->lock); + + dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n", + str_id, str_info->pipe_id); + retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, + IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0, + NULL, NULL, true, true, false, true); + + dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n", + retval); + mutex_lock(&sst_drv_ctx->sst_lock); + sst_clean_stream(str_info); + mutex_unlock(&sst_drv_ctx->sst_lock); + dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n"); + } else { + mutex_unlock(&str_info->lock); + retval = -EBADRQC; + dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n"); + } + + return retval; +} -- cgit v0.10.2 From 60dc8dbacb001b6400624ee72990b85d6d44bce6 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 20:00:18 +0530 Subject: ASoC: Intel: sst: Add some helper functions This patch adds helper functions like wait, creating ipc headers, shim wrappers. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c new file mode 100644 index 0000000..9e5f69b --- /dev/null +++ b/sound/soc/intel/sst/sst_pvt.c @@ -0,0 +1,446 @@ +/* + * sst_pvt.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../sst-dsp.h" + +int sst_shim_write(void __iomem *addr, int offset, int value) +{ + writel(value, addr + offset); + return 0; +} + +u32 sst_shim_read(void __iomem *addr, int offset) +{ + return readl(addr + offset); +} + +u64 sst_reg_read64(void __iomem *addr, int offset) +{ + u64 val = 0; + + memcpy_fromio(&val, addr + offset, sizeof(val)); + + return val; +} + +int sst_shim_write64(void __iomem *addr, int offset, u64 value) +{ + memcpy_toio(addr + offset, &value, sizeof(value)); + return 0; +} + +u64 sst_shim_read64(void __iomem *addr, int offset) +{ + u64 val = 0; + + memcpy_fromio(&val, addr + offset, sizeof(val)); + return val; +} + +void sst_set_fw_state_locked( + struct intel_sst_drv *sst_drv_ctx, int sst_state) +{ + mutex_lock(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = sst_state; + mutex_unlock(&sst_drv_ctx->sst_lock); +} + +/* + * sst_wait_interruptible - wait on event + * + * @sst_drv_ctx: Driver context + * @block: Driver block to wait on + * + * This function waits without a timeout (and is interruptable) for a + * given block event + */ +int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block) +{ + int retval = 0; + + if (!wait_event_interruptible(sst_drv_ctx->wait_queue, + block->condition)) { + /* event wake */ + if (block->ret_code < 0) { + dev_err(sst_drv_ctx->dev, + "stream failed %d\n", block->ret_code); + retval = -EBUSY; + } else { + dev_dbg(sst_drv_ctx->dev, "event up\n"); + retval = 0; + } + } else { + dev_err(sst_drv_ctx->dev, "signal interrupted\n"); + retval = -EINTR; + } + return retval; + +} + +unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) +{ + unsigned long long val = 0; + + switch (sst->pci_id) { + case SST_MRFLD_PCI_ID: + val = sst_shim_read64(sst->shim, addr); + break; + } + return val; +} + +void write_shim_data(struct intel_sst_drv *sst, int addr, + unsigned long long data) +{ + switch (sst->pci_id) { + case SST_MRFLD_PCI_ID: + sst_shim_write64(sst->shim, addr, (u64) data); + break; + } +} + +/* + * sst_wait_timeout - wait on event for timeout + * + * @sst_drv_ctx: Driver context + * @block: Driver block to wait on + * + * This function waits with a timeout value (and is not interruptible) on a + * given block event + */ +int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block) +{ + int retval = 0; + + /* + * NOTE: + * Observed that FW processes the alloc msg and replies even + * before the alloc thread has finished execution + */ + dev_dbg(sst_drv_ctx->dev, + "waiting for condition %x ipc %d drv_id %d\n", + block->condition, block->msg_id, block->drv_id); + if (wait_event_timeout(sst_drv_ctx->wait_queue, + block->condition, + msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { + /* event wake */ + dev_dbg(sst_drv_ctx->dev, "Event wake %x\n", + block->condition); + dev_dbg(sst_drv_ctx->dev, "message ret: %d\n", + block->ret_code); + retval = -block->ret_code; + } else { + block->on = false; + dev_err(sst_drv_ctx->dev, + "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n", + block->condition, block->msg_id, sst_drv_ctx->sst_state); + sst_drv_ctx->sst_state = SST_RESET; + + retval = -EBUSY; + } + return retval; +} + +/* + * sst_create_ipc_msg - create a IPC message + * + * @arg: ipc message + * @large: large or short message + * + * this function allocates structures to send a large or short + * message to the firmware + */ +int sst_create_ipc_msg(struct ipc_post **arg, bool large) +{ + struct ipc_post *msg; + + msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); + if (!msg) + return -ENOMEM; + if (large) { + msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); + if (!msg->mailbox_data) { + kfree(msg); + return -ENOMEM; + } + } else { + msg->mailbox_data = NULL; + } + msg->is_large = large; + *arg = msg; + return 0; +} + +/* + * sst_create_block_and_ipc_msg - Creates IPC message and sst block + * @arg: passed to sst_create_ipc_message API + * @large: large or short message + * @sst_drv_ctx: sst driver context + * @block: return block allocated + * @msg_id: IPC + * @drv_id: stream id or private id + */ +int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, + struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, + u32 msg_id, u32 drv_id) +{ + int retval = 0; + + retval = sst_create_ipc_msg(arg, large); + if (retval) + return retval; + *block = sst_create_block(sst_drv_ctx, msg_id, drv_id); + if (*block == NULL) { + kfree(*arg); + return -ENOMEM; + } + return retval; +} + +/* + * sst_clean_stream - clean the stream context + * + * @stream: stream structure + * + * this function resets the stream contexts + * should be called in free + */ +void sst_clean_stream(struct stream_info *stream) +{ + stream->status = STREAM_UN_INIT; + stream->prev = STREAM_UN_INIT; + mutex_lock(&stream->lock); + stream->cumm_bytes = 0; + mutex_unlock(&stream->lock); +} + +int sst_prepare_and_post_msg(struct intel_sst_drv *sst, + int task_id, int ipc_msg, int cmd_id, int pipe_id, + size_t mbox_data_len, const void *mbox_data, void **data, + bool large, bool fill_dsp, bool sync, bool response) +{ + struct ipc_post *msg = NULL; + struct ipc_dsp_hdr dsp_hdr; + struct sst_block *block; + int ret = 0, pvt_id; + + pvt_id = sst_assign_pvt_id(sst); + if (pvt_id < 0) + return pvt_id; + + if (response) + ret = sst_create_block_and_ipc_msg( + &msg, large, sst, &block, ipc_msg, pvt_id); + else + ret = sst_create_ipc_msg(&msg, large); + + if (ret < 0) { + test_and_clear_bit(pvt_id, &sst->pvt_id); + return -ENOMEM; + } + + dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n", + pvt_id, pipe_id, task_id, ipc_msg); + sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg, + task_id, large, pvt_id); + msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len; + msg->mrfld_header.p.header_high.part.res_rqd = !sync; + dev_dbg(sst->dev, "header:%x\n", + msg->mrfld_header.p.header_high.full); + dev_dbg(sst->dev, "response rqd: %x", + msg->mrfld_header.p.header_high.part.res_rqd); + dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d", + msg->mrfld_header.p.header_low_payload); + if (fill_dsp) { + sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + if (mbox_data_len) { + memcpy(msg->mailbox_data + sizeof(dsp_hdr), + mbox_data, mbox_data_len); + } + } + + if (sync) + sst->ops->post_message(sst, msg, true); + else + sst_add_to_dispatch_list_and_post(sst, msg); + + if (response) { + ret = sst_wait_timeout(sst, block); + if (ret < 0) { + goto out; + } else if(block->data) { + if (!data) + goto out; + *data = kzalloc(block->size, GFP_KERNEL); + if (!(*data)) { + ret = -ENOMEM; + goto out; + } else + memcpy(data, (void *) block->data, block->size); + } + } +out: + if (response) + sst_free_block(sst, block); + test_and_clear_bit(pvt_id, &sst->pvt_id); + return ret; +} + +int sst_pm_runtime_put(struct intel_sst_drv *sst_drv) +{ + int ret; + + pm_runtime_mark_last_busy(sst_drv->dev); + ret = pm_runtime_put_autosuspend(sst_drv->dev); + if (ret < 0) + return ret; + return 0; +} + +void sst_fill_header_mrfld(union ipc_header_mrfld *header, + int msg, int task_id, int large, int drv_id) +{ + header->full = 0; + header->p.header_high.part.msg_id = msg; + header->p.header_high.part.task_id = task_id; + header->p.header_high.part.large = large; + header->p.header_high.part.drv_id = drv_id; + header->p.header_high.part.done = 0; + header->p.header_high.part.busy = 1; + header->p.header_high.part.res_rqd = 1; +} + +void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, + int pipe_id, int len) +{ + dsp->cmd_id = msg; + dsp->mod_index_id = 0xff; + dsp->pipe_id = pipe_id; + dsp->length = len; + dsp->mod_id = 0; +} + +#define SST_MAX_BLOCKS 15 +/* + * sst_assign_pvt_id - assign a pvt id for stream + * + * @sst_drv_ctx : driver context + * + * this function assigns a private id for calls that dont have stream + * context yet, should be called with lock held + * uses bits for the id, and finds first free bits and assigns that + */ +int sst_assign_pvt_id(struct intel_sst_drv *drv) +{ + int local; + + spin_lock(&drv->block_lock); + /* find first zero index from lsb */ + local = ffz(drv->pvt_id); + dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); + if (local >= SST_MAX_BLOCKS){ + spin_unlock(&drv->block_lock); + dev_err(drv->dev, "PVT _ID error: no free id blocks "); + return -EINVAL; + } + /* toggle the index */ + change_bit(local, &drv->pvt_id); + spin_unlock(&drv->block_lock); + return local; +} + +void sst_init_stream(struct stream_info *stream, + int codec, int sst_id, int ops, u8 slot) +{ + stream->status = STREAM_INIT; + stream->prev = STREAM_UN_INIT; + stream->ops = ops; +} + +int sst_validate_strid( + struct intel_sst_drv *sst_drv_ctx, int str_id) +{ + if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) { + dev_err(sst_drv_ctx->dev, + "SST ERR: invalid stream id : %d, max %d\n", + str_id, sst_drv_ctx->info.max_streams); + return -EINVAL; + } + + return 0; +} + +struct stream_info *get_stream_info( + struct intel_sst_drv *sst_drv_ctx, int str_id) +{ + if (sst_validate_strid(sst_drv_ctx, str_id)) + return NULL; + return &sst_drv_ctx->streams[str_id]; +} + +int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx, + u32 pipe_id) +{ + int i; + + for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) + if (pipe_id == sst_drv_ctx->streams[i].pipe_id) + return i; + + dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id); + return -1; +} + +u32 relocate_imr_addr_mrfld(u32 base_addr) +{ + /* Get the difference from 512MB aligned base addr */ + /* relocate the base */ + base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024)); + return base_addr; +} + +void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, + struct ipc_post *msg) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); + list_add_tail(&msg->node, &sst->ipc_dispatch_list); + spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); + sst->ops->post_message(sst, NULL, false); +} -- cgit v0.10.2 From 0fbc7d7320202489383f520ecd1758b15a00e17c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Oct 2014 20:00:19 +0530 Subject: ASoC: Intel: sst: Add makefile and kconfig changes Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index f5b4a9c7..c719438 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -3,6 +3,7 @@ config SND_MFLD_MACHINE depends on INTEL_SCU_IPC select SND_SOC_SN95031 select SND_SST_MFLD_PLATFORM + select SND_SST_IPC help This adds support for ASoC machine driver for Intel(R) MID Medfield platform used as alsa device in audio substem in Intel(R) MID devices @@ -12,6 +13,9 @@ config SND_MFLD_MACHINE config SND_SST_MFLD_PLATFORM tristate +config SND_SST_IPC + tristate + config SND_SOC_INTEL_SST tristate "ASoC support for Intel(R) Smart Sound Technology" select SND_SOC_INTEL_SST_ACPI if ACPI diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index f841786..9ab43be 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -31,3 +31,6 @@ obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o + +# DSP driver +obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile new file mode 100644 index 0000000..4d0e79b --- /dev/null +++ b/sound/soc/intel/sst/Makefile @@ -0,0 +1,3 @@ +snd-intel-sst-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o + +obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst.o -- cgit v0.10.2 From 282a331fe25c74c23800bb7da1bb62c9e54fd738 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 16 Oct 2014 18:22:01 -0700 Subject: ASoC: Intel: Add new dependency for Broadwell machine I2C support for the RT286 codec is provided through the Designware I2C platform adapter in this machine. Thus, the adapter driver must be present. Signed-off-by: Ricardo Neri Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index c719438..774fab9 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -65,7 +65,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH config SND_SOC_INTEL_BROADWELL_MACH tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \\ + I2C_DESIGNWARE_PLATFORM select SND_SOC_INTEL_HASWELL select SND_COMPRESS_OFFLOAD select SND_SOC_RT286 -- cgit v0.10.2 From 161aa49ef1b99891b82d3599885eb8d5cbf0ebfc Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 16 Oct 2014 18:22:02 -0700 Subject: ASoC: Intel: Add new dependency for Haswell machine I2C support for the RT5640 codec is provided through the Designware I2C platform adapter in this machine. Thus, the driver must be present. Signed-off-by: Ricardo Neri Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 774fab9..2a3af88 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -36,7 +36,8 @@ config SND_SOC_INTEL_BAYTRAIL config SND_SOC_INTEL_HASWELL_MACH tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\ + I2C_DESIGNWARE_PLATFORM select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 help -- cgit v0.10.2 From cd6e82b814ca73c474b1a2fa48a54b251da44655 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 7 Oct 2014 10:25:37 +0800 Subject: ASoC: rt5645: Add the workqueue of the jack detect function for the debouncing Add the workqueue of the jack detect function for the debouncing. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 3fb83bf..57ba742 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2166,11 +2166,20 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(rt5645_set_jack_detect); +static void rt5645_jack_detect_work(struct work_struct *work) +{ + struct rt5645_priv *rt5645 = + container_of(work, struct rt5645_priv, jack_detect_work.work); + + rt5645_jack_detect(rt5645->codec, rt5645->jack); +} + static irqreturn_t rt5645_irq(int irq, void *data) { struct rt5645_priv *rt5645 = data; - rt5645_jack_detect(rt5645->codec, rt5645->jack); + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, msecs_to_jiffies(250)); return IRQ_HANDLED; } @@ -2436,6 +2445,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n"); } + INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, rt5645_dai, ARRAY_SIZE(rt5645_dai)); } @@ -2447,6 +2458,8 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) if (i2c->irq) free_irq(i2c->irq, rt5645); + cancel_delayed_work_sync(&rt5645->jack_detect_work); + if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) gpio_free(rt5645->pdata.hp_det_gpio); diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 50c62c5..5ec2520 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2168,6 +2168,7 @@ struct rt5645_priv { struct regmap *regmap; struct i2c_client *i2c; struct snd_soc_jack *jack; + struct delayed_work jack_detect_work; int sysclk; int sysclk_src; -- cgit v0.10.2 From 80fff6bf65dcae62255bdb592603dfc247c8cacf Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Fri, 3 Oct 2014 14:37:24 -0700 Subject: ASoC: rt5677: Include gpio driver header The header file is needed because struct gpio_chip is placed in struct rt5677_priv. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index d4eb6d5..99fd023 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -13,6 +13,7 @@ #define __RT5677_H__ #include +#include /* Info */ #define RT5677_RESET 0x00 -- cgit v0.10.2 From 40eb90a18e93fbd4fd0e6892b31241356c3c8e43 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Fri, 10 Oct 2014 20:46:36 -0700 Subject: ASoC: rt5677: Add option to configure gpio as floating/pullup/pulldown gpio_config is array of 6 elements that allows to set GPIO as floating, pullup, pulldown. Sponsored: Google ChromeOS Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt index 0701b83..f82f0e9 100644 --- a/Documentation/devicetree/bindings/sound/rt5677.txt +++ b/Documentation/devicetree/bindings/sound/rt5677.txt @@ -27,6 +27,12 @@ Optional properties: Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential, rather than single-ended. +- realtek,gpio-config + Array of six 8bit elements that configures GPIO. + 0 - floating (reset value) + 1 - pull down + 2 - pull up + Pins on the device (for linking into audio routes): * IN1P @@ -56,4 +62,5 @@ rt5677 { realtek,pow-ldo2-gpio = <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; realtek,in1-differential = "true"; + realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */ }; diff --git a/include/sound/rt5677.h b/include/sound/rt5677.h index 082670e..a56b429 100644 --- a/include/sound/rt5677.h +++ b/include/sound/rt5677.h @@ -27,6 +27,9 @@ struct rt5677_platform_data { bool lout3_diff; /* DMIC2 clock source selection */ enum rt5677_dmic2_clk dmic2_clk_pin; + + /* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */ + u8 gpio_config[6]; }; #endif diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 16aa4d9..a454df3 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -3309,6 +3309,38 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) return 0; } +/** Configures the gpio as + * 0 - floating + * 1 - pull down + * 2 - pull up + */ +static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset, + int value) +{ + int shift; + + switch (offset) { + case RT5677_GPIO1 ... RT5677_GPIO2: + shift = 2 * (1 - offset); + regmap_update_bits(rt5677->regmap, + RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL2, + 0x3 << shift, + (value & 0x3) << shift); + break; + + case RT5677_GPIO3 ... RT5677_GPIO6: + shift = 2 * (9 - offset); + regmap_update_bits(rt5677->regmap, + RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL3, + 0x3 << shift, + (value & 0x3) << shift); + break; + + default: + break; + } +} + static struct gpio_chip rt5677_template_chip = { .label = "rt5677", .owner = THIS_MODULE, @@ -3353,6 +3385,7 @@ static void rt5677_free_gpio(struct i2c_client *i2c) static int rt5677_probe(struct snd_soc_codec *codec) { struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + int i; rt5677->codec = codec; @@ -3371,6 +3404,9 @@ static int rt5677_probe(struct snd_soc_codec *codec) regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020); regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00); + for (i = 0; i < RT5677_GPIO_NUM; i++) + rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]); + return 0; } @@ -3590,6 +3626,9 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) (rt5677->pow_ldo2 != -ENOENT)) return rt5677->pow_ldo2; + of_property_read_u8_array(np, "realtek,gpio-config", + rt5677->pdata.gpio_config, RT5677_GPIO_NUM); + return 0; } -- cgit v0.10.2 From af48f1d08a5474184da9aaf8b77f4a2848b1875e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 6 Oct 2014 16:30:51 +0800 Subject: ASoC: rt5677: Support DSP function for VAD application The ALC5677 has a programmable DSP, and there is a SPI that is dadicated for DSP firmware loading. Therefore, the patch includes a SPI driver for writing the DSP firmware. The VAD(Voice Activaty Detection) has be implemented and use the DSP to recognize the key phase. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5dce451..4435f9f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -79,7 +79,7 @@ snd-soc-rt5640-objs := rt5640.o snd-soc-rt5645-objs := rt5645.o snd-soc-rt5651-objs := rt5651.o snd-soc-rt5670-objs := rt5670.o -snd-soc-rt5677-objs := rt5677.o +snd-soc-rt5677-objs := rt5677.o rt5677-spi.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c new file mode 100644 index 0000000..11c38f3 --- /dev/null +++ b/sound/soc/codecs/rt5677-spi.c @@ -0,0 +1,128 @@ +/* + * rt5677-spi.c -- RT5677 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5677-spi.h" + +static struct spi_device *g_spi; + +/** + * rt5677_spi_write - Write data to SPI. + * @txbuf: Data Buffer for writing. + * @len: Data length. + * + * + * Returns true for success. + */ +int rt5677_spi_write(u8 *txbuf, size_t len) +{ + int status; + + status = spi_write(g_spi, txbuf, len); + + if (status) + dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status); + + return status; +} + +/** + * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address. + * @addr: Start address. + * @txbuf: Data Buffer for writng. + * @len: Data length, it must be a multiple of 8. + * + * + * Returns true for success. + */ +int rt5677_spi_burst_write(u32 addr, const struct firmware *fw) +{ + u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE; + u8 *write_buf; + unsigned int i, end, offset = 0; + + write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL); + + if (write_buf == NULL) + return -ENOMEM; + + while (offset < fw->size) { + if (offset + RT5677_SPI_BUF_LEN <= fw->size) + end = RT5677_SPI_BUF_LEN; + else + end = fw->size % RT5677_SPI_BUF_LEN; + + write_buf[0] = spi_cmd; + write_buf[1] = ((addr + offset) & 0xff000000) >> 24; + write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; + write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; + write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; + + for (i = 0; i < end; i += 8) { + write_buf[i + 12] = fw->data[offset + i + 0]; + write_buf[i + 11] = fw->data[offset + i + 1]; + write_buf[i + 10] = fw->data[offset + i + 2]; + write_buf[i + 9] = fw->data[offset + i + 3]; + write_buf[i + 8] = fw->data[offset + i + 4]; + write_buf[i + 7] = fw->data[offset + i + 5]; + write_buf[i + 6] = fw->data[offset + i + 6]; + write_buf[i + 5] = fw->data[offset + i + 7]; + } + + write_buf[end + 5] = spi_cmd; + + rt5677_spi_write(write_buf, end + 6); + + offset += RT5677_SPI_BUF_LEN; + } + + kfree(write_buf); + + return 0; +} + +static int rt5677_spi_probe(struct spi_device *spi) +{ + g_spi = spi; + return 0; +} + +static struct spi_driver rt5677_spi_driver = { + .driver = { + .name = "rt5677", + .owner = THIS_MODULE, + }, + .probe = rt5677_spi_probe, +}; +module_spi_driver(rt5677_spi_driver); + +MODULE_DESCRIPTION("ASoC RT5677 SPI driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h new file mode 100644 index 0000000..7528bfd --- /dev/null +++ b/sound/soc/codecs/rt5677-spi.h @@ -0,0 +1,21 @@ +/* + * rt5677-spi.h -- RT5677 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou + * + * 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 __RT5671_SPI_H__ +#define __RT5671_SPI_H__ + +#define RT5677_SPI_BUF_LEN 240 +#define RT5677_SPI_CMD_BURST_WRITE 0x05 + +int rt5677_spi_write(u8 *txbuf, size_t len); +int rt5677_spi_burst_write(u32 addr, const struct firmware *fw); + +#endif /* __RT5677_SPI_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index a454df3..e6e54fa 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include "rl6231.h" #include "rt5677.h" +#include "rt5677-spi.h" #define RT5677_DEVICE_ID 0x6327 @@ -537,6 +539,243 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg) } } +/** + * rt5677_dsp_mode_i2c_write_addr - Write value to address on DSP mode. + * @codec: SoC audio codec device. + * @addr: Address index. + * @value: Address data. + * + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_write_addr(struct snd_soc_codec *codec, + unsigned int addr, unsigned int value, unsigned int opcode) +{ + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_DATA_MSB, + value >> 16); + if (ret < 0) { + dev_err(codec->dev, "Failed to set data msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_DATA_LSB, + value & 0xffff); + if (ret < 0) { + dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_OP_CODE, opcode); + if (ret < 0) { + dev_err(codec->dev, "Failed to set op code value: %d\n", ret); + goto err; + } + +err: + mutex_unlock(&rt5677->dsp_cmd_lock); + + return ret; +} + +/** + * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode. + * @codec: SoC audio codec device. + * @addr: Address index. + * @value: Address data. + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_read_addr( + struct snd_soc_codec *codec, unsigned int addr, unsigned int *value) +{ + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + int ret; + unsigned int msb, lsb; + + mutex_lock(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_OP_CODE , 0x0002); + if (ret < 0) { + dev_err(codec->dev, "Failed to set op code value: %d\n", ret); + goto err; + } + + regmap_read(rt5677->regmap, RT5677_DSP_I2C_DATA_MSB, &msb); + regmap_read(rt5677->regmap, RT5677_DSP_I2C_DATA_LSB, &lsb); + *value = (msb << 16) | lsb; + +err: + mutex_unlock(&rt5677->dsp_cmd_lock); + + return ret; +} + +/** + * rt5677_dsp_mode_i2c_write - Write register on DSP mode. + * @codec: SoC audio codec device. + * @reg: Register index. + * @value: Register data. + * + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + return rt5677_dsp_mode_i2c_write_addr(codec, 0x18020000 + reg * 2, + value, 0x0001); +} + +/** + * rt5677_dsp_mode_i2c_read - Read register on DSP mode. + * @codec: SoC audio codec device. + * @reg: Register index. + * + * + * Returns Register value. + */ +static unsigned int rt5677_dsp_mode_i2c_read( + struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int value = 0; + + rt5677_dsp_mode_i2c_read_addr(codec, 0x18020000 + reg * 2, &value); + + return value; +} + +/** + * rt5677_dsp_mode_i2c_update_bits - update register on DSP mode. + * @codec: audio codec + * @reg: register index. + * @mask: register mask + * @value: new value + * + * + * Returns 1 for change, 0 for no change, or negative error code. + */ +static int rt5677_dsp_mode_i2c_update_bits(struct snd_soc_codec *codec, + unsigned int reg, unsigned int mask, unsigned int value) +{ + unsigned int old, new; + int change, ret; + + ret = rt5677_dsp_mode_i2c_read(codec, reg); + if (ret < 0) { + dev_err(codec->dev, "Failed to read reg: %d\n", ret); + goto err; + } + + old = ret; + new = (old & ~mask) | (value & mask); + change = old != new; + if (change) { + ret = rt5677_dsp_mode_i2c_write(codec, reg, new); + if (ret < 0) { + dev_err(codec->dev, + "Failed to write reg: %d\n", ret); + goto err; + } + } + return change; + +err: + return ret; +} + +static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) +{ + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + static bool activity; + int ret; + + if (on && !activity) { + activity = true; + + regcache_cache_only(rt5677->regmap, false); + regcache_cache_bypass(rt5677->regmap, true); + + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1); + regmap_update_bits(rt5677->regmap, + RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_LDO1_SEL_MASK, 0x0); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_LDO1, RT5677_PWR_LDO1); + regmap_write(rt5677->regmap, RT5677_GLB_CLK2, 0x0080); + regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff); + regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07ff); + + ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1, + codec->dev); + if (ret == 0) { + rt5677_spi_burst_write(0x50000000, rt5677->fw1); + release_firmware(rt5677->fw1); + } + + ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2, + codec->dev); + if (ret == 0) { + rt5677_spi_burst_write(0x60000000, rt5677->fw2); + release_firmware(rt5677->fw2); + } + + rt5677_dsp_mode_i2c_update_bits(codec, RT5677_PWR_DSP1, 0x1, + 0x0); + + regcache_cache_bypass(rt5677->regmap, false); + regcache_cache_only(rt5677->regmap, true); + } else if (!on && activity) { + activity = false; + + regcache_cache_only(rt5677->regmap, false); + regcache_cache_bypass(rt5677->regmap, true); + + rt5677_dsp_mode_i2c_update_bits(codec, RT5677_PWR_DSP1, 0x1, + 0x1); + rt5677_dsp_mode_i2c_write(codec, RT5677_PWR_DSP1, 0x0001); + + regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); + + regcache_cache_bypass(rt5677->regmap, false); + regcache_mark_dirty(rt5677->regmap); + regcache_sync(rt5677->regmap); + } + + return 0; +} + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); @@ -556,6 +795,31 @@ static unsigned int bst_tlv[] = { 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), }; +static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = rt5677->dsp_vad_en; + + return 0; +} + +static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0]; + + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en); + + return 0; +} + static const struct snd_kcontrol_new rt5677_snd_controls[] = { /* OUTPUT Control */ SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1, @@ -627,6 +891,9 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = { SOC_DOUBLE_TLV("Mono ADC Boost Volume", RT5677_ADC_BST_CTRL2, RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0, adc_bst_tlv), + + SOC_SINGLE_EXT("DSP VAD Switch", SND_SOC_NOPM, 0, 1, 0, + rt5677_dsp_vad_get, rt5677_dsp_vad_put), }; /** @@ -3181,6 +3448,8 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + rt5677_set_dsp_vad(codec, false); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK, 0x0055); @@ -3214,6 +3483,9 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000); regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000); + + if (rt5677->dsp_vad_en) + rt5677_set_dsp_vad(codec, true); break; default: @@ -3407,6 +3679,8 @@ static int rt5677_probe(struct snd_soc_codec *codec) for (i = 0; i < RT5677_GPIO_NUM; i++) rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]); + mutex_init(&rt5677->dsp_cmd_lock); + return 0; } @@ -3426,8 +3700,11 @@ static int rt5677_suspend(struct snd_soc_codec *codec) { struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); - regcache_cache_only(rt5677->regmap, true); - regcache_mark_dirty(rt5677->regmap); + if (!rt5677->dsp_vad_en) { + regcache_cache_only(rt5677->regmap, true); + regcache_mark_dirty(rt5677->regmap); + } + if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 0); @@ -3442,8 +3719,11 @@ static int rt5677_resume(struct snd_soc_codec *codec) gpio_set_value_cansleep(rt5677->pow_ldo2, 1); msleep(10); } - regcache_cache_only(rt5677->regmap, false); - regcache_sync(rt5677->regmap); + + if (!rt5677->dsp_vad_en) { + regcache_cache_only(rt5677->regmap, false); + regcache_sync(rt5677->regmap); + } return 0; } diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 99fd023..20efa4a 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1507,6 +1507,9 @@ #define RT5677_GPIO5_FUNC_GPIO (0x0 << 9) #define RT5677_GPIO5_FUNC_DMIC (0x1 << 9) +#define RT5677_FIRMWARE1 "rt5677_dsp_fw1.bin" +#define RT5677_FIRMWARE2 "rt5677_dsp_fw2.bin" + /* System Clock Source */ enum { RT5677_SCLK_S_MCLK, @@ -1546,6 +1549,8 @@ struct rt5677_priv { struct snd_soc_codec *codec; struct rt5677_platform_data pdata; struct regmap *regmap; + const struct firmware *fw1, *fw2; + struct mutex dsp_cmd_lock; int sysclk; int sysclk_src; @@ -1559,6 +1564,7 @@ struct rt5677_priv { #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; #endif + bool dsp_vad_en; }; #endif /* __RT5677_H__ */ -- cgit v0.10.2 From 8a4bd60af4cbdfbdaab6dec6ab86471733197a4f Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Wed, 15 Oct 2014 13:55:32 -0700 Subject: ASoC: rt5677: Print more information if setting DAI clock failed Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index e6e54fa..caada91 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -3104,7 +3104,8 @@ static int rt5677_hw_params(struct snd_pcm_substream *substream, rt5677->lrck[dai->id] = params_rate(params); pre_div = rl6231_get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]); if (pre_div < 0) { - dev_err(codec->dev, "Unsupported clock setting\n"); + dev_err(codec->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n", + rt5677->sysclk, rt5677->lrck[dai->id]); return -EINVAL; } frame_size = snd_soc_params_to_frame_size(params); -- cgit v0.10.2 From 45b6e1d300b034678c42369aad3b27d37854d1fb Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Thu, 16 Oct 2014 09:40:58 -0700 Subject: ASoC: rt5677: fix build when kernel compiled without GPIOLIB support Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index caada91..d17d079 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -3646,6 +3646,11 @@ static void rt5677_free_gpio(struct i2c_client *i2c) gpiochip_remove(&rt5677->gpio_chip); } #else +static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset, + int value) +{ +} + static void rt5677_init_gpio(struct i2c_client *i2c) { } -- cgit v0.10.2 From ac884fc47b7750b1e7eaf04f0236610c84ceee54 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Fri, 17 Oct 2014 11:56:42 -0700 Subject: ASoC: rt5677: add build dependency to spi Since 9cb715a9d4c the codec has a hardcoded dependency to spi. Add this dependency to Kconfig. It fixes buildbot compilation failure: sound/built-in.o: In function `spi_write': >> rt5677-spi.c:(.text+0x8265f): undefined reference to `spi_sync' sound/built-in.o: In function `rt5677_spi_driver_init': >> rt5677-spi.c:(.init.text+0x17db): undefined reference to `spi_register_driver' Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..2c7482e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -85,7 +85,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5645 if I2C select SND_SOC_RT5651 if I2C select SND_SOC_RT5670 if I2C - select SND_SOC_RT5677 if I2C + select SND_SOC_RT5677 if I2C && SPI_MASTER select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SIRF_AUDIO_CODEC -- cgit v0.10.2 From 7f6d75d77683c8f9c18836a2fea2a1e76efc3a9a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 Oct 2014 10:50:56 -0300 Subject: ASoC: sgtl5000: Cleanup the comments Fix grammar and typos. Besides that, also fix the comment about the range of SYS_MCLK, which is from 8 to 27 MHz. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 6bb77d7..10160e7 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -530,16 +530,16 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai, /* * set clock according to i2s frame clock, - * sgtl5000 provide 2 clock sources. - * 1. sys_mclk. sample freq can only configure to + * sgtl5000 provides 2 clock sources: + * 1. sys_mclk: sample freq can only be configured to * 1/256, 1/384, 1/512 of sys_mclk. - * 2. pll. can derive any audio clocks. + * 2. pll: can derive any audio clocks. * * clock setting rules: - * 1. in slave mode, only sys_mclk can use. - * 2. as constraint by sys_mclk, sample freq should - * set to 32k, 44.1k and above. - * 3. using sys_mclk prefer to pll to save power. + * 1. in slave mode, only sys_mclk can be used + * 2. as constraint by sys_mclk, sample freq should be set to 32 kHz, 44.1 kHz + * and above. + * 3. usage of sys_mclk is preferred over pll to save power. */ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) { @@ -549,8 +549,8 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) /* * sample freq should be divided by frame clock, - * if frame clock lower than 44.1khz, sample feq should set to - * 32khz or 44.1khz. + * if frame clock is lower than 44.1 kHz, sample freq should be set to + * 32 kHz or 44.1 kHz. */ switch (frame_rate) { case 8000: @@ -603,7 +603,8 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) /* * calculate the divider of mclk/sample_freq, - * factor of freq =96k can only be 256, since mclk in range (12m,27m) + * factor of freq = 96 kHz can only be 256, since mclk is in the range + * of 8 MHz - 27 MHz */ switch (sgtl5000->sysclk / sys_fs) { case 256: @@ -619,7 +620,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) SGTL5000_MCLK_FREQ_SHIFT; break; default: - /* if mclk not satisify the divider, use pll */ + /* if mclk does not satisfy the divider, use pll */ if (sgtl5000->master) { clk_ctl |= SGTL5000_MCLK_FREQ_PLL << SGTL5000_MCLK_FREQ_SHIFT; @@ -795,7 +796,7 @@ static int ldo_regulator_enable(struct regulator_dev *dev) SGTL5000_LINEREG_D_POWERUP, SGTL5000_LINEREG_D_POWERUP); - /* when internal ldo enabled, simple digital power can be disabled */ + /* when internal ldo is enabled, simple digital power can be disabled */ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_LINREG_SIMPLE_POWERUP, 0); @@ -1079,7 +1080,7 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg) /* * sgtl5000 has 3 internal power supplies: * 1. VAG, normally set to vdda/2 - * 2. chargepump, set to different value + * 2. charge pump, set to different value * according to voltage of vdda and vddio * 3. line out VAG, normally set to vddio/2 * -- cgit v0.10.2 From bd0593f5f6add279257334b4a76aecd3ee8d31dc Mon Sep 17 00:00:00 2001 From: Jean-Michel Hautbois Date: Tue, 14 Oct 2014 08:43:11 +0200 Subject: ASoC: sgtl5000: Add MicBias resistor support in DT Some systems may require a different resistor than the default one (4K). This adds a property in sgtl5000 codec. It keeps the default of 4K when nothing is specified so it does not break existing code. Signed-off-by: Jean-Michel Hautbois Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt index 955df60..d6ec927 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -7,10 +7,18 @@ Required properties: - clocks : the clock provider of SYS_MCLK +- micbias-resistor-k-ohms : the bias resistor to be used in kOmhs + The resistor can take values of 2k, 4k or 8k. + If set to 0 it will be off. + If this node is not mentioned or if the value is unknown, then + micbias resistor is set to 4K. + + Example: codec: sgtl5000@0a { compatible = "fsl,sgtl5000"; reg = <0x0a>; clocks = <&clks 150>; + sgtl5000-micbias-resistor = <1>; }; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 10160e7..c417b4a 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,13 @@ struct ldo_regulator { bool enabled; }; +enum sgtl5000_micbias_resistor { + SGTL5000_MICBIAS_OFF = 0, + SGTL5000_MICBIAS_2K = 2, + SGTL5000_MICBIAS_4K = 4, + SGTL5000_MICBIAS_8K = 8, +}; + /* sgtl5000 private structure in codec */ struct sgtl5000_priv { int sysclk; /* sysclk rate */ @@ -131,6 +139,7 @@ struct sgtl5000_priv { struct regmap *regmap; struct clk *mclk; int revision; + u8 micbias_resistor; }; /* @@ -145,12 +154,14 @@ struct sgtl5000_priv { static int mic_bias_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(w->codec); + switch (event) { case SND_SOC_DAPM_POST_PMU: - /* change mic bias resistor to 4Kohm */ + /* change mic bias resistor */ snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL, - SGTL5000_BIAS_R_MASK, - SGTL5000_BIAS_R_4k << SGTL5000_BIAS_R_SHIFT); + SGTL5000_BIAS_R_MASK, + sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); break; case SND_SOC_DAPM_PRE_PMD: @@ -1327,7 +1338,9 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) SGTL5000_HP_ZCD_EN | SGTL5000_ADC_ZCD_EN); - snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 2); + snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_MASK, + sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); /* * disable DAP @@ -1419,6 +1432,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, struct sgtl5000_priv *sgtl5000; int ret, reg, rev; unsigned int mclk; + struct device_node *np = client->dev.of_node; + u32 value; sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), GFP_KERNEL); @@ -1471,6 +1486,38 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev); sgtl5000->revision = rev; + if (np) { + if (!of_property_read_u32(np, + "micbias-resistor-k-ohms", &value)) { + switch (value) { + case SGTL5000_MICBIAS_OFF: + sgtl5000->micbias_resistor = 0; + break; + case SGTL5000_MICBIAS_2K: + sgtl5000->micbias_resistor = 1; + break; + case SGTL5000_MICBIAS_4K: + sgtl5000->micbias_resistor = 2; + break; + case SGTL5000_MICBIAS_8K: + sgtl5000->micbias_resistor = 3; + break; + default: + sgtl5000->micbias_resistor = 2; + dev_err(&client->dev, + "Unsuitable MicBias resistor\n"); + } + } else { + /* default is 4Kohms */ + sgtl5000->micbias_resistor = 2; + } + dev_err(&client->dev, + "Unsuitable MicBias resistor\n"); + } + } else { + } + } + i2c_set_clientdata(client, sgtl5000); /* Ensure sgtl5000 will start with sane register values */ -- cgit v0.10.2 From 8735779774b8bbe14456c9e6ba4525eefc67a228 Mon Sep 17 00:00:00 2001 From: Jean-Michel Hautbois Date: Tue, 14 Oct 2014 08:43:12 +0200 Subject: ASoC: sgtl5000: Add MicBias voltage support Some systems may require to specify a bias different than default (1.25V). This adds a property in sgtl5000 codec. The property is specified in milli-volts so that it is coherent with datasheet. Signed-off-by: Jean-Michel Hautbois Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt index d6ec927..1aab403 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -13,6 +13,10 @@ Required properties: If this node is not mentioned or if the value is unknown, then micbias resistor is set to 4K. +- micbias-voltage-m-volts : the bias voltage to be used in mVolts + The voltage can take values from 1.25V to 3V by 250mV steps + If this node is not mentionned or the value is unknown, then + the value is set to 1.25V. Example: @@ -20,5 +24,6 @@ codec: sgtl5000@0a { compatible = "fsl,sgtl5000"; reg = <0x0a>; clocks = <&clks 150>; - sgtl5000-micbias-resistor = <1>; + micbias-resistor-k-ohms = <2>; + micbias-voltage-m-volts = <2250>; }; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index c417b4a..59336f6 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -140,6 +140,7 @@ struct sgtl5000_priv { struct clk *mclk; int revision; u8 micbias_resistor; + u8 micbias_voltage; }; /* @@ -1342,6 +1343,9 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) SGTL5000_BIAS_R_MASK, sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); + snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_MASK, + sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT); /* * disable DAP * TODO: @@ -1511,10 +1515,19 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, /* default is 4Kohms */ sgtl5000->micbias_resistor = 2; } + if (!of_property_read_u32(np, + "micbias-voltage-m-volts", &value)) { + /* 1250mV => 0 */ + /* steps of 250mV */ + if ((value >= 1250) && (value <= 3000)) + sgtl5000->micbias_voltage = (value / 250) - 5; + else { + sgtl5000->micbias_voltage = 0; dev_err(&client->dev, "Unsuitable MicBias resistor\n"); } } else { + sgtl5000->micbias_voltage = 0; } } -- cgit v0.10.2 From 84d4cbe9a60a1fdd35b4dd69951f31c518b467d8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 Oct 2014 15:29:58 -0300 Subject: ASoC: simple-card: Delete unneeded test before of_node_put of_node_put() supports NULL as its argument, so the initial test is not necessary. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index d1b7293..4f192ee 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -368,12 +368,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_link->cpu_dai_name = NULL; dai_link_of_err: - if (np) - of_node_put(np); - if (bitclkmaster) - of_node_put(bitclkmaster); - if (framemaster) - of_node_put(framemaster); + of_node_put(np); + of_node_put(bitclkmaster); + of_node_put(framemaster); return ret; } @@ -464,11 +461,9 @@ static int asoc_simple_card_unref(struct platform_device *pdev) num_links < card->num_links; num_links++, dai_link++) { np = (struct device_node *) dai_link->cpu_of_node; - if (np) - of_node_put(np); + of_node_put(np); np = (struct device_node *) dai_link->codec_of_node; - if (np) - of_node_put(np); + of_node_put(np); } return 0; } -- cgit v0.10.2 From 469cda294dac1c4128e9adcb0a94636bc0cb280e Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Tue, 21 Oct 2014 17:33:29 +0200 Subject: ASoC: tegra: Read and use the GPIO flags of the headphone detect The headphone detect was hardcoded to low-active, use the flags from DT to allow high-active as well. Signed-off-by: Alban Bedel Acked-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index a689883..4ebe387 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -44,6 +44,7 @@ struct tegra_rt5640 { struct tegra_asoc_utils_data util_data; int gpio_hp_det; + enum of_gpio_flags gpio_hp_det_flags; }; static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream, @@ -119,6 +120,8 @@ static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd) if (gpio_is_valid(machine->gpio_hp_det)) { tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det; + tegra_rt5640_hp_jack_gpio.invert = + !!(machine->gpio_hp_det_flags & OF_GPIO_ACTIVE_LOW); snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack, 1, &tegra_rt5640_hp_jack_gpio); @@ -180,7 +183,8 @@ static int tegra_rt5640_probe(struct platform_device *pdev) 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); + machine->gpio_hp_det = of_get_named_gpio_flags( + np, "nvidia,hp-det-gpios", 0, &machine->gpio_hp_det_flags); if (machine->gpio_hp_det == -EPROBE_DEFER) return -EPROBE_DEFER; -- cgit v0.10.2 From 98ad73c995ed4886c36a1fcfcda53fbff484f666 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Oct 2014 17:01:15 +0200 Subject: ASoC: dapm: Remove redundant cast Both path->name and e->texts[i] have type const char*, so the cast is slightly confusing and certainly unnecessary. Signed-off-by: Rasmus Villemoes Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c61cb9c..39f992b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -496,7 +496,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); - path->name = (char*)e->texts[i]; + path->name = e->texts[i]; if (i == item) path->connect = 1; else -- cgit v0.10.2 From b3baaa47cc49fab3ecffbbaee660ce003d17d1f7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 20 Oct 2014 20:54:30 +0530 Subject: ASoC: intel: use __iowrite32_copy for 32 bit copy The driver was using own method to do 32bit copy, turns out we have a kernel API so use that instead Tested-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c index b6d27c1..00f60c1 100644 --- a/sound/soc/intel/sst/sst_loader.c +++ b/sound/soc/intel/sst/sst_loader.c @@ -39,14 +39,12 @@ #include "sst.h" #include "../sst-dsp.h" -static void memcpy32_toio(void __iomem *dst, const void *src, int count) +static inline void memcpy32_toio(void __iomem *dst, const void *src, int count) { - int i; - const u32 *src_32 = src; - u32 *dst_32 = dst; - - for (i = 0; i < count/sizeof(u32); i++) - writel(*src_32++, dst_32++); + /* __iowrite32_copy uses 32-bit count values so divide by 4 for + * right count in words + */ + __iowrite32_copy(dst, src, count/4); } /** -- cgit v0.10.2 From 790b4075b3a6845543d02ab29c81dc450e7b6794 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 20 Oct 2014 20:54:31 +0530 Subject: ASoC: intel: log an error on double free the stream context should be freed only once on stream cleanup. If we ever hit a chance that stream context is getting double freed, though not an cause of panic as memory allocator can deal with this, we should still log this to help in finding issues and debugging Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c index aadb0db..3a5e920 100644 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ b/sound/soc/intel/sst/sst_drv_interface.c @@ -55,6 +55,8 @@ int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id) if (ret) sst_clean_stream(&ctx->streams[str_id]); return ret; + } else { + dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id); } return ret; } -- cgit v0.10.2 From dee2ce696ea8b37a26eef9f2a3fcc64df7179d98 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 20 Oct 2014 20:54:32 +0530 Subject: ASoC: intel: fix the kernldoc comment copypaste error on function sst_get_num_channel caused the comment to be wrong, so fix it here Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c index 3a5e920..183b1eb 100644 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ b/sound/soc/intel/sst/sst_drv_interface.c @@ -94,7 +94,7 @@ int sst_get_sfreq(struct snd_sst_params *str_param) } /* - * sst_get_sfreq - this function returns the frequency of the stream + * sst_get_num_channel - get number of channels for the stream * * @str_param : stream params */ -- cgit v0.10.2 From 33c1256f1ce30a94f4b590bb30baf787e17f64aa Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 20 Oct 2014 20:54:33 +0530 Subject: ASoC: intel: explain why block not found isn't error always The IPC blocking can be error when we don't find block or a short message, explain that by adding a comment about this scenario Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c index 41a2b41..2126f5b 100644 --- a/sound/soc/intel/sst/sst_ipc.c +++ b/sound/soc/intel/sst/sst_ipc.c @@ -54,6 +54,21 @@ struct sst_block *sst_create_block(struct intel_sst_drv *ctx, return msg; } +/* + * while handling the interrupts, we need to check for message status and + * then if we are blocking for a message + * + * here we are unblocking the blocked ones, this is based on id we have + * passed and search that for block threads. + * We will not find block in two cases + * a) when its small message and block in not there, so silently ignore + * them + * b) when we are actually not able to find the block (bug perhaps) + * + * Since we have bit of small messages we can spam kernel log with err + * print on above so need to keep as debug prints which should be enabled + * via dynamic debug while debugging IPC issues + */ int sst_wake_up_block(struct intel_sst_drv *ctx, int result, u32 drv_id, u32 ipc, void *data, u32 size) { -- cgit v0.10.2 From 7f26680170e322730c7c7553f5625fb04de4f5b8 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 20 Oct 2014 20:54:34 +0530 Subject: ASoC: intel: use __iowrite32_copy for 32 bit copy The sst-firmware was also using own method to do 32bit copy, turns out we have a kernel API so use that instead [For BYT] Tested-by: Jarkko Nikula Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index 3bb43da..cf3d199 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -32,13 +32,10 @@ static void block_module_remove(struct sst_module *module); -static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) +static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) { - u32 i; - - /* copy one 32 bit word at a time as 64 bit access is not supported */ - for (i = 0; i < bytes; i += 4) - memcpy_toio(dest + i, src + i, 4); + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, bytes/4); } /* create new generic firmware object */ -- cgit v0.10.2 From 78cb4d995b932d9014342ddc81ce0a5cc434ed98 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Oct 2014 12:14:56 +0200 Subject: ASoC: core: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a2..96ecdc3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2348,16 +2348,8 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, { struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = e->shift_l == e->shift_r ? 1 : 2; - uinfo->value.enumerated.items = e->items; - - if (uinfo->value.enumerated.item >= e->items) - uinfo->value.enumerated.item = e->items - 1; - strlcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item], - sizeof(uinfo->value.enumerated.name)); - return 0; + return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2, + e->items, e->texts); } EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); -- cgit v0.10.2 From 9313484238ca49fe5c7513dfcb36aaddcea8c298 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:28 +0200 Subject: ASoC: ak4535: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 30e2978..eced46d 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -373,36 +373,19 @@ static struct snd_soc_dai_driver ak4535_dai = { .ops = &ak4535_dai_ops, }; -static int ak4535_suspend(struct snd_soc_codec *codec) -{ - ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int ak4535_resume(struct snd_soc_codec *codec) { snd_soc_cache_sync(codec); - ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } static int ak4535_probe(struct snd_soc_codec *codec) { - /* power on device */ - ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_codec_controls(codec, ak4535_snd_controls, ARRAY_SIZE(ak4535_snd_controls)); return 0; } -/* power down chip */ -static int ak4535_remove(struct snd_soc_codec *codec) -{ - ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static const struct regmap_config ak4535_regmap = { .reg_bits = 8, .val_bits = 8, @@ -417,10 +400,10 @@ static const struct regmap_config ak4535_regmap = { static struct snd_soc_codec_driver soc_codec_dev_ak4535 = { .probe = ak4535_probe, - .remove = ak4535_remove, - .suspend = ak4535_suspend, .resume = ak4535_resume, .set_bias_level = ak4535_set_bias_level, + .suspend_bias_off = true, + .dapm_widgets = ak4535_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), .dapm_routes = ak4535_audio_map, -- cgit v0.10.2 From 4caab4194a99e58c08c70e7df846b9bda948f353 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:29 +0200 Subject: ASoC: ak4535: Use table based setup for controls Makes the code slightly shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index eced46d..9130d91 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -379,13 +379,6 @@ static int ak4535_resume(struct snd_soc_codec *codec) return 0; } -static int ak4535_probe(struct snd_soc_codec *codec) -{ - snd_soc_add_codec_controls(codec, ak4535_snd_controls, - ARRAY_SIZE(ak4535_snd_controls)); - return 0; -} - static const struct regmap_config ak4535_regmap = { .reg_bits = 8, .val_bits = 8, @@ -399,11 +392,12 @@ static const struct regmap_config ak4535_regmap = { }; static struct snd_soc_codec_driver soc_codec_dev_ak4535 = { - .probe = ak4535_probe, .resume = ak4535_resume, .set_bias_level = ak4535_set_bias_level, .suspend_bias_off = true, + .controls = ak4535_snd_controls, + .num_controls = ARRAY_SIZE(ak4535_snd_controls), .dapm_widgets = ak4535_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), .dapm_routes = ak4535_audio_map, -- cgit v0.10.2 From 0b0171e3ad22b5a3be01bbafddede4ebea1769bd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:30 +0200 Subject: ASoC: ak4641: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 7afe8f4..70861c7 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -505,39 +505,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = { }, }; -static int ak4641_suspend(struct snd_soc_codec *codec) -{ - ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int ak4641_resume(struct snd_soc_codec *codec) -{ - ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - -static int ak4641_probe(struct snd_soc_codec *codec) -{ - /* power on device */ - ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int ak4641_remove(struct snd_soc_codec *codec) -{ - ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - - static struct snd_soc_codec_driver soc_codec_dev_ak4641 = { - .probe = ak4641_probe, - .remove = ak4641_remove, - .suspend = ak4641_suspend, - .resume = ak4641_resume, .controls = ak4641_snd_controls, .num_controls = ARRAY_SIZE(ak4641_snd_controls), .dapm_widgets = ak4641_dapm_widgets, @@ -545,6 +513,7 @@ 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, + .suspend_bias_off = true, }; static const struct regmap_config ak4641_regmap = { -- cgit v0.10.2 From 61ce9ee3aad2fc7a505a420957e8205c4050db69 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:31 +0200 Subject: ASoC: ak4642: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 0417125..dde8b49 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -491,23 +491,7 @@ static int ak4642_resume(struct snd_soc_codec *codec) return 0; } - -static int ak4642_probe(struct snd_soc_codec *codec) -{ - ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int ak4642_remove(struct snd_soc_codec *codec) -{ - ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { - .probe = ak4642_probe, - .remove = ak4642_remove, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, .controls = ak4642_snd_controls, -- cgit v0.10.2 From e48d73c697b77b798a82e86c937fc41e597a1471 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:32 +0200 Subject: ASoC: ak4671: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 998fa0c..686cacb 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -611,20 +611,7 @@ static struct snd_soc_dai_driver ak4671_dai = { .ops = &ak4671_dai_ops, }; -static int ak4671_probe(struct snd_soc_codec *codec) -{ - return ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -} - -static int ak4671_remove(struct snd_soc_codec *codec) -{ - ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_ak4671 = { - .probe = ak4671_probe, - .remove = ak4671_remove, .set_bias_level = ak4671_set_bias_level, .controls = ak4671_snd_controls, .num_controls = ARRAY_SIZE(ak4671_snd_controls), -- cgit v0.10.2 From a613cc4063a315efe36f233006f424e958ef4e67 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:33 +0200 Subject: ASoC: max98088: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 2cd3e54..bb892b3 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1887,25 +1887,6 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec) max98088_handle_eq_pdata(codec); } -#ifdef CONFIG_PM -static int max98088_suspend(struct snd_soc_codec *codec) -{ - max98088_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int max98088_resume(struct snd_soc_codec *codec) -{ - max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define max98088_suspend NULL -#define max98088_resume NULL -#endif - static int max98088_probe(struct snd_soc_codec *codec) { struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); @@ -1946,9 +1927,6 @@ static int max98088_probe(struct snd_soc_codec *codec) snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV); - /* initialize registers cache to hardware default */ - max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00); snd_soc_write(codec, M98088_REG_22_MIX_DAC, @@ -1974,7 +1952,6 @@ static int max98088_remove(struct snd_soc_codec *codec) { struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); - max98088_set_bias_level(codec, SND_SOC_BIAS_OFF); kfree(max98088->eq_texts); return 0; @@ -1983,9 +1960,9 @@ static int max98088_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_max98088 = { .probe = max98088_probe, .remove = max98088_remove, - .suspend = max98088_suspend, - .resume = max98088_resume, .set_bias_level = max98088_set_bias_level, + .suspend_bias_off = true, + .controls = max98088_snd_controls, .num_controls = ARRAY_SIZE(max98088_snd_controls), .dapm_widgets = max98088_dapm_widgets, -- cgit v0.10.2 From a8669f60321c8cb08af76438727b6460d1b591b6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:34 +0200 Subject: ASoC: max98095: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 0ee6797..42103ca 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2317,9 +2317,6 @@ static int max98095_probe(struct snd_soc_codec *codec) snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV); - /* initialize registers cache to hardware default */ - max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_write(codec, M98095_048_MIX_DAC_LR, M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR); @@ -2359,8 +2356,6 @@ static int max98095_remove(struct snd_soc_codec *codec) struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); struct i2c_client *client = to_i2c_client(codec->dev); - max98095_set_bias_level(codec, SND_SOC_BIAS_OFF); - if (max98095->headphone_jack || max98095->mic_jack) max98095_jack_detect_disable(codec); -- cgit v0.10.2 From 46804120c59b1374f8beb2b8636ffe6b0a7c16c8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:35 +0200 Subject: ASoC: max9850: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 4fdf5aa..10f8e47 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -291,25 +291,6 @@ static struct snd_soc_dai_driver max9850_dai = { .ops = &max9850_dai_ops, }; -#ifdef CONFIG_PM -static int max9850_suspend(struct snd_soc_codec *codec) -{ - max9850_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int max9850_resume(struct snd_soc_codec *codec) -{ - max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define max9850_suspend NULL -#define max9850_resume NULL -#endif - static int max9850_probe(struct snd_soc_codec *codec) { /* enable zero-detect */ @@ -324,9 +305,8 @@ static int max9850_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_max9850 = { .probe = max9850_probe, - .suspend = max9850_suspend, - .resume = max9850_resume, .set_bias_level = max9850_set_bias_level, + .suspend_bias_off = true, .controls = max9850_controls, .num_controls = ARRAY_SIZE(max9850_controls), -- cgit v0.10.2 From 815b776cf5983ab69d548146fb979adac5dec4de Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:36 +0200 Subject: ASoC: sta32x: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 4874085..7e18200 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -833,23 +833,6 @@ static struct snd_soc_dai_driver sta32x_dai = { .ops = &sta32x_dai_ops, }; -#ifdef CONFIG_PM -static int sta32x_suspend(struct snd_soc_codec *codec) -{ - sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int sta32x_resume(struct snd_soc_codec *codec) -{ - sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define sta32x_suspend NULL -#define sta32x_resume NULL -#endif - static int sta32x_probe(struct snd_soc_codec *codec) { struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); @@ -936,7 +919,6 @@ static int sta32x_remove(struct snd_soc_codec *codec) struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); sta32x_watchdog_stop(sta32x); - sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF); regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); return 0; @@ -955,9 +937,8 @@ static bool sta32x_reg_is_volatile(struct device *dev, unsigned int reg) static const struct snd_soc_codec_driver sta32x_codec = { .probe = sta32x_probe, .remove = sta32x_remove, - .suspend = sta32x_suspend, - .resume = sta32x_resume, .set_bias_level = sta32x_set_bias_level, + .suspend_bias_off = true, .controls = sta32x_snd_controls, .num_controls = ARRAY_SIZE(sta32x_snd_controls), .dapm_widgets = sta32x_dapm_widgets, -- cgit v0.10.2 From 2062c1ff3596e1ae8aafe8082460d03d9a420282 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:37 +0200 Subject: ASoC: sta350: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index cc97dd5..bda2ee1 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -912,23 +912,6 @@ static struct snd_soc_dai_driver sta350_dai = { .ops = &sta350_dai_ops, }; -#ifdef CONFIG_PM -static int sta350_suspend(struct snd_soc_codec *codec) -{ - sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int sta350_resume(struct snd_soc_codec *codec) -{ - sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define sta350_suspend NULL -#define sta350_resume NULL -#endif - static int sta350_probe(struct snd_soc_codec *codec) { struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); @@ -1065,7 +1048,6 @@ static int sta350_remove(struct snd_soc_codec *codec) { struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); - sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); return 0; @@ -1074,9 +1056,8 @@ static int sta350_remove(struct snd_soc_codec *codec) static const struct snd_soc_codec_driver sta350_codec = { .probe = sta350_probe, .remove = sta350_remove, - .suspend = sta350_suspend, - .resume = sta350_resume, .set_bias_level = sta350_set_bias_level, + .suspend_bias_off = true, .controls = sta350_snd_controls, .num_controls = ARRAY_SIZE(sta350_snd_controls), .dapm_widgets = sta350_dapm_widgets, -- cgit v0.10.2 From cfbb77ce368b8d4181e06f8982a440702567eb98 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:38 +0200 Subject: ASoC: sta529: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 89c748d..b0f436d 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -319,41 +319,10 @@ static struct snd_soc_dai_driver sta529_dai = { .ops = &sta529_dai_ops, }; -static int sta529_probe(struct snd_soc_codec *codec) -{ - sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -/* power down chip */ -static int sta529_remove(struct snd_soc_codec *codec) -{ - sta529_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int sta529_suspend(struct snd_soc_codec *codec) -{ - sta529_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int sta529_resume(struct snd_soc_codec *codec) -{ - sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - static const struct snd_soc_codec_driver sta529_codec_driver = { - .probe = sta529_probe, - .remove = sta529_remove, .set_bias_level = sta529_set_bias_level, - .suspend = sta529_suspend, - .resume = sta529_resume, + .suspend_bias_off = true, + .controls = sta529_snd_controls, .num_controls = ARRAY_SIZE(sta529_snd_controls), }; -- cgit v0.10.2 From 4c07a43d9691ab1f264337d683dc8655b1edca46 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 10:56:39 +0200 Subject: ASoC: stac9766: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 53b810d..9878534 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -254,12 +254,6 @@ static int stac9766_reset(struct snd_soc_codec *codec, int try_warm) return 0; } -static int stac9766_codec_suspend(struct snd_soc_codec *codec) -{ - stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int stac9766_codec_resume(struct snd_soc_codec *codec) { u16 id, reset; @@ -278,7 +272,6 @@ reset: reset++; goto reset; } - stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } @@ -349,8 +342,6 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec) goto codec_err; } - stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_codec_controls(codec, stac9766_snd_ac97_controls, ARRAY_SIZE(stac9766_snd_ac97_controls)); @@ -371,9 +362,9 @@ static struct snd_soc_codec_driver soc_codec_dev_stac9766 = { .write = stac9766_ac97_write, .read = stac9766_ac97_read, .set_bias_level = stac9766_set_bias_level, + .suspend_bias_off = true, .probe = stac9766_codec_probe, .remove = stac9766_codec_remove, - .suspend = stac9766_codec_suspend, .resume = stac9766_codec_resume, .reg_cache_size = ARRAY_SIZE(stac9766_reg), .reg_word_size = sizeof(u16), -- cgit v0.10.2 From 15f6b09a00a6d12b594c439fb3a7e2d113a05592 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 19 Oct 2014 09:07:35 +0200 Subject: ASoC: soc-compress: consolidate two identical branches The actions taken in both branches are identical, so we can simplify the code. Spotted by Coverity. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index cecfab3..590a82f 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -258,10 +258,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); - else - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; @@ -456,11 +453,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, if (ret < 0) goto out; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); - else - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); - + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; out: -- cgit v0.10.2 From 5e3363ad1b7b2e1f197a3f56b01e21cb155ad454 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 16 Oct 2014 11:24:26 -0700 Subject: ASoC: rt5677: add GPIO IRQ support This allows to enable Mic Jack detection feature Signed-off-by: Oder Chiou Modified-by: Anatol Pomozov Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt index f82f0e9..740ff77 100644 --- a/Documentation/devicetree/bindings/sound/rt5677.txt +++ b/Documentation/devicetree/bindings/sound/rt5677.txt @@ -33,6 +33,15 @@ Optional properties: 1 - pull down 2 - pull up +- realtek,jd1-gpio + Configures GPIO Mic Jack detection 1. + Select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively. + +- realtek,jd2-gpio +- realtek,jd3-gpio + Configures GPIO Mic Jack detection 2 and 3. + Select 0 ~ 3 as OFF, GPIO4, GPIO5 and GPIO6 respectively. + Pins on the device (for linking into audio routes): * IN1P @@ -63,4 +72,5 @@ rt5677 { <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; realtek,in1-differential = "true"; realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */ + realtek,jd2-gpio = <3>; /* Enables Jack detection for GPIO6 */ }; diff --git a/include/sound/rt5677.h b/include/sound/rt5677.h index a56b429..d9eb7d8 100644 --- a/include/sound/rt5677.h +++ b/include/sound/rt5677.h @@ -30,6 +30,13 @@ struct rt5677_platform_data { /* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */ u8 gpio_config[6]; + + /* jd1 can select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively */ + unsigned int jd1_gpio; + /* jd2 and jd3 can select 0 ~ 3 as + OFF, GPIO4, GPIO5 and GPIO6 respectively */ + unsigned int jd2_gpio; + unsigned int jd3_gpio; }; #endif diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index d17d079..6c73dfd 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -3614,6 +3614,46 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset, } } +static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct rt5677_priv *rt5677 = gpio_to_rt5677(chip); + struct regmap_irq_chip_data *data = rt5677->irq_data; + int irq; + + if (offset >= RT5677_GPIO1 && offset <= RT5677_GPIO3) { + if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) || + (rt5677->pdata.jd1_gpio == 2 && + offset == RT5677_GPIO2) || + (rt5677->pdata.jd1_gpio == 3 && + offset == RT5677_GPIO3)) { + irq = RT5677_IRQ_JD1; + } else { + return -ENXIO; + } + } + + if (offset >= RT5677_GPIO4 && offset <= RT5677_GPIO6) { + if ((rt5677->pdata.jd2_gpio == 1 && offset == RT5677_GPIO4) || + (rt5677->pdata.jd2_gpio == 2 && + offset == RT5677_GPIO5) || + (rt5677->pdata.jd2_gpio == 3 && + offset == RT5677_GPIO6)) { + irq = RT5677_IRQ_JD2; + } else if ((rt5677->pdata.jd3_gpio == 1 && + offset == RT5677_GPIO4) || + (rt5677->pdata.jd3_gpio == 2 && + offset == RT5677_GPIO5) || + (rt5677->pdata.jd3_gpio == 3 && + offset == RT5677_GPIO6)) { + irq = RT5677_IRQ_JD3; + } else { + return -ENXIO; + } + } + + return regmap_irq_get_virq(data, irq); +} + static struct gpio_chip rt5677_template_chip = { .label = "rt5677", .owner = THIS_MODULE, @@ -3621,6 +3661,7 @@ static struct gpio_chip rt5677_template_chip = { .set = rt5677_gpio_set, .direction_input = rt5677_gpio_direction_in, .get = rt5677_gpio_get, + .to_irq = rt5677_to_irq, .can_sleep = 1, }; @@ -3685,6 +3726,31 @@ static int rt5677_probe(struct snd_soc_codec *codec) for (i = 0; i < RT5677_GPIO_NUM; i++) rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]); + if (rt5677->irq_data) { + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000, + 0x8000); + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018, + 0x0008); + + if (rt5677->pdata.jd1_gpio) + regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, + RT5677_SEL_GPIO_JD1_MASK, + rt5677->pdata.jd1_gpio << + RT5677_SEL_GPIO_JD1_SFT); + + if (rt5677->pdata.jd2_gpio) + regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, + RT5677_SEL_GPIO_JD2_MASK, + rt5677->pdata.jd2_gpio << + RT5677_SEL_GPIO_JD2_SFT); + + if (rt5677->pdata.jd3_gpio) + regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, + RT5677_SEL_GPIO_JD3_MASK, + rt5677->pdata.jd3_gpio << + RT5677_SEL_GPIO_JD3_SFT); + } + mutex_init(&rt5677->dsp_cmd_lock); return 0; @@ -3915,9 +3981,74 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) of_property_read_u8_array(np, "realtek,gpio-config", rt5677->pdata.gpio_config, RT5677_GPIO_NUM); + of_property_read_u32(np, "realtek,jd1-gpio", &rt5677->pdata.jd1_gpio); + of_property_read_u32(np, "realtek,jd2-gpio", &rt5677->pdata.jd2_gpio); + of_property_read_u32(np, "realtek,jd3-gpio", &rt5677->pdata.jd3_gpio); + return 0; } +static struct regmap_irq rt5677_irqs[] = { + [RT5677_IRQ_JD1] = { + .reg_offset = 0, + .mask = RT5677_EN_IRQ_GPIO_JD1, + }, + [RT5677_IRQ_JD2] = { + .reg_offset = 0, + .mask = RT5677_EN_IRQ_GPIO_JD2, + }, + [RT5677_IRQ_JD3] = { + .reg_offset = 0, + .mask = RT5677_EN_IRQ_GPIO_JD3, + }, +}; + +static struct regmap_irq_chip rt5677_irq_chip = { + .name = "rt5677", + .irqs = rt5677_irqs, + .num_irqs = ARRAY_SIZE(rt5677_irqs), + + .num_regs = 1, + .status_base = RT5677_IRQ_CTRL1, + .mask_base = RT5677_IRQ_CTRL1, + .mask_invert = 1, +}; + +int rt5677_irq_init(struct i2c_client *i2c) +{ + int ret; + struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); + + if (!rt5677->pdata.jd1_gpio && + !rt5677->pdata.jd2_gpio && + !rt5677->pdata.jd3_gpio) + return 0; + + if (!i2c->irq) { + dev_err(&i2c->dev, "No interrupt specified\n"); + return -EINVAL; + } + + ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0, + &rt5677_irq_chip, &rt5677->irq_data); + + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret); + return ret; + } + + return 0; +} + +void rt5677_irq_exit(struct i2c_client *i2c) +{ + struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); + + if (rt5677->irq_data) + regmap_del_irq_chip(i2c->irq, rt5677->irq_data); +} + static int rt5677_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -4015,6 +4146,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, } rt5677_init_gpio(i2c); + rt5677_irq_init(i2c); return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677, rt5677_dai, ARRAY_SIZE(rt5677_dai)); @@ -4022,6 +4154,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, static int rt5677_i2c_remove(struct i2c_client *i2c) { + rt5677_irq_exit(i2c); + snd_soc_unregister_codec(&i2c->dev); rt5677_free_gpio(i2c); diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 20efa4a..d2c743c 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1368,6 +1368,48 @@ #define RT5677_SEL_SRC_IB01 (0x1 << 0) #define RT5677_SEL_SRC_IB01_SFT 0 +/* Jack Detect Control 1 (0xb5) */ +#define RT5677_SEL_GPIO_JD1_MASK (0x3 << 14) +#define RT5677_SEL_GPIO_JD1_SFT 14 +#define RT5677_SEL_GPIO_JD2_MASK (0x3 << 12) +#define RT5677_SEL_GPIO_JD2_SFT 12 +#define RT5677_SEL_GPIO_JD3_MASK (0x3 << 10) +#define RT5677_SEL_GPIO_JD3_SFT 10 + +/* IRQ Control 1 (0xbd) */ +#define RT5677_STA_GPIO_JD1 (0x1 << 15) +#define RT5677_STA_GPIO_JD1_SFT 15 +#define RT5677_EN_IRQ_GPIO_JD1 (0x1 << 14) +#define RT5677_EN_IRQ_GPIO_JD1_SFT 14 +#define RT5677_EN_GPIO_JD1_STICKY (0x1 << 13) +#define RT5677_EN_GPIO_JD1_STICKY_SFT 13 +#define RT5677_INV_GPIO_JD1 (0x1 << 12) +#define RT5677_INV_GPIO_JD1_SFT 12 +#define RT5677_STA_GPIO_JD2 (0x1 << 11) +#define RT5677_STA_GPIO_JD2_SFT 11 +#define RT5677_EN_IRQ_GPIO_JD2 (0x1 << 10) +#define RT5677_EN_IRQ_GPIO_JD2_SFT 10 +#define RT5677_EN_GPIO_JD2_STICKY (0x1 << 9) +#define RT5677_EN_GPIO_JD2_STICKY_SFT 9 +#define RT5677_INV_GPIO_JD2 (0x1 << 8) +#define RT5677_INV_GPIO_JD2_SFT 8 +#define RT5677_STA_MICBIAS1_OVCD (0x1 << 7) +#define RT5677_STA_MICBIAS1_OVCD_SFT 7 +#define RT5677_EN_IRQ_MICBIAS1_OVCD (0x1 << 6) +#define RT5677_EN_IRQ_MICBIAS1_OVCD_SFT 6 +#define RT5677_EN_MICBIAS1_OVCD_STICKY (0x1 << 5) +#define RT5677_EN_MICBIAS1_OVCD_STICKY_SFT 5 +#define RT5677_INV_MICBIAS1_OVCD (0x1 << 4) +#define RT5677_INV_MICBIAS1_OVCD_SFT 4 +#define RT5677_STA_GPIO_JD3 (0x1 << 3) +#define RT5677_STA_GPIO_JD3_SFT 3 +#define RT5677_EN_IRQ_GPIO_JD3 (0x1 << 2) +#define RT5677_EN_IRQ_GPIO_JD3_SFT 2 +#define RT5677_EN_GPIO_JD3_STICKY (0x1 << 1) +#define RT5677_EN_GPIO_JD3_STICKY_SFT 1 +#define RT5677_INV_GPIO_JD3 (0x1 << 0) +#define RT5677_INV_GPIO_JD3_SFT 0 + /* GPIO status (0xbf) */ #define RT5677_GPIO6_STATUS_MASK (0x1 << 5) #define RT5677_GPIO6_STATUS_SFT 5 @@ -1545,6 +1587,12 @@ enum { RT5677_GPIO_NUM, }; +enum { + RT5677_IRQ_JD1, + RT5677_IRQ_JD2, + RT5677_IRQ_JD3, +}; + struct rt5677_priv { struct snd_soc_codec *codec; struct rt5677_platform_data pdata; @@ -1565,6 +1613,7 @@ struct rt5677_priv { struct gpio_chip gpio_chip; #endif bool dsp_vad_en; + struct regmap_irq_chip_data *irq_data; }; #endif /* __RT5677_H__ */ -- cgit v0.10.2 From cdc4508b4d1c609e3b0e4f23697edbee0d23b86e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 19:36:33 +0200 Subject: ASoC: dapm: Reduce number of checked paths in dapm_widget_in_card_paths() Each widget has a list of all the paths that it is connected to. There is no need to iterate over all paths when we are only interested in the paths of a specific widget. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 39f992b..2c4bfdb 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3788,35 +3788,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); +/** + * dapm_is_external_path() - Checks if a path is a external path + * @card: The card the path belongs to + * @path: The path to check + * + * Returns true if the path is either between two different DAPM contexts or + * between two external pins of the same DAPM context. Otherwise returns + * false. + */ +static bool dapm_is_external_path(struct snd_soc_card *card, + struct snd_soc_dapm_path *path) +{ + dev_dbg(card->dev, + "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", + path->source->name, path->source->id, path->source->dapm, + path->sink->name, path->sink->id, path->sink->dapm); + + /* Connection between two different DAPM contexts */ + if (path->source->dapm != path->sink->dapm) + return true; + + /* Loopback connection from external pin to external pin */ + if (path->sink->id == snd_soc_dapm_input) { + switch (path->source->id) { + case snd_soc_dapm_output: + case snd_soc_dapm_micbias: + return true; + default: + break; + } + } + + return false; +} + static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_path *p; - list_for_each_entry(p, &card->paths, list) { - if ((p->source == w) || (p->sink == w)) { - dev_dbg(card->dev, - "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", - p->source->name, p->source->id, p->source->dapm, - p->sink->name, p->sink->id, p->sink->dapm); + list_for_each_entry(p, &w->sources, list_sink) { + if (dapm_is_external_path(card, p)) + return true; + } - /* Connected to something other than the codec */ - if (p->source->dapm != p->sink->dapm) - return true; - /* - * Loopback connection from codec external pin to - * codec external pin - */ - if (p->sink->id == snd_soc_dapm_input) { - switch (p->source->id) { - case snd_soc_dapm_output: - case snd_soc_dapm_micbias: - return true; - default: - break; - } - } - } + list_for_each_entry(p, &w->sinks, list_source) { + if (dapm_is_external_path(card, p)) + return true; } return false; -- cgit v0.10.2 From 7ddd4cd5c31ccaf32febe52462f9fdc915893212 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 19:36:34 +0200 Subject: ASoC: dapm: Remove always true path source/sink checks A path has always a valid source and a valid sink otherwise we wouldn't add it in the first place. Hence all tests that check if sink/source is non NULL always evaluate to true and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2c4bfdb..28269f2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -909,7 +909,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, trace_snd_soc_dapm_output_path(widget, path); - if (path->sink && path->connect) { + if (path->connect) { path->walked = 1; path->walking = 1; @@ -1017,7 +1017,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, trace_snd_soc_dapm_input_path(widget, path); - if (path->source && path->connect) { + if (path->connect) { path->walked = 1; path->walking = 1; @@ -1219,9 +1219,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) !path->connected(path->source, path->sink)) continue; - if (!path->sink) - continue; - if (dapm_widget_power_check(path->sink)) return 1; } @@ -1636,12 +1633,9 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, /* If we changed our power state perhaps our neigbours changed * also. */ - list_for_each_entry(path, &w->sources, list_sink) { - if (path->source) { - dapm_widget_set_peer_power(path->source, power, - path->connect); - } - } + list_for_each_entry(path, &w->sources, list_sink) + dapm_widget_set_peer_power(path->source, power, path->connect); + switch (w->id) { case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: @@ -1650,12 +1644,9 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, /* Supplies can't affect their outputs, only their inputs */ break; default: - list_for_each_entry(path, &w->sinks, list_source) { - if (path->sink) { - dapm_widget_set_peer_power(path->sink, power, - path->connect); - } - } + list_for_each_entry(path, &w->sinks, list_source) + dapm_widget_set_peer_power(path->sink, power, + path->connect); break; } -- cgit v0.10.2 From cdef2ad3ae64cc1ab2daeff26335e0dde988eed7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 19:36:38 +0200 Subject: ASoC: dapm: Remove special DAI widget power check functions dapm_adc_check_power() checks if the widget is active, if yes it only checks whether there are any connected input paths. Otherwise it calls dapm_generic_check_power() which will check for both connected input and output paths. But the function that checks for connected output paths will return true if the widget is a active sink. Which means the generic power check function will work just fine and there is no need for a special power check function. The same applies for dapm_dac_check_power(), but with input and output paths reversed. This patch removes both dapm_adc_check_power() and dapm_dac_check_power() and replace their usage with dapm_generic_check_power(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 28269f2..219d73c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1169,38 +1169,6 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) return out != 0 && in != 0; } -/* Check to see if an ADC has power */ -static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) -{ - int in; - - DAPM_UPDATE_STAT(w, power_checks); - - if (w->active) { - in = is_connected_input_ep(w, NULL); - dapm_clear_walk_input(w->dapm, &w->sources); - return in != 0; - } else { - return dapm_generic_check_power(w); - } -} - -/* Check to see if a DAC has power */ -static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) -{ - int out; - - DAPM_UPDATE_STAT(w, power_checks); - - if (w->active) { - out = is_connected_output_ep(w, NULL); - dapm_clear_walk_output(w->dapm, &w->sinks); - return out != 0; - } else { - return dapm_generic_check_power(w); - } -} - /* Check to see if a power supply is needed */ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) { @@ -3086,12 +3054,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_mux: w->power_check = dapm_generic_check_power; break; - case snd_soc_dapm_dai_out: - w->power_check = dapm_adc_check_power; - break; - case snd_soc_dapm_dai_in: - w->power_check = dapm_dac_check_power; - break; case snd_soc_dapm_adc: case snd_soc_dapm_aif_out: case snd_soc_dapm_dac: @@ -3106,6 +3068,8 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_dai_link: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_dai_in: w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_supply: -- cgit v0.10.2 From 130897ac5ac03adb4604d27497c378c64c7b22dd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Oct 2014 19:36:39 +0200 Subject: ASoC: dapm: Remove path 'walked' flag The 'walked' flag was used to avoid walking paths that have already been walked. But since we started caching the number of inputs and outputs of a path we never actually get into a situation where we try to walk a path that has the 'walked' flag set. There are two cases in which we can end up walking a path multiple times within a single run of is_connected_output_ep() or is_connected_input_ep(). 1) If a path splits up and rejoins later: .--> C ---v A -> B E --> F '--> D ---^ When walking from A to F we'll end up at E twice, once via C and once via D. But since we do a depth first search we'll fully discover the path and initialize the number of outputs/inputs of the widget the first time we get there. The second time we get there we'll use the cached value and not bother to check any of the paths again. So we'll never see a path where 'walked' is set in this case. 2) If there is a circle: A --> B <-- C <-.--> F '--> D ---' When walking from A to F we'll end up twice at B. But since there is a circle the 'walking' flag will still be set on B once we get there the second time. This means we won't look at any of it's outgoing paths. So in this case we won't ever see a path where 'walked' is set either. So it is safe to remove the flag. This on one hand means we remove some always true checks from one of the hottest paths of the DAPM algorithm and on the other hand means we do not have to do the tedious clearing of the flag after checking the number inputs or outputs of a widget. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3a4d7da..ebb93f2 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -508,7 +508,6 @@ struct snd_soc_dapm_path { /* status */ u32 connect:1; /* source and sink widgets are connected */ - u32 walked:1; /* path has been walked */ u32 walking:1; /* path is in the process of being walked */ u32 weak:1; /* path ignored for power management */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 219d73c..f03e0cf 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -754,34 +754,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) return 0; } -/* reset 'walked' bit for each dapm path */ -static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm, - struct list_head *sink) -{ - struct snd_soc_dapm_path *p; - - list_for_each_entry(p, sink, list_source) { - if (p->walked) { - p->walked = 0; - dapm_clear_walk_output(dapm, &p->sink->sinks); - } - } -} - -static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm, - struct list_head *source) -{ - struct snd_soc_dapm_path *p; - - list_for_each_entry(p, source, list_sink) { - if (p->walked) { - p->walked = 0; - dapm_clear_walk_input(dapm, &p->source->sources); - } - } -} - - /* We implement power down on suspend by checking the power state of * the ALSA card - when we are suspending the ALSA state for the card * is set to D3. @@ -904,13 +876,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, if (path->walking) return 1; - if (path->walked) - continue; - trace_snd_soc_dapm_output_path(widget, path); if (path->connect) { - path->walked = 1; path->walking = 1; /* do we need to add this widget to the list ? */ @@ -1012,13 +980,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, if (path->walking) return 1; - if (path->walked) - continue; - trace_snd_soc_dapm_input_path(widget, path); if (path->connect) { - path->walked = 1; path->walking = 1; /* do we need to add this widget to the list ? */ @@ -1066,15 +1030,10 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); dapm_reset(card); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) paths = is_connected_output_ep(dai->playback_widget, list); - dapm_clear_walk_output(&card->dapm, - &dai->playback_widget->sinks); - } else { + else paths = is_connected_input_ep(dai->capture_widget, list); - dapm_clear_walk_input(&card->dapm, - &dai->capture_widget->sources); - } trace_snd_soc_dapm_connected(paths, stream); mutex_unlock(&card->dapm_mutex); @@ -1163,9 +1122,7 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); in = is_connected_input_ep(w, NULL); - dapm_clear_walk_input(w->dapm, &w->sources); out = is_connected_output_ep(w, NULL); - dapm_clear_walk_output(w->dapm, &w->sinks); return out != 0 && in != 0; } @@ -1823,9 +1780,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, return -ENOMEM; in = is_connected_input_ep(w, NULL); - dapm_clear_walk_input(w->dapm, &w->sources); out = is_connected_output_ep(w, NULL); - dapm_clear_walk_output(w->dapm, &w->sinks); ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", w->name, w->power ? "On" : "Off", -- cgit v0.10.2 From 2d27deb40db74c751c991e96ca91d675f966a0c5 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Wed, 22 Oct 2014 20:04:08 +0800 Subject: ASoC: rt5677: rt5677_irq_init() can be static sound/soc/codecs/rt5677.c:4017:5: sparse: symbol 'rt5677_irq_init' was not declared. Should it be static? sound/soc/codecs/rt5677.c:4044:6: sparse: symbol 'rt5677_irq_exit' was not declared. Should it be static? Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 6c73dfd..413bccb 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4014,7 +4014,7 @@ static struct regmap_irq_chip rt5677_irq_chip = { .mask_invert = 1, }; -int rt5677_irq_init(struct i2c_client *i2c) +static int rt5677_irq_init(struct i2c_client *i2c) { int ret; struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); @@ -4041,7 +4041,7 @@ int rt5677_irq_init(struct i2c_client *i2c) return 0; } -void rt5677_irq_exit(struct i2c_client *i2c) +static void rt5677_irq_exit(struct i2c_client *i2c) { struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); -- cgit v0.10.2 From ace0eb1e91a75b84b1be3d610b79509a5bd94df1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Oct 2014 18:13:46 -0700 Subject: ASoC: rsnd: tidyup debug information when read/write b8c637864a6904a9ba8e0df556d5bdf9f26b2c54 (ASoC: rsnd: use regmap_mmio instead of original regmap bus) added regmap_mmio support on Renesas R-Car sound driver. Then, debug information of register read/write indicates regmap index, not register address. This is a little bit confusable information. This patch tidyup debug message, and added regmap debug hint on comment area. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index f95e7ab..61dee68 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -8,6 +8,17 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +/* + * #define DEBUG + * + * you can also add below in + * ${LINUX}/drivers/base/regmap/regmap.c + * for regmap debug + * + * #define LOG_DEVICE "xxxx.rcar_sound" + */ + #include "rsnd.h" struct rsnd_gen { @@ -67,9 +78,10 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; - regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + dev_dbg(dev, "r %s(%d) - %4d : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); - dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val); + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); return val; } @@ -84,9 +96,10 @@ void rsnd_write(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); + dev_dbg(dev, "w %s(%d) - %4d : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data); - dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data); + regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); } void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, @@ -98,11 +111,11 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; + dev_dbg(dev, "b %s(%d) - %4d : %08x/%08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask); + regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), mask, data); - - dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n", - rsnd_mod_name(mod), reg, data, mask); } #define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \ -- cgit v0.10.2 From 9960ce97432bdb1defc76ed80ac19e37e8778bc6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Oct 2014 18:13:56 -0700 Subject: ASoC: rsnd: tidyup RSND_DVC_VOLUME_NUM to RSND_DVC_CHANNELS RSND_DVC_VOLUME_NUM means DVC channel number. This patch tidyups this un-understandable naming Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 3f44393..b5f95ad4 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -12,7 +12,7 @@ #define RSND_DVC_NAME_SIZE 16 #define RSND_DVC_VOLUME_MAX 100 -#define RSND_DVC_VOLUME_NUM 2 +#define RSND_DVC_CHANNELS 2 #define DVC_NAME "dvc" @@ -20,8 +20,8 @@ struct rsnd_dvc { struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; - u8 volume[RSND_DVC_VOLUME_NUM]; - u8 mute[RSND_DVC_VOLUME_NUM]; + u8 volume[RSND_DVC_CHANNELS]; + u8 mute[RSND_DVC_CHANNELS]; }; #define rsnd_mod_to_dvc(_mod) \ @@ -37,11 +37,11 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); u32 max = (0x00800000 - 1); - u32 vol[RSND_DVC_VOLUME_NUM]; + u32 vol[RSND_DVC_CHANNELS]; u32 mute = 0; int i; - for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) { + for (i = 0; i < RSND_DVC_CHANNELS; i++) { vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i]; mute |= (!!dvc->mute[i]) << i; } @@ -150,7 +150,7 @@ static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); u8 *val = (u8 *)kctrl->private_value; - uinfo->count = RSND_DVC_VOLUME_NUM; + uinfo->count = RSND_DVC_CHANNELS; uinfo->value.integer.min = 0; if (val == dvc->volume) { @@ -170,7 +170,7 @@ static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, u8 *val = (u8 *)kctrl->private_value; int i; - for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) + for (i = 0; i < RSND_DVC_CHANNELS; i++) ucontrol->value.integer.value[i] = val[i]; return 0; @@ -183,7 +183,7 @@ static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, u8 *val = (u8 *)kctrl->private_value; int i, change = 0; - for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) { + for (i = 0; i < RSND_DVC_CHANNELS; i++) { change |= (ucontrol->value.integer.value[i] != val[i]); val[i] = ucontrol->value.integer.value[i]; } -- cgit v0.10.2 From 92b9a6991b2e3a4ccf5ffc956730d36835d53a79 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 21 Oct 2014 18:14:14 -0700 Subject: ASoC: rsnd: add struct rsnd_dvc_cfg and control DVC settings DVC can control Digital Volume / Mute / Volume Ramp etc, and these uses different max value. Current driver is using fixed max value for each settings. This patch adds new struct rsnd_dvc_cfg, and control these. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index b5f95ad4..deaf0fa 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -11,17 +11,21 @@ #include "rsnd.h" #define RSND_DVC_NAME_SIZE 16 -#define RSND_DVC_VOLUME_MAX 100 #define RSND_DVC_CHANNELS 2 #define DVC_NAME "dvc" +struct rsnd_dvc_cfg { + unsigned int max; + u32 val[RSND_DVC_CHANNELS]; +}; + struct rsnd_dvc { struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; - u8 volume[RSND_DVC_CHANNELS]; - u8 mute[RSND_DVC_CHANNELS]; + struct rsnd_dvc_cfg volume; + struct rsnd_dvc_cfg mute; }; #define rsnd_mod_to_dvc(_mod) \ @@ -36,18 +40,15 @@ struct rsnd_dvc { static void rsnd_dvc_volume_update(struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u32 max = (0x00800000 - 1); - u32 vol[RSND_DVC_CHANNELS]; u32 mute = 0; int i; for (i = 0; i < RSND_DVC_CHANNELS; i++) { - vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i]; - mute |= (!!dvc->mute[i]) << i; + mute |= (!!dvc->mute.val[i]) << i; } - rsnd_mod_write(mod, DVC_VOL0R, vol[0]); - rsnd_mod_write(mod, DVC_VOL1R, vol[1]); + rsnd_mod_write(mod, DVC_VOL0R, dvc->volume.val[0]); + rsnd_mod_write(mod, DVC_VOL1R, dvc->volume.val[1]); rsnd_mod_write(mod, DVC_ZCMCR, mute); } @@ -146,20 +147,16 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod, static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo) { - struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); - struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u8 *val = (u8 *)kctrl->private_value; + struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; uinfo->count = RSND_DVC_CHANNELS; uinfo->value.integer.min = 0; + uinfo->value.integer.max = cfg->max; - if (val == dvc->volume) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->value.integer.max = RSND_DVC_VOLUME_MAX; - } else { + if (cfg->max == 1) uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->value.integer.max = 1; - } + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; return 0; } @@ -167,11 +164,11 @@ static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *ucontrol) { - u8 *val = (u8 *)kctrl->private_value; + struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; int i; for (i = 0; i < RSND_DVC_CHANNELS; i++) - ucontrol->value.integer.value[i] = val[i]; + ucontrol->value.integer.value[i] = cfg->val[i]; return 0; } @@ -180,12 +177,12 @@ static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *ucontrol) { struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); - u8 *val = (u8 *)kctrl->private_value; + struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; int i, change = 0; for (i = 0; i < RSND_DVC_CHANNELS; i++) { - change |= (ucontrol->value.integer.value[i] != val[i]); - val[i] = ucontrol->value.integer.value[i]; + change |= (ucontrol->value.integer.value[i] != cfg->val[i]); + cfg->val[i] = ucontrol->value.integer.value[i]; } if (change) @@ -198,7 +195,7 @@ static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - u8 *private) + struct rsnd_dvc_cfg *private) { struct snd_card *card = rtd->card->snd_card; struct snd_kcontrol *kctrl; @@ -232,18 +229,20 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, int ret; /* Volume */ + dvc->volume.max = 0x00800000 - 1; ret = __rsnd_dvc_pcm_new(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Playback Volume" : "DVC In Capture Volume", - dvc->volume); + &dvc->volume); if (ret < 0) return ret; /* Mute */ + dvc->mute.max = 1; ret = __rsnd_dvc_pcm_new(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Mute Switch" : "DVC In Mute Switch", - dvc->mute); + &dvc->mute); if (ret < 0) return ret; -- cgit v0.10.2 From 00d647b081b5ef2193fd15910fbd103f483a5d15 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 23 Oct 2014 17:15:18 +0900 Subject: ASoC: jack: update calls to gpiod_get*() Add the new flags argument to calls of (devm_)gpiod_get*() and remove any direction setting code afterwards. Currently both forms (with or without the flags argument) are valid thanks to transitional macros in . These macros will be removed once all consumers are updated and the flags argument will become compulsary. Signed-off-by: Alexandre Courbot Signed-off-by: Mark Brown diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ab47fea..f921d00 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -309,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, /* GPIO descriptor */ gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev, gpios[i].name, - gpios[i].idx); + gpios[i].idx, GPIOD_IN); if (IS_ERR(gpios[i].desc)) { ret = PTR_ERR(gpios[i].desc); dev_err(gpios[i].gpiod_dev, @@ -327,17 +327,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, goto undo; } - ret = gpio_request(gpios[i].gpio, gpios[i].name); + ret = gpio_request_one(gpios[i].gpio, GPIOF_IN, + gpios[i].name); if (ret) goto undo; gpios[i].desc = gpio_to_desc(gpios[i].gpio); } - ret = gpiod_direction_input(gpios[i].desc); - if (ret) - goto err; - INIT_DELAYED_WORK(&gpios[i].work, gpio_work); gpios[i].jack = jack; -- cgit v0.10.2 From e29bee098ea1cc9b8537628f3c1cdf60ead83514 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 20 Oct 2014 20:30:13 -0700 Subject: ASoC: rt5677: fix rt5677 spi driver build Create a separate module for rt5677 spi driver. Without this patch, the build fails due to multiple defs of 'init_module' and 'cleanup_module'. module_spi_driver() defines its own module, so it can't be part of the rt5677 module. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2c7482e..6f21a76 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -504,6 +504,10 @@ config SND_SOC_RT5670 config SND_SOC_RT5677 tristate +config SND_SOC_RT5677_SPI + tristate + default SND_SOC_RT5677 + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4435f9f..3e57edc 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -79,7 +79,8 @@ snd-soc-rt5640-objs := rt5640.o snd-soc-rt5645-objs := rt5645.o snd-soc-rt5651-objs := rt5651.o snd-soc-rt5670-objs := rt5670.o -snd-soc-rt5677-objs := rt5677.o rt5677-spi.o +snd-soc-rt5677-objs := rt5677.o +snd-soc-rt5677-spi-objs := rt5677-spi.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -256,6 +257,7 @@ obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o +obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index 11c38f3..ef6348c 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -52,6 +52,7 @@ int rt5677_spi_write(u8 *txbuf, size_t len) return status; } +EXPORT_SYMBOL_GPL(rt5677_spi_write); /** * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address. @@ -107,6 +108,7 @@ int rt5677_spi_burst_write(u32 addr, const struct firmware *fw) return 0; } +EXPORT_SYMBOL_GPL(rt5677_spi_burst_write); static int rt5677_spi_probe(struct spi_device *spi) { -- cgit v0.10.2 From 39581031a90d69e4b79cd044756169ff35ecab46 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 24 Oct 2014 13:49:46 +0530 Subject: ASoC: Intel: mrfld: Replace pci_id with unique device id In order to support both ACPI and PCI devices we need to use a genric device id in driver, so change all pci_id instances to device_id Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index d88cdd9..fa34217 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -167,7 +167,7 @@ static struct intel_sst_ops mrfld_ops = { int sst_driver_ops(struct intel_sst_drv *sst) { - switch (sst->pci_id) { + switch (sst->dev_id) { case SST_MRFLD_PCI_ID: sst->tstamp = SST_TIME_STAMP_MRFLD; sst->ops = &mrfld_ops; @@ -175,7 +175,7 @@ int sst_driver_ops(struct intel_sst_drv *sst) default: dev_err(sst->dev, - "SST Driver capablities missing for pci_id: %x", sst->pci_id); + "SST Driver capablities missing for dev_id: %x", sst->dev_id); return -EINVAL; }; } @@ -210,7 +210,7 @@ static int intel_sst_probe(struct pci_dev *pci, return -ENOMEM; sst_drv_ctx->dev = &pci->dev; - sst_drv_ctx->pci_id = pci->device; + sst_drv_ctx->dev_id = pci->device; if (!sst_pdata) return -EINVAL; @@ -278,7 +278,7 @@ static int intel_sst_probe(struct pci_dev *pci, /* map registers */ /* DDR base */ - if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) { + if (sst_drv_ctx->dev_id == SST_MRFLD_PCI_ID) { sst_drv_ctx->ddr_base = pci_resource_start(pci, 0); /* check that the relocated IMR base matches with FW Binary */ ddr_base = relocate_imr_addr_mrfld(sst_drv_ctx->ddr_base); @@ -357,7 +357,7 @@ static int intel_sst_probe(struct pci_dev *pci, dev_dbg(sst_drv_ctx->dev, "Registered IRQ 0x%x\n", pci->irq); /* default intr are unmasked so set this as masked */ - if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) + if (sst_drv_ctx->dev_id == SST_MRFLD_PCI_ID) sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, 0xFFFF0038); pci_set_drvdata(pci, sst_drv_ctx); diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index bfcf51a..b65b9c0 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -337,7 +337,8 @@ struct sst_shim_regs64 { * struct intel_sst_drv - driver ops * * @sst_state : current sst device state - * @pci_id : PCI device id loaded + * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi + * devices * @shim : SST shim pointer * @mailbox : SST mailbox pointer * @iram : SST IRAM pointer @@ -371,7 +372,7 @@ struct sst_shim_regs64 { struct intel_sst_drv { int sst_state; int irq_num; - unsigned int pci_id; + unsigned int dev_id; void __iomem *ddr; void __iomem *shim; void __iomem *mailbox; diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c index 9e5f69b..1c2e081 100644 --- a/sound/soc/intel/sst/sst_pvt.c +++ b/sound/soc/intel/sst/sst_pvt.c @@ -115,7 +115,7 @@ unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) { unsigned long long val = 0; - switch (sst->pci_id) { + switch (sst->dev_id) { case SST_MRFLD_PCI_ID: val = sst_shim_read64(sst->shim, addr); break; @@ -126,7 +126,7 @@ unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) void write_shim_data(struct intel_sst_drv *sst, int addr, unsigned long long data) { - switch (sst->pci_id) { + switch (sst->dev_id) { case SST_MRFLD_PCI_ID: sst_shim_write64(sst->shim, addr, (u64) data); break; -- cgit v0.10.2 From 43c5e23197a187a6f4dade97f3bd23e35636ab1f Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 24 Oct 2014 13:49:47 +0530 Subject: ASoC: Intel: mrfld - Define ipc_info structure This will be used to abstract the differances in ipc offsets for different chips. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h index 268a96ae..6021dee 100644 --- a/arch/x86/include/asm/platform_sst_audio.h +++ b/arch/x86/include/asm/platform_sst_audio.h @@ -102,6 +102,11 @@ struct sst_lib_dnld_info { bool mod_ddr_dnld; }; +struct sst_ipc_info { + int ipc_offset; + unsigned int mbox_recv_off; +}; + struct sst_platform_info { const struct sst_info *probe_data; const struct sst_ipc_info *ipc_info; -- cgit v0.10.2 From 9a80e8f597f3bde0e1d4a4abb021d475520005a5 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 24 Oct 2014 13:49:48 +0530 Subject: ASoC: Intel: mrfld: Define sst_res_info for acpi To query resources in ACPI systems we need to define a data structure. This would be set as platform_info for the devices. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h index 6021dee..7249e6d 100644 --- a/arch/x86/include/asm/platform_sst_audio.h +++ b/arch/x86/include/asm/platform_sst_audio.h @@ -102,6 +102,27 @@ struct sst_lib_dnld_info { bool mod_ddr_dnld; }; +struct sst_res_info { + unsigned int shim_offset; + unsigned int shim_size; + unsigned int shim_phy_addr; + unsigned int ssp0_offset; + unsigned int ssp0_size; + unsigned int dma0_offset; + unsigned int dma0_size; + unsigned int dma1_offset; + unsigned int dma1_size; + unsigned int iram_offset; + unsigned int iram_size; + unsigned int dram_offset; + unsigned int dram_size; + unsigned int mbox_offset; + unsigned int mbox_size; + unsigned int acpi_lpe_res_index; + unsigned int acpi_ddr_index; + unsigned int acpi_ipc_irq_index; +}; + struct sst_ipc_info { int ipc_offset; unsigned int mbox_recv_off; @@ -110,7 +131,9 @@ struct sst_ipc_info { struct sst_platform_info { const struct sst_info *probe_data; const struct sst_ipc_info *ipc_info; + const struct sst_res_info *res_info; const struct sst_lib_dnld_info *lib_info; + const char *platform; }; int add_sst_platform_device(void); #endif -- cgit v0.10.2 From 4a2019480bc5146eb54fc5f0b2ff57b95629a09a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:41:56 +0200 Subject: ASoC: dapm: Only mark paths dirty when the connection status changed Rework soc_dapm_{mixer,mux}_update_power() to only mark a path dirty if the connect state if the path has actually changed. This avoids unnecessary power state checks for the widgets involved. Also factor out the common code that is involved in this into a helper function. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f03e0cf..116d443 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1925,12 +1925,31 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif +/* + * soc_dapm_connect_path() - Connects or disconnects a path + * @path: The path to update + * @connect: The new connect state of the path. True if the path is connected, + * false if it is disconneted. + * @reason: The reason why the path changed (for debugging only) + */ +static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, + bool connect, const char *reason) +{ + if (path->connect == connect) + return; + + path->connect = connect; + dapm_mark_dirty(path->source, reason); + dapm_mark_dirty(path->sink, reason); +} + /* test and update the power status of a mux widget */ static int soc_dapm_mux_update_power(struct snd_soc_card *card, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; + bool connect; lockdep_assert_held(&card->dapm_mutex); @@ -1941,16 +1960,12 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, found = 1; /* we now need to match the string in the enum to the path */ - if (!(strcmp(path->name, e->texts[mux]))) { - path->connect = 1; /* new connection */ - dapm_mark_dirty(path->source, "mux connection"); - } else { - if (path->connect) - dapm_mark_dirty(path->source, - "mux disconnection"); - path->connect = 0; /* old connection must be powered down */ - } - dapm_mark_dirty(path->sink, "mux change"); + if (!(strcmp(path->name, e->texts[mux]))) + connect = true; + else + connect = false; + + soc_dapm_connect_path(path, connect, "mux update"); } if (found) @@ -1989,9 +2004,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { found = 1; - path->connect = connect; - dapm_mark_dirty(path->source, "mixer connection"); - dapm_mark_dirty(path->sink, "mixer update"); + soc_dapm_connect_path(path, connect, "mixer update"); } if (found) -- cgit v0.10.2 From 98407efc1384b31cdcb1eeddc74ee35499d3418f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:41:57 +0200 Subject: ASoC: dapm: Do not add un-muxed paths to MUX control Paths that are directly connected to a MUX widget are not affected by changes to the MUX's control. Rather than checking if a path is directly connected each time the MUX is updated do it only once when MUX is created. We can also remove the check for e->texts[mux] != NULL, since if that condition was true the code would have had already crashed much earlier (And generally speaking if a enum's 'texts' entry is NULL it's a bug in the driver). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 116d443..1fed2207b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -738,8 +738,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) if (ret < 0) return ret; - list_for_each_entry(path, &w->sources, list_sink) - dapm_kcontrol_add_path(w->kcontrols[0], path); + list_for_each_entry(path, &w->sources, list_sink) { + if (path->name) + dapm_kcontrol_add_path(w->kcontrols[0], path); + } return 0; } @@ -1955,9 +1957,6 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { - if (!path->name || !e->texts[mux]) - continue; - found = 1; /* we now need to match the string in the enum to the path */ if (!(strcmp(path->name, e->texts[mux]))) -- cgit v0.10.2 From 5fe5b767dc6fb3df6fa6eaa8e05b727914f2bb4c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:41:58 +0200 Subject: ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets Controls on a path only have an effect if the sink on the path is either a mixer or mux widget. Currently we sort of silently ignore controls on other paths, but since they don't do anything having them on other paths does not make much sense and it is probably safe to assume that if we see such a path it is a mistake in the driver that registered the path. This patch modifies snd_soc_dapm_add_path() to report an error if a path with and control is encountered where we didn't expect a control. This also allows to simplify the code quite a bit. The patch also moves the connecting of the path lists out of dapm_connect_mux() and dapm_connect_mixer() into snd_soc_dapm_add_path(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1fed2207b..c49df10 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -469,10 +469,9 @@ out: /* connect mux widget to its interconnecting audio paths */ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, - struct snd_soc_dapm_path *path, const char *control_name, - const struct snd_kcontrol_new *kcontrol) + struct snd_soc_dapm_path *path, const char *control_name) { + const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, item; int i; @@ -493,9 +492,6 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, for (i = 0; i < e->items; i++) { if (!(strcmp(control_name, e->texts[i]))) { - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &dest->sources); - list_add(&path->list_source, &src->sinks); path->name = e->texts[i]; if (i == item) path->connect = 1; @@ -509,11 +505,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, } /* set up initial codec paths */ -static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, - struct snd_soc_dapm_path *p, int i) +static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) { struct soc_mixer_control *mc = (struct soc_mixer_control *) - w->kcontrol_news[i].private_value; + p->sink->kcontrol_news[i].private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int max = mc->max; @@ -522,7 +517,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, unsigned int val; if (reg != SND_SOC_NOPM) { - soc_dapm_read(w->dapm, reg, &val); + soc_dapm_read(p->sink->dapm, reg, &val); val = (val >> shift) & mask; if (invert) val = max - val; @@ -534,19 +529,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, /* connect mixer widget to its interconnecting audio paths */ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, struct snd_soc_dapm_path *path, const char *control_name) { int i; /* search for mixer kcontrol */ - for (i = 0; i < dest->num_kcontrols; i++) { - if (!strcmp(control_name, dest->kcontrol_news[i].name)) { - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &dest->sources); - list_add(&path->list_source, &src->sinks); - path->name = dest->kcontrol_news[i].name; - dapm_set_mixer_path_status(dest, path, i); + for (i = 0; i < path->sink->num_kcontrols; i++) { + if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { + path->name = path->sink->kcontrol_news[i].name; + dapm_set_mixer_path_status(path, i); return 0; } } @@ -2272,69 +2263,40 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, wsource->ext = 1; } - dapm_mark_dirty(wsource, "Route added"); - dapm_mark_dirty(wsink, "Route added"); - /* connect static paths */ if (control == NULL) { - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); path->connect = 1; - return 0; - } - - /* connect dynamic paths */ - switch (wsink->id) { - case snd_soc_dapm_adc: - case snd_soc_dapm_dac: - case snd_soc_dapm_pga: - case snd_soc_dapm_out_drv: - case snd_soc_dapm_input: - case snd_soc_dapm_output: - case snd_soc_dapm_siggen: - case snd_soc_dapm_micbias: - case snd_soc_dapm_vmid: - case snd_soc_dapm_pre: - case snd_soc_dapm_post: - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_aif_in: - case snd_soc_dapm_aif_out: - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - case snd_soc_dapm_dai_link: - case snd_soc_dapm_kcontrol: - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); - path->connect = 1; - return 0; - case snd_soc_dapm_mux: - ret = dapm_connect_mux(dapm, wsource, wsink, path, control, - &wsink->kcontrol_news[0]); - if (ret != 0) - goto err; - break; - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - ret = dapm_connect_mixer(dapm, wsource, wsink, path, control); - if (ret != 0) + } else { + /* connect dynamic paths */ + switch (wsink->id) { + case snd_soc_dapm_mux: + ret = dapm_connect_mux(dapm, path, control); + if (ret != 0) + goto err; + break; + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + ret = dapm_connect_mixer(dapm, path, control); + if (ret != 0) + goto err; + break; + default: + dev_err(dapm->dev, + "Control not supported for path %s -> [%s] -> %s\n", + wsource->name, control, wsink->name); + ret = -EINVAL; goto err; - break; - case snd_soc_dapm_hp: - case snd_soc_dapm_mic: - case snd_soc_dapm_line: - case snd_soc_dapm_spk: - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); - path->connect = 0; - return 0; + } } + list_add(&path->list, &dapm->card->paths); + list_add(&path->list_sink, &wsink->sources); + list_add(&path->list_source, &wsource->sinks); + + dapm_mark_dirty(wsource, "Route added"); + dapm_mark_dirty(wsink, "Route added"); + return 0; err: kfree(path); -- cgit v0.10.2 From 6dd98b0a3e58b7b48a422802b5610b95ef5128eb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:41:59 +0200 Subject: ASoC: dapm: Introduce toplevel widget categories DAPM widgets can be classified into four categories: * supply: Supply widgets do not affect the power state of their non-supply widget neighbors and unlike other widgets a supply widget is not powered up when it is on an active path, but when at least on of its neighbors is powered up. * source: A source is a widget that receives data from outside the DAPM graph or generates data. This can for example be a microphone, the playback DMA or a signal generator. A source widget will be considered powered up if there is an active path to a sink widget. * sink: A sink is a widget that transmits data to somewhere outside of the DAPM graph. This can e.g. be a speaker or the capture DMA. A sink widget will be considered powered up if there is an active path from a source widget. * normal: Normal widgets are widgets not covered by the categories above. A normal widget will be considered powered up if it is on an active path between a source widget and a sink widget. The way the number of input and output paths for a widget is calculated depends on its category. There are a bunch of factors which decide which category a widget is. Currently there is no formal classification of these categories and we calculate the category of the widget based on these factors whenever we want to know it. This is at least once for every widget during each power update sequence. The factors which determine the category of the widgets are mostly static though and if at all change rather seldom. This patch introduces three new per widget flags, one for each of non-normal widgets categories. Instead of re-computing the category each time we want to know them the flags will be checked. For the majority of widgets the category is solely determined by the widget id, which means it never changes and only has to be set once when the widget is created. The only widgets with dynamic categories are: snd_soc_dapm_dai_out: Is considered a sink iff the capture stream is active, otherwise normal. snd_soc_dapm_dai_in: Is considered a source iff the playback stream is active, otherwise normal. snd_soc_dapm_input: Is considered a sink iff it has no outgoing paths, otherwise normal. snd_soc_dapm_output: Is considered a source iff it has no incoming paths, otherwise normal. snd_soc_dapm_line: Is considered a sink iff it has no outgoing paths and is considered a source iff it has no incoming paths, otherwise normal. For snd_soc_dapm_dai_out/snd_soc_dapm_dai_in widgets the category will be updated when a stream is started or stopped. For the other dynamic widgets the category will be updated when a path connecting to it is added or removed. Introducing those new widget categories allows to make is_connected_{output,input}_ep, which are among the hottest paths of the DAPM algorithm, more generic and significantly shorter. The before and after sizes for is_connected_{output,input}_ep are: On ARM (defconfig + CONFIG_SND_SOC): function old new delta is_connected_output_ep 480 340 -140 is_connected_input_ep 456 352 -104 On amd64 (defconfig + CONFIG_SND_SOC): function old new delta is_connected_output_ep 579 427 -152 is_connected_input_ep 563 427 -136 Which is about a 25%-30% decrease, other architectures are expected to have similar numbers. At the same time the size of the snd_soc_dapm_widget struct does not change since the new flags are stored in the same word as the existing flags. Note: that since the per widget 'ext' flag was only used to decide whether a snd_soc_dapm_input or snd_soc_dapm_output widget was a source or a sink it is now unused and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ebb93f2..8cf3aad 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -543,11 +543,13 @@ struct snd_soc_dapm_widget { unsigned char active:1; /* active stream on DAC, ADC's */ unsigned char connected:1; /* connected codec pin */ unsigned char new:1; /* cnew complete */ - unsigned char ext:1; /* has external widgets */ unsigned char force:1; /* force state */ unsigned char ignore_suspend:1; /* kept enabled over suspend */ unsigned char new_power:1; /* power from this run */ unsigned char power_checked:1; /* power checked this run */ + unsigned char is_supply:1; /* Widget is a supply type widget */ + unsigned char is_sink:1; /* Widget is a sink type widget */ + unsigned char is_source:1; /* Widget is a source type widget */ int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c49df10..2cad5f7 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -821,43 +821,12 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); - switch (widget->id) { - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_kcontrol: + if (widget->is_supply) return 0; - default: - break; - } - switch (widget->id) { - case snd_soc_dapm_adc: - case snd_soc_dapm_aif_out: - case snd_soc_dapm_dai_out: - if (widget->active) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; - } - default: - break; - } - - if (widget->connected) { - /* connected pin ? */ - if (widget->id == snd_soc_dapm_output && !widget->ext) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; - } - - /* connected jack or spk ? */ - if (widget->id == snd_soc_dapm_hp || - widget->id == snd_soc_dapm_spk || - (widget->id == snd_soc_dapm_line && - !list_empty(&widget->sources))) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; - } + if (widget->is_sink && widget->connected) { + widget->outputs = snd_soc_dapm_suspend_check(widget); + return widget->outputs; } list_for_each_entry(path, &widget->sinks, list_source) { @@ -913,55 +882,12 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); - switch (widget->id) { - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_kcontrol: + if (widget->is_supply) return 0; - default: - break; - } - - /* active stream ? */ - switch (widget->id) { - case snd_soc_dapm_dac: - case snd_soc_dapm_aif_in: - case snd_soc_dapm_dai_in: - if (widget->active) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - default: - break; - } - - if (widget->connected) { - /* connected pin ? */ - if (widget->id == snd_soc_dapm_input && !widget->ext) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - /* connected VMID/Bias for lower pops */ - if (widget->id == snd_soc_dapm_vmid) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - - /* connected jack ? */ - if (widget->id == snd_soc_dapm_mic || - (widget->id == snd_soc_dapm_line && - !list_empty(&widget->sinks))) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - - /* signal generator */ - if (widget->id == snd_soc_dapm_siggen) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } + if (widget->is_source && widget->connected) { + widget->inputs = snd_soc_dapm_suspend_check(widget); + return widget->inputs; } list_for_each_entry(path, &widget->sources, list_sink) { @@ -1554,18 +1480,11 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, list_for_each_entry(path, &w->sources, list_sink) dapm_widget_set_peer_power(path->source, power, path->connect); - switch (w->id) { - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_kcontrol: - /* Supplies can't affect their outputs, only their inputs */ - break; - default: + /* Supplies can't affect their outputs, only their inputs */ + if (!w->is_supply) { list_for_each_entry(path, &w->sinks, list_source) dapm_widget_set_peer_power(path->sink, power, path->connect); - break; } if (power) @@ -2226,6 +2145,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); +/* + * dapm_update_widget_flags() - Re-compute widget sink and source flags + * @w: The widget for which to update the flags + * + * Some widgets have a dynamic category which depends on which neighbors they + * are connected to. This function update the category for these widgets. + * + * This function must be called whenever a path is added or removed to a widget. + */ +static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + + switch (w->id) { + case snd_soc_dapm_input: + w->is_source = 1; + list_for_each_entry(p, &w->sources, list_sink) { + if (p->source->id == snd_soc_dapm_micbias || + p->source->id == snd_soc_dapm_mic || + p->source->id == snd_soc_dapm_line || + p->source->id == snd_soc_dapm_output) { + w->is_source = 0; + break; + } + } + break; + case snd_soc_dapm_output: + w->is_sink = 1; + list_for_each_entry(p, &w->sinks, list_source) { + if (p->sink->id == snd_soc_dapm_spk || + p->sink->id == snd_soc_dapm_hp || + p->sink->id == snd_soc_dapm_line || + p->sink->id == snd_soc_dapm_input) { + w->is_sink = 0; + break; + } + } + break; + case snd_soc_dapm_line: + w->is_sink = !list_empty(&w->sources); + w->is_source = !list_empty(&w->sinks); + break; + default: + break; + } +} + static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, const char *control, @@ -2247,22 +2213,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); - /* check for external widgets */ - if (wsink->id == snd_soc_dapm_input) { - if (wsource->id == snd_soc_dapm_micbias || - wsource->id == snd_soc_dapm_mic || - wsource->id == snd_soc_dapm_line || - wsource->id == snd_soc_dapm_output) - wsink->ext = 1; - } - if (wsource->id == snd_soc_dapm_output) { - if (wsink->id == snd_soc_dapm_spk || - wsink->id == snd_soc_dapm_hp || - wsink->id == snd_soc_dapm_line || - wsink->id == snd_soc_dapm_input) - wsource->ext = 1; - } - /* connect static paths */ if (control == NULL) { path->connect = 1; @@ -2294,6 +2244,9 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); + dapm_update_widget_flags(wsource); + dapm_update_widget_flags(wsink); + dapm_mark_dirty(wsource, "Route added"); dapm_mark_dirty(wsink, "Route added"); @@ -2377,6 +2330,7 @@ err: static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { + struct snd_soc_dapm_widget *wsource, *wsink; struct snd_soc_dapm_path *path, *p; const char *sink; const char *source; @@ -2414,10 +2368,17 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, } if (path) { - dapm_mark_dirty(path->source, "Route removed"); - dapm_mark_dirty(path->sink, "Route removed"); + wsource = path->source; + wsink = path->sink; + + dapm_mark_dirty(wsource, "Route removed"); + dapm_mark_dirty(wsink, "Route removed"); dapm_free_path(path); + + /* Update any path related flags */ + dapm_update_widget_flags(wsource); + dapm_update_widget_flags(wsink); } else { dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n", source, sink); @@ -2975,26 +2936,33 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, } switch (w->id) { - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: + case snd_soc_dapm_mic: + case snd_soc_dapm_input: + w->is_source = 1; w->power_check = dapm_generic_check_power; break; - case snd_soc_dapm_mux: + case snd_soc_dapm_spk: + case snd_soc_dapm_hp: + case snd_soc_dapm_output: + w->is_sink = 1; w->power_check = dapm_generic_check_power; break; + case snd_soc_dapm_vmid: + case snd_soc_dapm_siggen: + w->is_source = 1; + w->power_check = dapm_always_on_check_power; + break; + case snd_soc_dapm_mux: + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_adc: case snd_soc_dapm_aif_out: case snd_soc_dapm_dac: case snd_soc_dapm_aif_in: case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: - case snd_soc_dapm_input: - case snd_soc_dapm_output: case snd_soc_dapm_micbias: - case snd_soc_dapm_spk: - case snd_soc_dapm_hp: - case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_dai_link: case snd_soc_dapm_dai_out: @@ -3005,6 +2973,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: case snd_soc_dapm_kcontrol: + w->is_supply = 1; w->power_check = dapm_supply_check_power; break; default: @@ -3368,6 +3337,11 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: break; } + + if (w->id == snd_soc_dapm_dai_in) + w->is_source = w->active; + else + w->is_sink = w->active; } } -- cgit v0.10.2 From c1862c8bae520a8986dd7c47ce33f16eb7c791c2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:42:00 +0200 Subject: ASoC: dapm: Add a flag to mark paths connected to supply widgets Supply widgets do not count towards the input and output widgets of their neighbors and for supply widgets themselves we do not care for the number of input or output paths. This means that a path that connects to a supply widget effectively behaves the same as a path that as the weak property set. This patch adds a new path flag that gets set to true when the path is connected to at least one supply widget. If a path with the flag set is encountered in is_connected_{input,output}_ep() is is skipped in the same way that weak paths are skipped. This slightly brings down the number of path checks. Since both the weak and the supply flag are implemented as bitfields which are stored in the same word there is no runtime overhead due to checking both rather than just one and also the size of the path struct is not increased by this patch. Another advantage is that we do not have to handle supply widgets in is_connected_{input,output}_ep() anymore since it will never be called for supply widgets. The only exception is from dapm_widget_power_read_file() where a check is added to special case supply widgets. Testing with the ADAU1761, which has a handful of supply widgets, shows the following changes in the DAPM stats for a playback stream start. Power Path Neighbour Before: 34 78 117 After: 34 48 117 Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8cf3aad..e7ebeb7 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -510,6 +510,7 @@ struct snd_soc_dapm_path { u32 connect:1; /* source and sink widgets are connected */ u32 walking:1; /* path is in the process of being walked */ u32 weak:1; /* path ignored for power management */ + u32 is_supply:1; /* At least one of the connected widgets is a supply */ int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2cad5f7..d89be15 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -821,9 +821,6 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); - if (widget->is_supply) - return 0; - if (widget->is_sink && widget->connected) { widget->outputs = snd_soc_dapm_suspend_check(widget); return widget->outputs; @@ -832,7 +829,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, list_for_each_entry(path, &widget->sinks, list_source) { DAPM_UPDATE_STAT(widget, neighbour_checks); - if (path->weak) + if (path->weak || path->is_supply) continue; if (path->walking) @@ -882,9 +879,6 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); - if (widget->is_supply) - return 0; - if (widget->is_source && widget->connected) { widget->inputs = snd_soc_dapm_suspend_check(widget); return widget->inputs; @@ -893,7 +887,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, list_for_each_entry(path, &widget->sources, list_sink) { DAPM_UPDATE_STAT(widget, neighbour_checks); - if (path->weak) + if (path->weak || path->is_supply) continue; if (path->walking) @@ -1691,8 +1685,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (!buf) return -ENOMEM; - in = is_connected_input_ep(w, NULL); - out = is_connected_output_ep(w, NULL); + /* Supply widgets are not handled by is_connected_{input,output}_ep() */ + if (w->is_supply) { + in = 0; + out = 0; + } else { + in = is_connected_input_ep(w, NULL); + out = is_connected_output_ep(w, NULL); + } ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", w->name, w->power ? "On" : "Off", @@ -2213,6 +2213,9 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); + if (wsource->is_supply || wsink->is_supply) + path->is_supply = 1; + /* connect static paths */ if (control == NULL) { path->connect = 1; -- cgit v0.10.2 From 8be4da29cf5b8ec65e974c36e7ae4d90b381ac5e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:42:01 +0200 Subject: ASoC: dapm: Mark endpoints instead of IO widgets dirty during suspend/resume The state of endpoint widgets is affected by that card's power state. Endpoint widgets that do no have the ignore_suspend flag set will be considered inactive during suspend. So they have to be re-checked and marked dirty after the card's power state changes. Currently the input and output widgets are marked dirty instead, this works most of the time since typically a path from one endpoint to another will go via a input or output widget. But marking the endpoints dirty is technically more correct and will also work for odd corner cases. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e7ebeb7..43ca165 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -435,7 +435,7 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card); unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); /* Mostly internal - should not normally be used */ -void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); +void dapm_mark_endpoints_dirty(struct snd_soc_card *card); /* dapm path query */ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a2..443be00 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -629,8 +629,8 @@ int snd_soc_suspend(struct device *dev) SND_SOC_DAPM_STREAM_SUSPEND); } - /* Recheck all analogue paths too */ - dapm_mark_io_dirty(&card->dapm); + /* Recheck all endpoints too, their state is affected by suspend */ + dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* suspend all CODECs */ @@ -796,8 +796,8 @@ static void soc_resume_deferred(struct work_struct *work) /* userspace can access us now we are back as we were before */ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); - /* Recheck all analogue paths too */ - dapm_mark_io_dirty(&card->dapm); + /* Recheck all endpoints too, their state is affected by suspend */ + dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d89be15..ffcda7e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -159,27 +159,20 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) } } -void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm) +void dapm_mark_endpoints_dirty(struct snd_soc_card *card) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; mutex_lock(&card->dapm_mutex); list_for_each_entry(w, &card->widgets, list) { - switch (w->id) { - case snd_soc_dapm_input: - case snd_soc_dapm_output: - dapm_mark_dirty(w, "Rechecking inputs and outputs"); - break; - default: - break; - } + if (w->is_sink || w->is_source) + dapm_mark_dirty(w, "Rechecking endpoints"); } mutex_unlock(&card->dapm_mutex); } -EXPORT_SYMBOL_GPL(dapm_mark_io_dirty); +EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); /* create a new dapm widget */ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( -- cgit v0.10.2 From e409dfbfccf9a49409197afc677a21e1c11ba015 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:42:02 +0200 Subject: ASoC: dapm: Add a few supply widget sanity checks Supply widgets are somewhat special and not all kinds of paths to or from supply widgets make sense. This patch adds a few sanity checks that errors out during the path instantiation for those invalid paths. This will prevent drivers to depend on weird behavior resulting from such paths as well as will allow the DAPM algorithms to assume that they never see such paths. This patch adds checks for the following three invalid types of paths: * A path with a non-supply widget as a source connected to a supply widget as a sink. Such a path has no effect on either of the two connected widgets. * Paths with a connected() callback that have a non-supply widget as the source. The DAPM algorithm only uses the conneceted() callback for supply widget power checks. And since it prevents caching of the DAPM state there is no intention to make it more generic as it has negative performance implications. * Paths which connect a supply to a mixer or mux via a control. Controls are only meant to affect the routing of audio data. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index ffcda7e..8e26c2b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2194,6 +2194,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_path *path; int ret; + if (wsink->is_supply && !wsource->is_supply) { + dev_err(dapm->dev, + "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (connected && !wsource->is_supply) { + dev_err(dapm->dev, + "connected() callback only supported for supply widgets (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (wsource->is_supply && control) { + dev_err(dapm->dev, + "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", + wsource->name, control, wsink->name); + return -EINVAL; + } + path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); if (!path) return -ENOMEM; -- cgit v0.10.2 From 92a99ea439c4e27fc6e32eb6d51c5d091c6084bd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 17:42:03 +0200 Subject: ASoC: dapm: Use more aggressive caching Currently we cache the number of input and output paths going to/from a widget only within a power update sequence. But not in between power update sequences. But we know how changes to the DAPM graph affect the number of input (form a source) and output (to a sink) paths of a widget and only need to recalculate them if a operation has been performed that might have changed them. * Adding/removing or connecting/disconnecting a path means that the for the source of the path the number of output paths can change and for the sink the number of input paths can change. * Connecting/disconnecting a widget has the same effect has connecting/ disconnecting all paths of the widget. So for the widget itself the number of inputs and outputs can change, for all sinks of the widget the number of inputs can change and for all sources of the widget the number of outputs can change. * Activating/Deactivating a stream can either change the number of outputs on the sources of the widget associated with the stream or the number of inputs on the sinks. Instead of always invalidating all cached numbers of input and output paths for each power up or down sequence this patch restructures the code to only invalidate the cached numbers when a operation that might change them has been performed. This can greatly reduce the number of DAPM power checks for some very common operations. Since per DAPM operation typically only either change the number of inputs or outputs the number of path checks is reduced by at least 50%. The number of neighbor checks is also reduced about the same percentage, but since the number of neighbors encountered when walking from sink to source is not the same as when walking from source to sink the actual numbers will slightly vary from card to card (e.g. for a mixer we see 1 neighbor when walking from source to sink, but the number of inputs neighbors when walking from source to sink). Bigger improvements can be observed for widgets with multiple connected inputs and output (e.g. mixers probably being the most widespread form of this). Previously we had to re-calculate the number of inputs and outputs on all input and output paths. With this change we only have to re-calculate the number of outputs on the input path that got changed and the number of inputs on the output paths. E.g. imagine the following example: A --> B ----. v M --> N --> Z <-- S <-- R | v X Widget Z has multiple input paths, if any change was made that cause Z to be marked as dirty the power state of Z has to be re-computed. This requires to know the number of inputs and outputs of Z, which requires to know the number of inputs and outputs of all widgets on all paths from or to Z. Previously this meant re-computing all inputs and outputs of all the path going into or out of Z. With this patch in place only paths that actually have changed need to be re-computed. If the system is idle (or the part of the system affected by the changed path) the number of path checks drops to either 0 or 1, regardless of how large or complex the DAPM context is. 0 if there is no connected sink and no connected source. 1 if there is either a connected source or sink, but not both. The number of neighbor checks again will scale accordingly and will be a constant number that is the number of inputs or outputs of the widget for which we did the path check. When loading a state file or switching between different profiles typically multiple mixer and mux settings are changed, so we see the benefit of this patch multiplied for these kinds of operations. Testing with the ADAU1761 shows the following changes in DAPM stats for changing a single Mixer switch for a Mixer with 5 inputs while the DAPM context is idle. Power Path Neighbour Before: 2 12 30 After: 2 1 2 For the same switch, but with a active playback stream the stat changed are as follows. Power Path Neighbour Before: 10 20 54 After: 10 7 21 Cumulative numbers for switching the audio profile which changes 7 controls while the system is idle: Power Path Neighbour Before: 16 80 170 After: 16 7 23 Cumulative numbers for switching the audio profile which changes 7 controls while playback is active: Power Path Neighbour Before: 51 123 273 After: 51 29 109 Starting (or stopping) the playback stream: Power Path Neighbour Before: 34 34 117 After: 34 17 69 Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 43ca165..89823cf 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -569,6 +569,7 @@ struct snd_soc_dapm_widget { struct list_head sinks; /* used during DAPM updates */ + struct list_head work_list; struct list_head power_list; struct list_head dirty; int inputs; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8e26c2b..6bf2c97 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -159,6 +159,116 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) } } +/* + * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input + * paths + * @w: The widget for which to invalidate the cached number of input paths + * + * The function resets the cached number of inputs for the specified widget and + * all widgets that can be reached via outgoing paths from the widget. + * + * This function must be called if the number of input paths for a widget might + * have changed. E.g. if the source state of a widget changes or a path is added + * or activated with the widget as the sink. + */ +static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_widget *sink; + struct snd_soc_dapm_path *p; + LIST_HEAD(list); + + dapm_assert_locked(w->dapm); + + if (w->inputs == -1) + return; + + w->inputs = -1; + list_add_tail(&w->work_list, &list); + + list_for_each_entry(w, &list, work_list) { + list_for_each_entry(p, &w->sinks, list_source) { + if (p->is_supply || p->weak || !p->connect) + continue; + sink = p->sink; + if (sink->inputs != -1) { + sink->inputs = -1; + list_add_tail(&sink->work_list, &list); + } + } + } +} + +/* + * dapm_widget_invalidate_output_paths() - Invalidate the cached number of + * output paths + * @w: The widget for which to invalidate the cached number of output paths + * + * Resets the cached number of outputs for the specified widget and all widgets + * that can be reached via incoming paths from the widget. + * + * This function must be called if the number of output paths for a widget might + * have changed. E.g. if the sink state of a widget changes or a path is added + * or activated with the widget as the source. + */ +static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_widget *source; + struct snd_soc_dapm_path *p; + LIST_HEAD(list); + + dapm_assert_locked(w->dapm); + + if (w->outputs == -1) + return; + + w->outputs = -1; + list_add_tail(&w->work_list, &list); + + list_for_each_entry(w, &list, work_list) { + list_for_each_entry(p, &w->sources, list_sink) { + if (p->is_supply || p->weak || !p->connect) + continue; + source = p->source; + if (source->outputs != -1) { + source->outputs = -1; + list_add_tail(&source->work_list, &list); + } + } + } +} + +/* + * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs + * for the widgets connected to a path + * @p: The path to invalidate + * + * Resets the cached number of inputs for the sink of the path and the cached + * number of outputs for the source of the path. + * + * This function must be called when a path is added, removed or the connected + * state changes. + */ +static void dapm_path_invalidate(struct snd_soc_dapm_path *p) +{ + /* + * Weak paths or supply paths do not influence the number of input or + * output paths of their neighbors. + */ + if (p->weak || p->is_supply) + return; + + /* + * The number of connected endpoints is the sum of the number of + * connected endpoints of all neighbors. If a node with 0 connected + * endpoints is either connected or disconnected that sum won't change, + * so there is no need to re-check the path. + */ + if (p->source->inputs != 0) + dapm_widget_invalidate_input_paths(p->sink); + if (p->sink->outputs != 0) + dapm_widget_invalidate_output_paths(p->source); +} + void dapm_mark_endpoints_dirty(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -166,8 +276,13 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) mutex_lock(&card->dapm_mutex); list_for_each_entry(w, &card->widgets, list) { - if (w->is_sink || w->is_source) + if (w->is_sink || w->is_source) { dapm_mark_dirty(w, "Rechecking endpoints"); + if (w->is_sink) + dapm_widget_invalidate_output_paths(w); + if (w->is_source) + dapm_widget_invalidate_input_paths(w); + } } mutex_unlock(&card->dapm_mutex); @@ -379,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card) list_for_each_entry(w, &card->widgets, list) { w->new_power = w->power; w->power_checked = false; - w->inputs = -1; - w->outputs = -1; } } @@ -931,10 +1044,19 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list) { struct snd_soc_card *card = dai->card; + struct snd_soc_dapm_widget *w; int paths; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm_reset(card); + + /* + * For is_connected_{output,input}_ep fully discover the graph we need + * to reset the cached number of inputs and outputs. + */ + list_for_each_entry(w, &card->widgets, list) { + w->inputs = -1; + w->outputs = -1; + } if (stream == SNDRV_PCM_STREAM_PLAYBACK) paths = is_connected_output_ep(dai->playback_widget, list); @@ -1846,6 +1968,7 @@ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, path->connect = connect; dapm_mark_dirty(path->source, reason); dapm_mark_dirty(path->sink, reason); + dapm_path_invalidate(path); } /* test and update the power status of a mux widget */ @@ -2084,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, return -EINVAL; } - if (w->connected != status) + if (w->connected != status) { dapm_mark_dirty(w, "pin configuration"); + dapm_widget_invalidate_input_paths(w); + dapm_widget_invalidate_output_paths(w); + } w->connected = status; if (status == 0) @@ -2267,6 +2393,9 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, dapm_mark_dirty(wsource, "Route added"); dapm_mark_dirty(wsink, "Route added"); + if (dapm->card->instantiated && path->connect) + dapm_path_invalidate(path); + return 0; err: kfree(path); @@ -2390,6 +2519,8 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, dapm_mark_dirty(wsource, "Route removed"); dapm_mark_dirty(wsink, "Route removed"); + if (path->connect) + dapm_path_invalidate(path); dapm_free_path(path); @@ -3007,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, INIT_LIST_HEAD(&w->dirty); list_add(&w->list, &dapm->card->widgets); + w->inputs = -1; + w->outputs = -1; + /* machine layer set ups unconnected pins and insertions */ w->connected = 1; return w; @@ -3355,10 +3489,13 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, break; } - if (w->id == snd_soc_dapm_dai_in) + if (w->id == snd_soc_dapm_dai_in) { w->is_source = w->active; - else + dapm_widget_invalidate_input_paths(w); + } else { w->is_sink = w->active; + dapm_widget_invalidate_output_paths(w); + } } } @@ -3485,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, } dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin); - w->connected = 1; + if (!w->connected) { + /* + * w->force does not affect the number of input or output paths, + * so we only have to recheck if w->connected is changed + */ + dapm_widget_invalidate_input_paths(w); + dapm_widget_invalidate_output_paths(w); + w->connected = 1; + } w->force = 1; dapm_mark_dirty(w, "force enable"); -- cgit v0.10.2 From c1b4d1c7774189002bc08766ec10e339dfbc98d6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Oct 2014 20:25:56 +0200 Subject: ASoC: Use generic control handlers for S8 control Commit f227b88f0fce ("ASoC: core: Add signed register volume control logic") added support for signed control to the generic volsw control handler. This makes it possible to use them for the S8 control as well, rather than having to use a custom control handler implementation. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ba7130..ad47e96 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -36,6 +36,11 @@ {.reg = xreg, .rreg = xreg, .shift = shift_left, \ .rshift = shift_right, .max = xmax, .platform_max = xmax, \ .invert = xinvert, .autodisable = xautodisable}) +#define SOC_DOUBLE_S_VALUE(xreg, shift_left, shift_right, xmin, xmax, xsign_bit, xinvert, xautodisable) \ + ((unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xreg, .shift = shift_left, \ + .rshift = shift_right, .min = xmin, .max = xmax, .platform_max = xmax, \ + .sign_bit = xsign_bit, .invert = xinvert, .autodisable = xautodisable}) #define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \ SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable) #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \ @@ -171,11 +176,9 @@ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_READWRITE, \ .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \ - .put = snd_soc_put_volsw_s8, \ - .private_value = (unsigned long)&(struct soc_mixer_control) \ - {.reg = xreg, .min = xmin, .max = xmax, \ - .platform_max = xmax} } + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = snd_soc_put_volsw, \ + .private_value = SOC_DOUBLE_S_VALUE(xreg, 0, 8, xmin, xmax, 7, 0, 0) } #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \ { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ .items = xitems, .texts = xtexts, \ @@ -545,12 +548,6 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 96ecdc3..47c378a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2721,93 +2721,6 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); /** - * snd_soc_info_volsw_s8 - signed mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a signed mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int platform_max; - int min = mc->min; - - if (!mc->platform_max) - mc->platform_max = mc->max; - platform_max = mc->platform_max; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max - min; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8); - -/** - * snd_soc_get_volsw_s8 - signed mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a signed mixer control. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - unsigned int val; - int min = mc->min; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = - ((signed char)(val & 0xff))-min; - ucontrol->value.integer.value[1] = - ((signed char)((val >> 8) & 0xff))-min; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8); - -/** - * snd_soc_put_volsw_sgn - signed mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a signed mixer control. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - int min = mc->min; - unsigned int val; - - val = (ucontrol->value.integer.value[0]+min) & 0xff; - val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; - - return snd_soc_component_update_bits(component, reg, 0xffff, val); -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); - -/** * snd_soc_info_volsw_range - single mixer info callback with range. * @kcontrol: mixer control * @uinfo: control element information -- cgit v0.10.2 From 728384a10b673dbd9819b524852df23142ee2fdd Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Fri, 24 Oct 2014 21:50:04 +0400 Subject: ARM: pxa: spitz: register spitz-audio device Register spitz-audio device to be used by ASoC driver to bind ASoC platform driver. Currently old 'soc-audio' approach is used, which needs to be replaced with proper device. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Mark Brown diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 840c3a4..962a7f3 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -924,6 +924,14 @@ static inline void spitz_i2c_init(void) {} #endif /****************************************************************************** + * Audio devices + ******************************************************************************/ +static inline void spitz_audio_init(void) +{ + platform_device_register_simple("spitz-audio", -1, NULL, 0); +} + +/****************************************************************************** * Machine init ******************************************************************************/ static void spitz_poweroff(void) @@ -970,6 +978,7 @@ static void __init spitz_init(void) spitz_nor_init(); spitz_nand_init(); spitz_i2c_init(); + spitz_audio_init(); } static void __init spitz_fixup(struct tag *tags, char **cmdline) -- cgit v0.10.2 From ecf0015161b2220879fcd41c030761b4eb561b95 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Fri, 24 Oct 2014 21:50:05 +0400 Subject: ASoC: pxa: Convert spitz to use snd_soc_register_card() Use snd_soc_register_card() instead of creating a "soc-audio" platform device. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 1373b01..d7d5fb2 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -305,19 +305,15 @@ static struct snd_soc_card snd_soc_spitz = { .num_dapm_routes = ARRAY_SIZE(spitz_audio_map), }; -static struct platform_device *spitz_snd_device; - -static int __init spitz_init(void) +static int spitz_probe(struct platform_device *pdev) { + struct snd_soc_card *card = &snd_soc_spitz; int ret; - if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) - return -ENODEV; - - if (machine_is_borzoi() || machine_is_spitz()) - spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS; - else + if (machine_is_akita()) spitz_mic_gpio = AKITA_GPIO_MIC_BIAS; + else + spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS; ret = gpio_request(spitz_mic_gpio, "MIC GPIO"); if (ret) @@ -327,37 +323,45 @@ static int __init spitz_init(void) if (ret) goto err2; - spitz_snd_device = platform_device_alloc("soc-audio", -1); - if (!spitz_snd_device) { - ret = -ENOMEM; + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); goto err2; } - platform_set_drvdata(spitz_snd_device, &snd_soc_spitz); - - ret = platform_device_add(spitz_snd_device); - if (ret) - goto err3; - return 0; -err3: - platform_device_put(spitz_snd_device); err2: gpio_free(spitz_mic_gpio); err1: return ret; } -static void __exit spitz_exit(void) +static int spitz_remove(struct platform_device *pdev) { - platform_device_unregister(spitz_snd_device); + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); gpio_free(spitz_mic_gpio); + return 0; } -module_init(spitz_init); -module_exit(spitz_exit); +static struct platform_driver spitz_driver = { + .driver = { + .name = "spitz-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = spitz_probe, + .remove = spitz_remove, +}; + +module_platform_driver(spitz_driver); MODULE_AUTHOR("Richard Purdie"); MODULE_DESCRIPTION("ALSA SoC Spitz"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:spitz-audio"); -- cgit v0.10.2 From 3f7256fe5fc64132a2dd19695255c990aa2188cf Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 24 Oct 2014 13:01:25 -0200 Subject: ASoC: sgtl5000: Use the preferred form for passing a size of a struct According to Documentation/CodingStyle - Chapter 14: "The preferred form for passing a size of a struct is the following: p = kmalloc(sizeof(*p), ...); The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed to a memory allocator is not." So do it as recommeded. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 59336f6..490404c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1439,8 +1439,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, struct device_node *np = client->dev.of_node; u32 value; - sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), - GFP_KERNEL); + sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL); if (!sgtl5000) return -ENOMEM; -- cgit v0.10.2 From 54ec2d5f3f751ddcbf07b0fc1e5f01e43015e8e0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 24 Oct 2014 13:01:26 -0200 Subject: ASoC: wm8962: Use the preferred form for passing a size of a struct According to Documentation/CodingStyle - Chapter 14: "The preferred form for passing a size of a struct is the following: p = kmalloc(sizeof(*p), ...); The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed to a memory allocator is not." So do it as recommeded. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 9077411..cfd3891 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3552,8 +3552,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, unsigned int reg; int ret, i, irq_pol, trigger; - wm8962 = devm_kzalloc(&i2c->dev, sizeof(struct wm8962_priv), - GFP_KERNEL); + wm8962 = devm_kzalloc(&i2c->dev, sizeof(*wm8962), GFP_KERNEL); if (wm8962 == NULL) return -ENOMEM; -- cgit v0.10.2 From cea82d8af3986508a05949cfbb6ad8e99ffc15eb Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 24 Oct 2014 13:01:27 -0200 Subject: ASoC: wm8731: Use the preferred form for passing a size of a struct According to Documentation/CodingStyle - Chapter 14: "The preferred form for passing a size of a struct is the following: p = kmalloc(sizeof(*p), ...); The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed to a memory allocator is not." So do it as recommeded. Cc: Charles Keepax Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index eebb328..2c9f2a7 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -680,8 +680,7 @@ static int wm8731_spi_probe(struct spi_device *spi) struct wm8731_priv *wm8731; int ret; - wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv), - GFP_KERNEL); + wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL); if (wm8731 == NULL) return -ENOMEM; -- cgit v0.10.2 From 41282920aa35033a4fcf2bc68aeba42a037e6c4d Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 24 Oct 2014 16:48:11 -0700 Subject: ASoC: fsl-asoc-card: Don't bypass settings if cpu-dai is Master When cpu-dai is the DAI Master (CBM_CFx), it may need some configurations, set_sysclk() call for eample, for cpu-dai side in the hw_params(), even if the set_bias_level() has already taken care of the codec-dai side. So this patch just simply adds an additional condition. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 007c772..14572e6 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -125,7 +125,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, priv->sample_rate = params_rate(params); priv->sample_format = params_format(params); - if (priv->card.set_bias_level) + /* + * If codec-dai is DAI Master and all configurations are already in the + * set_bias_level(), bypass the remaining settings in hw_params(). + * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS. + */ + if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) return 0; /* Specific configurations of DAIs starts from here */ -- cgit v0.10.2 From 1b5721b24306c2daf786f3b31f678b40ab9b2ce7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 27 Oct 2014 18:04:52 -0700 Subject: ASoC: simple-card: add asoc_simple_card_parse_daifmt() Current daifmt setting method in simple-card driver is placed to many places, and using un-readable/confusable method. This patch adds new asoc_simple_card_parse_daifmt() and tidyup code. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 4f192ee..cac95d7 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -226,6 +226,53 @@ asoc_simple_card_sub_parse_of(struct device_node *np, return 0; } +static int asoc_simple_card_parse_daifmt(struct device_node *node, + struct simple_card_data *priv, + struct device_node *cpu, + struct device_node *codec, + char *prefix, int idx) +{ + struct device *dev = simple_priv_to_dev(priv); + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); + struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; + struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + unsigned int daifmt; + + daifmt = snd_soc_of_parse_daifmt(node, prefix, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + + if (strlen(prefix) && !bitclkmaster && !framemaster) { + /* + * No dai-link level and master setting was not found from + * sound node level, revert back to legacy DT parsing and + * take the settings from codec node. + */ + dev_dbg(dev, "Revert to legacy daifmt parsing\n"); + + cpu_dai->fmt = codec_dai->fmt = + snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | + (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); + } else { + if (codec == bitclkmaster) + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; + else + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; + + cpu_dai->fmt = daifmt; + codec_dai->fmt = daifmt; + } + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return 0; +} + static int asoc_simple_card_dai_link_of(struct device_node *node, struct simple_card_data *priv, int idx, @@ -234,10 +281,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, struct device *dev = simple_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct device_node *np = NULL; - struct device_node *bitclkmaster = NULL; - struct device_node *framemaster = NULL; - unsigned int daifmt; + struct device_node *cpu = NULL; + struct device_node *codec = NULL; char *name; char prop[128]; char *prefix = ""; @@ -247,85 +292,36 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (is_top_level_node) prefix = "simple-audio-card,"; - daifmt = snd_soc_of_parse_daifmt(node, prefix, - &bitclkmaster, &framemaster); - daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - snprintf(prop, sizeof(prop), "%scpu", prefix); - np = of_get_child_by_name(node, prop); - if (!np) { + cpu = of_get_child_by_name(node, prop); + + snprintf(prop, sizeof(prop), "%scodec", prefix); + codec = of_get_child_by_name(node, prop); + + if (!cpu || !codec) { ret = -EINVAL; dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); goto dai_link_of_err; } - ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai, + ret = asoc_simple_card_parse_daifmt(node, priv, + cpu, codec, prefix, idx); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai, &dai_link->cpu_of_node, &dai_link->cpu_dai_name, &cpu_args); if (ret < 0) goto dai_link_of_err; - dai_props->cpu_dai.fmt = daifmt; - switch (((np == bitclkmaster) << 4) | (np == framemaster)) { - case 0x11: - dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; - break; - case 0x10: - dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; - break; - case 0x01: - dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; - break; - default: - dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; - break; - } - - of_node_put(np); - snprintf(prop, sizeof(prop), "%scodec", prefix); - np = of_get_child_by_name(node, prop); - if (!np) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } - - ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai, + ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai, &dai_link->codec_of_node, &dai_link->codec_dai_name, NULL); if (ret < 0) goto dai_link_of_err; - if (strlen(prefix) && !bitclkmaster && !framemaster) { - /* - * No DAI link level and master setting was found - * from sound node level, revert back to legacy DT - * parsing and take the settings from codec node. - */ - dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n", - __func__); - dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt = - snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) | - (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); - } else { - dai_props->codec_dai.fmt = daifmt; - switch (((np == bitclkmaster) << 4) | (np == framemaster)) { - case 0x11: - dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; - break; - case 0x10: - dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; - break; - case 0x01: - dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; - break; - default: - dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; - break; - } - } - if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { ret = -EINVAL; goto dai_link_of_err; @@ -368,9 +364,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_link->cpu_dai_name = NULL; dai_link_of_err: - of_node_put(np); - of_node_put(bitclkmaster); - of_node_put(framemaster); + of_node_put(cpu); + of_node_put(codec); + return ret; } -- cgit v0.10.2 From e9600bc166d529cf03862afae51fb2e3cf987d02 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 28 Oct 2014 17:37:12 +0000 Subject: ASoC: Intel: Make ADSP memory block allocation more generic Current block allocation is tied to block type and requestor type. Make the allocation more generic by removing the struct module parameter and adding a generic block allocator structure. Also pass in the list that the blocks have to be added too in order to remove dependence on block requestor type. ASoC: Intel: update scratch allocator to use generic block allocator Update the scratch allocator to use the generic block allocator and calculate total scratch buffer size. ASoC: Intel: Add call to calculate offsets internally within the DSP. A call to calculate internal DSP memory addresses used to allocate persistent and scartch buffers. ASoC: Intel: Add runtime module support. Add support for runtime module objects that can be created for every FW module that is parsed from the FW file. This gives a 1:N mapping between the FW module from file and the runtime instantiations of that module. We also need to make sure we remove every module and runtime module when we unload the FW. ASoC: Intel: Add DMA firmware loading support Add support for DMA to load firmware modules to the DSP memory blocks. Two DMA engines are supported, DesignWare and Intel MID. ASoC: Intel: Add runtime module lookup API call Add an API to allow quick lookup of runtime modules based on ID. ASoC: Intel: Provide streams with dynamic module information Remove the hard coded module paramaters and provide each module with dynamically generated buffer information for scratch and persistent buffers. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c index fc58876..5a9e567 100644 --- a/sound/soc/intel/sst-baytrail-dsp.c +++ b/sound/soc/intel/sst-baytrail-dsp.c @@ -67,17 +67,12 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, { struct dma_block_info *block; struct sst_module *mod; - struct sst_module_data block_data; struct sst_module_template template; int count; memset(&template, 0, sizeof(template)); template.id = module->type; template.entry = module->entry_point; - template.p.type = SST_MEM_DRAM; - template.p.data_type = SST_DATA_P; - template.s.type = SST_MEM_DRAM; - template.s.data_type = SST_DATA_S; mod = sst_module_new(fw, &template, NULL); if (mod == NULL) @@ -94,19 +89,19 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, switch (block->type) { case SST_BYT_IRAM: - block_data.offset = block->ram_offset + + mod->offset = block->ram_offset + dsp->addr.iram_offset; - block_data.type = SST_MEM_IRAM; + mod->type = SST_MEM_IRAM; break; case SST_BYT_DRAM: - block_data.offset = block->ram_offset + + mod->offset = block->ram_offset + dsp->addr.dram_offset; - block_data.type = SST_MEM_DRAM; + mod->type = SST_MEM_DRAM; break; case SST_BYT_CACHE: - block_data.offset = block->ram_offset + + mod->offset = block->ram_offset + (dsp->addr.fw_ext - dsp->addr.lpe); - block_data.type = SST_MEM_CACHE; + mod->type = SST_MEM_CACHE; break; default: dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n", @@ -114,11 +109,10 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, return -EINVAL; } - block_data.size = block->size; - block_data.data_type = SST_DATA_M; - block_data.data = (void *)block + sizeof(*block); + mod->size = block->size; + mod->data = (void *)block + sizeof(*block); - sst_module_insert_fixed_block(mod, &block_data); + sst_module_alloc_blocks(mod); block = (void *)block + sizeof(*block) + block->size; } diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h index ffb308b..be81b86 100644 --- a/sound/soc/intel/sst-dsp-priv.h +++ b/sound/soc/intel/sst-dsp-priv.h @@ -26,6 +26,9 @@ struct sst_mem_block; struct sst_module; struct sst_fw; +/* do we need to remove or keep */ +#define DSP_DRAM_ADDR_OFFSET 0x400000 + /* * DSP Operations exported by platform Audio DSP driver. */ @@ -67,6 +70,8 @@ struct sst_addr { u32 shim_offset; u32 iram_offset; u32 dram_offset; + u32 dsp_iram_offset; + u32 dsp_dram_offset; void __iomem *lpe; void __iomem *shim; void __iomem *pci_cfg; @@ -84,15 +89,6 @@ struct sst_mailbox { }; /* - * Audio DSP Firmware data types. - */ -enum sst_data_type { - SST_DATA_M = 0, /* module block data */ - SST_DATA_P = 1, /* peristant data (text, data) */ - SST_DATA_S = 2, /* scratch data (usually buffers) */ -}; - -/* * Audio DSP memory block types. */ enum sst_mem_type { @@ -125,23 +121,6 @@ struct sst_fw { }; /* - * Audio DSP Generic Module data. - * - * This is used to dsecribe any sections of persistent (text and data) and - * scratch (buffers) of module data in ADSP memory space. - */ -struct sst_module_data { - - enum sst_mem_type type; /* destination memory type */ - enum sst_data_type data_type; /* type of module data */ - - u32 size; /* size in bytes */ - int32_t offset; /* offset in FW file */ - u32 data_offset; /* offset in ADSP memory space */ - void *data; /* module data */ -}; - -/* * Audio DSP Generic Module Template. * * Used to define and register a new FW module. This data is extracted from @@ -150,15 +129,52 @@ struct sst_module_data { struct sst_module_template { u32 id; u32 entry; /* entry point */ - struct sst_module_data s; /* scratch data */ - struct sst_module_data p; /* peristant data */ + u32 scratch_size; + u32 persistent_size; +}; + +/* + * Block Allocator - Used to allocate blocks of DSP memory. + */ +struct sst_block_allocator { + u32 id; + u32 offset; + int size; + enum sst_mem_type type; +}; + +/* + * Runtime Module Instance - A module object can be instanciated multiple + * times within the DSP FW. + */ +struct sst_module_runtime { + struct sst_dsp *dsp; + int id; + struct sst_module *module; /* parent module we belong too */ + + u32 persistent_offset; /* private memory offset */ + void *private; + + struct list_head list; + struct list_head block_list; /* list of blocks used */ +}; + +/* + * Runtime Module Context - The runtime context must be manually stored by the + * driver prior to enter S3 and restored after leaving S3. This should really be + * part of the memory context saved by the enter D3 message IPC ??? + */ +struct sst_module_runtime_context { + dma_addr_t dma_buffer; + u32 *buffer; }; /* * Audio DSP Generic Module. * * Each Firmware file can consist of 1..N modules. A module can span multiple - * ADSP memory blocks. The simplest FW will be a file with 1 module. + * ADSP memory blocks. The simplest FW will be a file with 1 module. A module + * can be instanciated multiple times in the DSP. */ struct sst_module { struct sst_dsp *dsp; @@ -167,10 +183,13 @@ struct sst_module { /* module configuration */ u32 id; u32 entry; /* module entry point */ - u32 offset; /* module offset in firmware file */ + s32 offset; /* module offset in firmware file */ u32 size; /* module size */ - struct sst_module_data s; /* scratch data */ - struct sst_module_data p; /* peristant data */ + u32 scratch_size; /* global scratch memory required */ + u32 persistent_size; /* private memory required */ + enum sst_mem_type type; /* destination memory type */ + u32 data_offset; /* offset in ADSP memory space */ + void *data; /* module data */ /* runtime */ u32 usage_count; /* can be unloaded if count == 0 */ @@ -180,6 +199,7 @@ struct sst_module { struct list_head block_list; /* Module list of blocks in use */ struct list_head list; /* DSP list of modules */ struct list_head list_fw; /* FW list of modules */ + struct list_head runtime_list; /* list of runtime module objects*/ }; /* @@ -208,7 +228,6 @@ struct sst_mem_block { struct sst_block_ops *ops; /* block operations, if any */ /* block status */ - enum sst_data_type data_type; /* data type held in this block */ u32 bytes_used; /* bytes in use by modules */ void *private; /* generic core does not touch this */ int users; /* number of modules using this block */ @@ -253,6 +272,11 @@ struct sst_dsp { struct list_head module_list; struct list_head fw_list; + /* scratch buffer */ + struct list_head scratch_block_list; + u32 scratch_offset; + u32 scratch_size; + /* platform data */ struct sst_pdata *pdata; @@ -290,18 +314,33 @@ void sst_fw_unload(struct sst_fw *sst_fw); /* Create/Free firmware modules */ struct sst_module *sst_module_new(struct sst_fw *sst_fw, struct sst_module_template *template, void *private); -void sst_module_free(struct sst_module *sst_module); -int sst_module_insert(struct sst_module *sst_module); -int sst_module_remove(struct sst_module *sst_module); -int sst_module_insert_fixed_block(struct sst_module *module, - struct sst_module_data *data); +void sst_module_free(struct sst_module *module); struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id); - -/* allocate/free pesistent/scratch memory regions managed by drv */ -struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp); -void sst_mem_block_free_scratch(struct sst_dsp *dsp, - struct sst_module *scratch); -int sst_block_module_remove(struct sst_module *module); +int sst_module_alloc_blocks(struct sst_module *module); +int sst_module_free_blocks(struct sst_module *module); + +/* Create/Free firmware module runtime instances */ +struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module, + int id, void *private); +void sst_module_runtime_free(struct sst_module_runtime *runtime); +struct sst_module_runtime *sst_module_runtime_get_from_id( + struct sst_module *module, u32 id); +int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime, + int offset); +int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime); +int sst_module_runtime_save(struct sst_module_runtime *runtime, + struct sst_module_runtime_context *context); +int sst_module_runtime_restore(struct sst_module_runtime *runtime, + struct sst_module_runtime_context *context); + +/* generic block allocation */ +int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba, + struct list_head *block_list); +int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list); + +/* scratch allocation */ +int sst_block_alloc_scratch(struct sst_dsp *dsp); +void sst_block_free_scratch(struct sst_dsp *dsp); /* Register the DSPs memory blocks - would be nice to read from ACPI */ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, @@ -309,4 +348,10 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, void *private); void sst_mem_block_unregister_all(struct sst_dsp *dsp); +/* Create/Free DMA resources */ +int sst_dma_new(struct sst_dsp *sst); +void sst_dma_free(struct sst_dma *dma); + +u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, + enum sst_mem_type type); #endif diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c index cd23060..d0fc685 100644 --- a/sound/soc/intel/sst-dsp.c +++ b/sound/soc/intel/sst-dsp.c @@ -352,6 +352,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev, INIT_LIST_HEAD(&sst->free_block_list); INIT_LIST_HEAD(&sst->module_list); INIT_LIST_HEAD(&sst->fw_list); + INIT_LIST_HEAD(&sst->scratch_block_list); /* Initialise SST Audio DSP */ if (sst->ops->init) { @@ -366,6 +367,10 @@ struct sst_dsp *sst_dsp_new(struct device *dev, if (err) goto irq_err; + err = sst_dma_new(sst); + if (err) + dev_warn(dev, "sst_dma_new failed %d\n", err); + return sst; irq_err: @@ -381,6 +386,9 @@ void sst_dsp_free(struct sst_dsp *sst) free_irq(sst->irq, sst); if (sst->ops->free) sst->ops->free(sst); + + if (sst->dma) + sst_dma_free(sst->dma); } EXPORT_SYMBOL_GPL(sst_dsp_free); diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h index 3165dfa..17ee923 100644 --- a/sound/soc/intel/sst-dsp.h +++ b/sound/soc/intel/sst-dsp.h @@ -245,6 +245,13 @@ void sst_memcpy_fromio_32(struct sst_dsp *sst, /* DSP reset & boot */ void sst_dsp_reset(struct sst_dsp *sst); int sst_dsp_boot(struct sst_dsp *sst); +/* DMA */ +int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id); +void sst_dsp_dma_put_channel(struct sst_dsp *dsp); +int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr, + dma_addr_t src_addr, size_t size); +int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr, + dma_addr_t src_addr, size_t size); /* Msg IO */ void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index cf3d199..692a6ae 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -23,6 +23,11 @@ #include #include #include +#include + +/* supported DMA engine drivers */ +#include +#include #include #include @@ -30,7 +35,20 @@ #include "sst-dsp.h" #include "sst-dsp-priv.h" -static void block_module_remove(struct sst_module *module); +#define SST_DMA_RESOURCES 2 +#define SST_DSP_DMA_MAX_BURST 0x3 +#define SST_HSW_BLOCK_ANY 0xffffffff + +#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000 + +struct sst_dma { + struct sst_dsp *sst; + + struct dw_dma_chip *chip; + + struct dma_async_tx_descriptor *desc; + struct dma_chan *ch; +}; static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) { @@ -38,6 +56,281 @@ static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 byte __iowrite32_copy((void *)dest, src, bytes/4); } +static void sst_dma_transfer_complete(void *arg) +{ + struct sst_dsp *sst = (struct sst_dsp *)arg; + + dev_dbg(sst->dev, "DMA: callback\n"); +} + +static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr, + dma_addr_t src_addr, size_t size) +{ + struct dma_async_tx_descriptor *desc; + struct sst_dma *dma = sst->dma; + + if (dma->ch == NULL) { + dev_err(sst->dev, "error: no DMA channel\n"); + return -ENODEV; + } + + dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n", + (unsigned long)src_addr, (unsigned long)dest_addr, size); + + desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr, + src_addr, size, DMA_CTRL_ACK); + if (!desc){ + dev_err(sst->dev, "error: dma prep memcpy failed\n"); + return -EINVAL; + } + + desc->callback = sst_dma_transfer_complete; + desc->callback_param = sst; + + desc->tx_submit(desc); + dma_wait_for_async_tx(desc); + + return 0; +} + +/* copy to DSP */ +int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr, + dma_addr_t src_addr, size_t size) +{ + return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP, + src_addr, size); +} +EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto); + +/* copy from DSP */ +int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr, + dma_addr_t src_addr, size_t size) +{ + return sst_dsp_dma_copy(sst, dest_addr, + src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size); +} +EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom); + +/* remove module from memory - callers hold locks */ +static void block_list_remove(struct sst_dsp *dsp, + struct list_head *block_list) +{ + struct sst_mem_block *block, *tmp; + int err; + + /* disable each block */ + list_for_each_entry(block, block_list, module_list) { + + if (block->ops && block->ops->disable) { + err = block->ops->disable(block); + if (err < 0) + dev_err(dsp->dev, + "error: cant disable block %d:%d\n", + block->type, block->index); + } + } + + /* mark each block as free */ + list_for_each_entry_safe(block, tmp, block_list, module_list) { + list_del(&block->module_list); + list_move(&block->list, &dsp->free_block_list); + dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n", + block->type, block->index, block->offset); + } +} + +/* prepare the memory block to receive data from host - callers hold locks */ +static int block_list_prepare(struct sst_dsp *dsp, + struct list_head *block_list) +{ + struct sst_mem_block *block; + int ret = 0; + + /* enable each block so that's it'e ready for data */ + list_for_each_entry(block, block_list, module_list) { + + if (block->ops && block->ops->enable) { + ret = block->ops->enable(block); + if (ret < 0) { + dev_err(dsp->dev, + "error: cant disable block %d:%d\n", + block->type, block->index); + goto err; + } + } + } + return ret; + +err: + list_for_each_entry(block, block_list, module_list) { + if (block->ops && block->ops->disable) + block->ops->disable(block); + } + return ret; +} + +struct dw_dma_platform_data dw_pdata = { + .is_private = 1, + .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, + .chan_priority = CHAN_PRIORITY_ASCENDING, +}; + +static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, + int irq) +{ + struct dw_dma_chip *chip; + int err; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return ERR_PTR(-ENOMEM); + + chip->irq = irq; + chip->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(chip->regs)) + return ERR_CAST(chip->regs); + + err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31)); + if (err) + return ERR_PTR(err); + + chip->dev = dev; + err = dw_dma_probe(chip, &dw_pdata); + if (err) + return ERR_PTR(err); + + return chip; +} + +static void dw_remove(struct dw_dma_chip *chip) +{ + dw_dma_remove(chip); +} + +static bool dma_chan_filter(struct dma_chan *chan, void *param) +{ + struct sst_dsp *dsp = (struct sst_dsp *)param; + + return chan->device->dev == dsp->dma_dev; +} + +int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) +{ + struct sst_dma *dma = dsp->dma; + struct dma_slave_config slave; + dma_cap_mask_t mask; + int ret; + + /* The Intel MID DMA engine driver needs the slave config set but + * Synopsis DMA engine driver safely ignores the slave config */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_MEMCPY, mask); + + dma->ch = dma_request_channel(mask, dma_chan_filter, dsp); + if (dma->ch == NULL) { + dev_err(dsp->dev, "error: DMA request channel failed\n"); + return -EIO; + } + + memset(&slave, 0, sizeof(slave)); + slave.direction = DMA_MEM_TO_DEV; + slave.src_addr_width = + slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST; + + ret = dmaengine_slave_config(dma->ch, &slave); + if (ret) { + dev_err(dsp->dev, "error: unable to set DMA slave config %d\n", + ret); + dma_release_channel(dma->ch); + dma->ch = NULL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel); + +void sst_dsp_dma_put_channel(struct sst_dsp *dsp) +{ + struct sst_dma *dma = dsp->dma; + + if (!dma->ch) + return; + + dma_release_channel(dma->ch); + dma->ch = NULL; +} +EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel); + +int sst_dma_new(struct sst_dsp *sst) +{ + struct sst_pdata *sst_pdata = sst->pdata; + struct sst_dma *dma; + struct resource mem; + const char *dma_dev_name; + int ret = 0; + + /* configure the correct platform data for whatever DMA engine + * is attached to the ADSP IP. */ + switch (sst->pdata->dma_engine) { + case SST_DMA_TYPE_DW: + dma_dev_name = "dw_dmac"; + break; + case SST_DMA_TYPE_MID: + dma_dev_name = "Intel MID DMA"; + break; + default: + dev_err(sst->dev, "error: invalid DMA engine %d\n", + sst->pdata->dma_engine); + return -EINVAL; + } + + dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + dma->sst = sst; + + memset(&mem, 0, sizeof(mem)); + + mem.start = sst->addr.lpe_base + sst_pdata->dma_base; + mem.end = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1; + mem.flags = IORESOURCE_MEM; + + /* now register DMA engine device */ + dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq); + if (IS_ERR(dma->chip)) { + dev_err(sst->dev, "error: DMA device register failed\n"); + ret = PTR_ERR(dma->chip); + goto err_dma_dev; + } + + sst->dma = dma; + sst->fw_use_dma = true; + return 0; + +err_dma_dev: + devm_kfree(sst->dev, dma); + return ret; +} +EXPORT_SYMBOL(sst_dma_new); + +void sst_dma_free(struct sst_dma *dma) +{ + + if (dma == NULL) + return; + + if (dma->ch) + dma_release_channel(dma->ch); + + if (dma->chip) + dw_remove(dma->chip); + +} +EXPORT_SYMBOL(sst_dma_free); + /* create new generic firmware object */ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, const struct firmware *fw, void *private) @@ -68,6 +361,12 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, /* copy FW data to DMA-able memory */ memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size); + if (dsp->fw_use_dma) { + err = sst_dsp_dma_get_channel(dsp, 0); + if (err < 0) + goto chan_err; + } + /* call core specific FW paser to load FW data into DSP */ err = dsp->ops->parse_fw(sst_fw); if (err < 0) { @@ -75,6 +374,9 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, goto parse_err; } + if (dsp->fw_use_dma) + sst_dsp_dma_put_channel(dsp); + mutex_lock(&dsp->mutex); list_add(&sst_fw->list, &dsp->fw_list); mutex_unlock(&dsp->mutex); @@ -82,9 +384,13 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, return sst_fw; parse_err: - dma_free_coherent(dsp->dev, sst_fw->size, + if (dsp->fw_use_dma) + sst_dsp_dma_put_channel(dsp); +chan_err: + dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf, sst_fw->dmable_fw_paddr); + sst_fw->dma_buf = NULL; kfree(sst_fw); return NULL; } @@ -108,21 +414,37 @@ EXPORT_SYMBOL_GPL(sst_fw_reload); void sst_fw_unload(struct sst_fw *sst_fw) { - struct sst_dsp *dsp = sst_fw->dsp; - struct sst_module *module, *tmp; + struct sst_dsp *dsp = sst_fw->dsp; + struct sst_module *module, *mtmp; + struct sst_module_runtime *runtime, *rtmp; + + dev_dbg(dsp->dev, "unloading firmware\n"); - dev_dbg(dsp->dev, "unloading firmware\n"); + mutex_lock(&dsp->mutex); + + /* check module by module */ + list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) { + if (module->sst_fw == sst_fw) { + + /* remove runtime modules */ + list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) { + + block_list_remove(dsp, &runtime->block_list); + list_del(&runtime->list); + kfree(runtime); + } + + /* now remove the module */ + block_list_remove(dsp, &module->block_list); + list_del(&module->list); + kfree(module); + } + } - mutex_lock(&dsp->mutex); - list_for_each_entry_safe(module, tmp, &dsp->module_list, list) { - if (module->sst_fw == sst_fw) { - block_module_remove(module); - list_del(&module->list); - kfree(module); - } - } + /* remove all scratch blocks */ + block_list_remove(dsp, &dsp->scratch_block_list); - mutex_unlock(&dsp->mutex); + mutex_unlock(&dsp->mutex); } EXPORT_SYMBOL_GPL(sst_fw_unload); @@ -135,7 +457,8 @@ void sst_fw_free(struct sst_fw *sst_fw) list_del(&sst_fw->list); mutex_unlock(&dsp->mutex); - dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf, + if (sst_fw->dma_buf) + dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf, sst_fw->dmable_fw_paddr); kfree(sst_fw); } @@ -172,11 +495,11 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw, sst_module->id = template->id; sst_module->dsp = dsp; sst_module->sst_fw = sst_fw; - - memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data)); - memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data)); + sst_module->scratch_size = template->scratch_size; + sst_module->persistent_size = template->persistent_size; INIT_LIST_HEAD(&sst_module->block_list); + INIT_LIST_HEAD(&sst_module->runtime_list); mutex_lock(&dsp->mutex); list_add(&sst_module->list, &dsp->module_list); @@ -199,73 +522,122 @@ void sst_module_free(struct sst_module *sst_module) } EXPORT_SYMBOL_GPL(sst_module_free); -static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type, - u32 offset) +struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module, + int id, void *private) +{ + struct sst_dsp *dsp = module->dsp; + struct sst_module_runtime *runtime; + + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (runtime == NULL) + return NULL; + + runtime->id = id; + runtime->dsp = dsp; + runtime->module = module; + INIT_LIST_HEAD(&runtime->block_list); + + mutex_lock(&dsp->mutex); + list_add(&runtime->list, &module->runtime_list); + mutex_unlock(&dsp->mutex); + + return runtime; +} +EXPORT_SYMBOL_GPL(sst_module_runtime_new); + +void sst_module_runtime_free(struct sst_module_runtime *runtime) +{ + struct sst_dsp *dsp = runtime->dsp; + + mutex_lock(&dsp->mutex); + list_del(&runtime->list); + mutex_unlock(&dsp->mutex); + + kfree(runtime); +} +EXPORT_SYMBOL_GPL(sst_module_runtime_free); + +static struct sst_mem_block *find_block(struct sst_dsp *dsp, + struct sst_block_allocator *ba) { struct sst_mem_block *block; list_for_each_entry(block, &dsp->free_block_list, list) { - if (block->type == type && block->offset == offset) + if (block->type == ba->type && block->offset == ba->offset) return block; } return NULL; } -static int block_alloc_contiguous(struct sst_module *module, - struct sst_module_data *data, u32 offset, int size) +/* Block allocator must be on block boundary */ +static int block_alloc_contiguous(struct sst_dsp *dsp, + struct sst_block_allocator *ba, struct list_head *block_list) { struct list_head tmp = LIST_HEAD_INIT(tmp); - struct sst_dsp *dsp = module->dsp; struct sst_mem_block *block; + u32 block_start = SST_HSW_BLOCK_ANY; + int size = ba->size, offset = ba->offset; - while (size > 0) { - block = find_block(dsp, data->type, offset); + while (ba->size > 0) { + + block = find_block(dsp, ba); if (!block) { list_splice(&tmp, &dsp->free_block_list); + + ba->size = size; + ba->offset = offset; return -ENOMEM; } list_move_tail(&block->list, &tmp); - offset += block->size; - size -= block->size; + ba->offset += block->size; + ba->size -= block->size; } + ba->size = size; + ba->offset = offset; + + list_for_each_entry(block, &tmp, list) { + + if (block->offset < block_start) + block_start = block->offset; + + list_add(&block->module_list, block_list); - list_for_each_entry(block, &tmp, list) - list_add(&block->module_list, &module->block_list); + dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", + block->type, block->index, block->offset); + } list_splice(&tmp, &dsp->used_block_list); return 0; } -/* allocate free DSP blocks for module data - callers hold locks */ -static int block_alloc(struct sst_module *module, - struct sst_module_data *data) +/* allocate first free DSP blocks for data - callers hold locks */ +static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba, + struct list_head *block_list) { - struct sst_dsp *dsp = module->dsp; struct sst_mem_block *block, *tmp; int ret = 0; - if (data->size == 0) + if (ba->size == 0) return 0; /* find first free whole blocks that can hold module */ list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { /* ignore blocks with wrong type */ - if (block->type != data->type) + if (block->type != ba->type) continue; - if (data->size > block->size) + if (ba->size > block->size) continue; - data->offset = block->offset; - block->data_type = data->data_type; - block->bytes_used = data->size % block->size; - list_add(&block->module_list, &module->block_list); + ba->offset = block->offset; + block->bytes_used = ba->size % block->size; + list_add(&block->module_list, block_list); list_move(&block->list, &dsp->used_block_list); - dev_dbg(dsp->dev, " *module %d added block %d:%d\n", - module->id, block->type, block->index); + dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", + block->type, block->index, block->offset); return 0; } @@ -273,15 +645,19 @@ static int block_alloc(struct sst_module *module, list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { /* ignore blocks with wrong type */ - if (block->type != data->type) + if (block->type != ba->type) continue; /* do we span > 1 blocks */ - if (data->size > block->size) { - ret = block_alloc_contiguous(module, data, - block->offset, data->size); + if (ba->size > block->size) { + + /* align ba to block boundary */ + ba->offset = block->offset; + + ret = block_alloc_contiguous(dsp, ba, block_list); if (ret == 0) return ret; + } } @@ -289,93 +665,74 @@ static int block_alloc(struct sst_module *module, return -ENOMEM; } -/* remove module from memory - callers hold locks */ -static void block_module_remove(struct sst_module *module) +int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba, + struct list_head *block_list) { - struct sst_mem_block *block, *tmp; - struct sst_dsp *dsp = module->dsp; - int err; + int ret; - /* disable each block */ - list_for_each_entry(block, &module->block_list, module_list) { + dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n", + ba->size, ba->offset, ba->type); - if (block->ops && block->ops->disable) { - err = block->ops->disable(block); - if (err < 0) - dev_err(dsp->dev, - "error: cant disable block %d:%d\n", - block->type, block->index); - } - } + mutex_lock(&dsp->mutex); - /* mark each block as free */ - list_for_each_entry_safe(block, tmp, &module->block_list, module_list) { - list_del(&block->module_list); - list_move(&block->list, &dsp->free_block_list); + ret = block_alloc(dsp, ba, block_list); + if (ret < 0) { + dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret); + goto out; } -} -/* prepare the memory block to receive data from host - callers hold locks */ -static int block_module_prepare(struct sst_module *module) -{ - struct sst_mem_block *block; - int ret = 0; - - /* enable each block so that's it'e ready for module P/S data */ - list_for_each_entry(block, &module->block_list, module_list) { + /* prepare DSP blocks for module usage */ + ret = block_list_prepare(dsp, block_list); + if (ret < 0) + dev_err(dsp->dev, "error: prepare failed\n"); - if (block->ops && block->ops->enable) { - ret = block->ops->enable(block); - if (ret < 0) { - dev_err(module->dsp->dev, - "error: cant disable block %d:%d\n", - block->type, block->index); - goto err; - } - } - } +out: + mutex_unlock(&dsp->mutex); return ret; +} +EXPORT_SYMBOL_GPL(sst_alloc_blocks); -err: - list_for_each_entry(block, &module->block_list, module_list) { - if (block->ops && block->ops->disable) - block->ops->disable(block); - } - return ret; +int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list) +{ + mutex_lock(&dsp->mutex); + block_list_remove(dsp, block_list); + mutex_unlock(&dsp->mutex); + return 0; } +EXPORT_SYMBOL_GPL(sst_free_blocks); /* allocate memory blocks for static module addresses - callers hold locks */ -static int block_alloc_fixed(struct sst_module *module, - struct sst_module_data *data) +static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba, + struct list_head *block_list) { - struct sst_dsp *dsp = module->dsp; struct sst_mem_block *block, *tmp; - u32 end = data->offset + data->size, block_end; + u32 end = ba->offset + ba->size, block_end; int err; /* only IRAM/DRAM blocks are managed */ - if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM) + if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM) return 0; /* are blocks already attached to this module */ - list_for_each_entry_safe(block, tmp, &module->block_list, module_list) { + list_for_each_entry_safe(block, tmp, block_list, module_list) { - /* force compacting mem blocks of the same data_type */ - if (block->data_type != data->data_type) + /* ignore blocks with wrong type */ + if (block->type != ba->type) continue; block_end = block->offset + block->size; /* find block that holds section */ - if (data->offset >= block->offset && end < block_end) + if (ba->offset >= block->offset && end <= block_end) return 0; /* does block span more than 1 section */ - if (data->offset >= block->offset && data->offset < block_end) { + if (ba->offset >= block->offset && ba->offset < block_end) { - err = block_alloc_contiguous(module, data, - block->offset + block->size, - data->size - block->size); + /* align ba to block boundary */ + ba->size -= block_end - ba->offset; + ba->offset = block_end; + err = block_alloc_contiguous(dsp, ba, block_list); if (err < 0) return -ENOMEM; @@ -388,82 +745,270 @@ static int block_alloc_fixed(struct sst_module *module, list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { block_end = block->offset + block->size; + /* ignore blocks with wrong type */ + if (block->type != ba->type) + continue; + /* find block that holds section */ - if (data->offset >= block->offset && end < block_end) { + if (ba->offset >= block->offset && end <= block_end) { /* add block */ - block->data_type = data->data_type; list_move(&block->list, &dsp->used_block_list); - list_add(&block->module_list, &module->block_list); + list_add(&block->module_list, block_list); + dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", + block->type, block->index, block->offset); return 0; } /* does block span more than 1 section */ - if (data->offset >= block->offset && data->offset < block_end) { + if (ba->offset >= block->offset && ba->offset < block_end) { - err = block_alloc_contiguous(module, data, - block->offset, data->size); + /* align ba to block boundary */ + ba->offset = block->offset; + + err = block_alloc_contiguous(dsp, ba, block_list); if (err < 0) return -ENOMEM; return 0; } - } return -ENOMEM; } /* Load fixed module data into DSP memory blocks */ -int sst_module_insert_fixed_block(struct sst_module *module, - struct sst_module_data *data) +int sst_module_alloc_blocks(struct sst_module *module) { struct sst_dsp *dsp = module->dsp; + struct sst_fw *sst_fw = module->sst_fw; + struct sst_block_allocator ba; int ret; + ba.size = module->size; + ba.type = module->type; + ba.offset = module->offset; + + dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n", + ba.size, ba.offset, ba.type); + mutex_lock(&dsp->mutex); /* alloc blocks that includes this section */ - ret = block_alloc_fixed(module, data); + ret = block_alloc_fixed(dsp, &ba, &module->block_list); if (ret < 0) { dev_err(dsp->dev, "error: no free blocks for section at offset 0x%x size 0x%x\n", - data->offset, data->size); + module->offset, module->size); mutex_unlock(&dsp->mutex); return -ENOMEM; } /* prepare DSP blocks for module copy */ - ret = block_module_prepare(module); + ret = block_list_prepare(dsp, &module->block_list); if (ret < 0) { dev_err(dsp->dev, "error: fw module prepare failed\n"); goto err; } /* copy partial module data to blocks */ - sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size); + if (dsp->fw_use_dma) { + ret = sst_dsp_dma_copyto(dsp, + dsp->addr.lpe_base + module->offset, + sst_fw->dmable_fw_paddr + module->data_offset, + module->size); + if (ret < 0) { + dev_err(dsp->dev, "error: module copy failed\n"); + goto err; + } + } else + sst_memcpy32(dsp->addr.lpe + module->offset, module->data, + module->size); mutex_unlock(&dsp->mutex); return ret; err: - block_module_remove(module); + block_list_remove(dsp, &module->block_list); mutex_unlock(&dsp->mutex); return ret; } -EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block); +EXPORT_SYMBOL_GPL(sst_module_alloc_blocks); /* Unload entire module from DSP memory */ -int sst_block_module_remove(struct sst_module *module) +int sst_module_free_blocks(struct sst_module *module) { struct sst_dsp *dsp = module->dsp; mutex_lock(&dsp->mutex); - block_module_remove(module); + block_list_remove(dsp, &module->block_list); + mutex_unlock(&dsp->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(sst_module_free_blocks); + +int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime, + int offset) +{ + struct sst_dsp *dsp = runtime->dsp; + struct sst_module *module = runtime->module; + struct sst_block_allocator ba; + int ret; + + if (module->persistent_size == 0) + return 0; + + ba.size = module->persistent_size; + ba.type = SST_MEM_DRAM; + + mutex_lock(&dsp->mutex); + + /* do we need to allocate at a fixed address ? */ + if (offset != 0) { + + ba.offset = offset; + + dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n", + ba.size, ba.type, ba.offset); + + /* alloc blocks that includes this section */ + ret = block_alloc_fixed(dsp, &ba, &runtime->block_list); + + } else { + dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n", + ba.size, ba.type); + + /* alloc blocks that includes this section */ + ret = block_alloc(dsp, &ba, &runtime->block_list); + } + if (ret < 0) { + dev_err(dsp->dev, + "error: no free blocks for runtime module size 0x%x\n", + module->persistent_size); + mutex_unlock(&dsp->mutex); + return -ENOMEM; + } + runtime->persistent_offset = ba.offset; + + /* prepare DSP blocks for module copy */ + ret = block_list_prepare(dsp, &runtime->block_list); + if (ret < 0) { + dev_err(dsp->dev, "error: runtime block prepare failed\n"); + goto err; + } + + mutex_unlock(&dsp->mutex); + return ret; + +err: + block_list_remove(dsp, &module->block_list); + mutex_unlock(&dsp->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks); + +int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime) +{ + struct sst_dsp *dsp = runtime->dsp; + + mutex_lock(&dsp->mutex); + block_list_remove(dsp, &runtime->block_list); mutex_unlock(&dsp->mutex); return 0; } -EXPORT_SYMBOL_GPL(sst_block_module_remove); +EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks); + +int sst_module_runtime_save(struct sst_module_runtime *runtime, + struct sst_module_runtime_context *context) +{ + struct sst_dsp *dsp = runtime->dsp; + struct sst_module *module = runtime->module; + int ret = 0; + + dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n", + runtime->id, runtime->persistent_offset, + module->persistent_size); + + context->buffer = dma_alloc_coherent(dsp->dma_dev, + module->persistent_size, + &context->dma_buffer, GFP_DMA | GFP_KERNEL); + if (!context->buffer) { + dev_err(dsp->dev, "error: DMA context alloc failed\n"); + return -ENOMEM; + } + + mutex_lock(&dsp->mutex); + + if (dsp->fw_use_dma) { + + ret = sst_dsp_dma_get_channel(dsp, 0); + if (ret < 0) + goto err; + + ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer, + dsp->addr.lpe_base + runtime->persistent_offset, + module->persistent_size); + sst_dsp_dma_put_channel(dsp); + if (ret < 0) { + dev_err(dsp->dev, "error: context copy failed\n"); + goto err; + } + } else + sst_memcpy32(context->buffer, dsp->addr.lpe + + runtime->persistent_offset, + module->persistent_size); + +err: + mutex_unlock(&dsp->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(sst_module_runtime_save); + +int sst_module_runtime_restore(struct sst_module_runtime *runtime, + struct sst_module_runtime_context *context) +{ + struct sst_dsp *dsp = runtime->dsp; + struct sst_module *module = runtime->module; + int ret = 0; + + dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n", + runtime->id, runtime->persistent_offset, + module->persistent_size); + + mutex_lock(&dsp->mutex); + + if (!context->buffer) { + dev_info(dsp->dev, "no context buffer need to restore!\n"); + goto err; + } + + if (dsp->fw_use_dma) { + + ret = sst_dsp_dma_get_channel(dsp, 0); + if (ret < 0) + goto err; + + ret = sst_dsp_dma_copyto(dsp, + dsp->addr.lpe_base + runtime->persistent_offset, + context->dma_buffer, module->persistent_size); + sst_dsp_dma_put_channel(dsp); + if (ret < 0) { + dev_err(dsp->dev, "error: module copy failed\n"); + goto err; + } + } else + sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset, + context->buffer, module->persistent_size); + + dma_free_coherent(dsp->dma_dev, module->persistent_size, + context->buffer, context->dma_buffer); + context->buffer = NULL; + +err: + mutex_unlock(&dsp->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(sst_module_runtime_restore); /* register a DSP memory block for use with FW based modules */ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, @@ -516,80 +1061,83 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp) EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all); /* allocate scratch buffer blocks */ -struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp) +int sst_block_alloc_scratch(struct sst_dsp *dsp) { - struct sst_module *sst_module, *scratch; - struct sst_mem_block *block, *tmp; - u32 block_size; - int ret = 0; - - scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL); - if (scratch == NULL) - return NULL; + struct sst_module *module; + struct sst_block_allocator ba; + int ret; mutex_lock(&dsp->mutex); /* calculate required scratch size */ - list_for_each_entry(sst_module, &dsp->module_list, list) { - if (scratch->s.size < sst_module->s.size) - scratch->s.size = sst_module->s.size; + dsp->scratch_size = 0; + list_for_each_entry(module, &dsp->module_list, list) { + dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n", + module->id, module->scratch_size); + if (dsp->scratch_size < module->scratch_size) + dsp->scratch_size = module->scratch_size; } - dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n", - scratch->s.size); - - /* init scratch module */ - scratch->dsp = dsp; - scratch->s.type = SST_MEM_DRAM; - scratch->s.data_type = SST_DATA_S; - INIT_LIST_HEAD(&scratch->block_list); + dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n", + dsp->scratch_size); - /* check free blocks before looking at used blocks for space */ - if (!list_empty(&dsp->free_block_list)) - block = list_first_entry(&dsp->free_block_list, - struct sst_mem_block, list); - else - block = list_first_entry(&dsp->used_block_list, - struct sst_mem_block, list); - block_size = block->size; + if (dsp->scratch_size == 0) { + dev_info(dsp->dev, "no modules need scratch buffer\n"); + mutex_unlock(&dsp->mutex); + return 0; + } /* allocate blocks for module scratch buffers */ dev_dbg(dsp->dev, "allocating scratch blocks\n"); - ret = block_alloc(scratch, &scratch->s); + + ba.size = dsp->scratch_size; + ba.type = SST_MEM_DRAM; + + /* do we need to allocate at fixed offset */ + if (dsp->scratch_offset != 0) { + + dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n", + ba.size, ba.type, ba.offset); + + ba.offset = dsp->scratch_offset; + + /* alloc blocks that includes this section */ + ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list); + + } else { + dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n", + ba.size, ba.type); + + ba.offset = 0; + ret = block_alloc(dsp, &ba, &dsp->scratch_block_list); + } if (ret < 0) { dev_err(dsp->dev, "error: can't alloc scratch blocks\n"); - goto err; + mutex_unlock(&dsp->mutex); + return ret; } - /* assign the same offset of scratch to each module */ - list_for_each_entry(sst_module, &dsp->module_list, list) - sst_module->s.offset = scratch->s.offset; - - mutex_unlock(&dsp->mutex); - return scratch; + ret = block_list_prepare(dsp, &dsp->scratch_block_list); + if (ret < 0) { + dev_err(dsp->dev, "error: scratch block prepare failed\n"); + return ret; + } -err: - list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list) - list_del(&block->module_list); + /* assign the same offset of scratch to each module */ + dsp->scratch_offset = ba.offset; mutex_unlock(&dsp->mutex); - return NULL; + return dsp->scratch_size; } -EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch); +EXPORT_SYMBOL_GPL(sst_block_alloc_scratch); /* free all scratch blocks */ -void sst_mem_block_free_scratch(struct sst_dsp *dsp, - struct sst_module *scratch) +void sst_block_free_scratch(struct sst_dsp *dsp) { - struct sst_mem_block *block, *tmp; - mutex_lock(&dsp->mutex); - - list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list) - list_del(&block->module_list); - + block_list_remove(dsp, &dsp->scratch_block_list); mutex_unlock(&dsp->mutex); } -EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch); +EXPORT_SYMBOL_GPL(sst_block_free_scratch); /* get a module from it's unique ID */ struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id) @@ -609,3 +1157,40 @@ struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id) return NULL; } EXPORT_SYMBOL_GPL(sst_module_get_from_id); + +struct sst_module_runtime *sst_module_runtime_get_from_id( + struct sst_module *module, u32 id) +{ + struct sst_module_runtime *runtime; + struct sst_dsp *dsp = module->dsp; + + mutex_lock(&dsp->mutex); + + list_for_each_entry(runtime, &module->runtime_list, list) { + if (runtime->id == id) { + mutex_unlock(&dsp->mutex); + return runtime; + } + } + + mutex_unlock(&dsp->mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id); + +/* returns block address in DSP address space */ +u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, + enum sst_mem_type type) +{ + switch (type) { + case SST_MEM_IRAM: + return offset - dsp->addr.iram_offset + + dsp->addr.dsp_iram_offset; + case SST_MEM_DRAM: + return offset - dsp->addr.dram_offset + + dsp->addr.dsp_dram_offset; + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(sst_dsp_get_offset); diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c index 4b6c163..5058dc8 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -42,6 +42,10 @@ #define SST_LP_SHIM_OFFSET 0xE7000 #define SST_WPT_IRAM_OFFSET 0xA0000 #define SST_LP_IRAM_OFFSET 0x80000 +#define SST_WPT_DSP_DRAM_OFFSET 0x400000 +#define SST_WPT_DSP_IRAM_OFFSET 0x00000 +#define SST_LPT_DSP_DRAM_OFFSET 0x400000 +#define SST_LPT_DSP_IRAM_OFFSET 0x00000 #define SST_SHIM_PM_REG 0x84 @@ -86,9 +90,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, { struct dma_block_info *block; struct sst_module *mod; - struct sst_module_data block_data; struct sst_module_template template; - int count; + int count, ret; void __iomem *ram; /* TODO: allowed module types need to be configurable */ @@ -109,13 +112,9 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, memset(&template, 0, sizeof(template)); template.id = module->type; - template.entry = module->entry_point; - template.p.size = module->info.persistent_size; - template.p.type = SST_MEM_DRAM; - template.p.data_type = SST_DATA_P; - template.s.size = module->info.scratch_size; - template.s.type = SST_MEM_DRAM; - template.s.data_type = SST_DATA_S; + template.entry = module->entry_point - 4; + template.persistent_size = module->info.persistent_size; + template.scratch_size = module->info.scratch_size; mod = sst_module_new(fw, &template, NULL); if (mod == NULL) @@ -135,14 +134,14 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, switch (block->type) { case SST_HSW_IRAM: ram = dsp->addr.lpe; - block_data.offset = + mod->offset = block->ram_offset + dsp->addr.iram_offset; - block_data.type = SST_MEM_IRAM; + mod->type = SST_MEM_IRAM; break; case SST_HSW_DRAM: ram = dsp->addr.lpe; - block_data.offset = block->ram_offset; - block_data.type = SST_MEM_DRAM; + mod->offset = block->ram_offset; + mod->type = SST_MEM_DRAM; break; default: dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n", @@ -151,30 +150,34 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, return -EINVAL; } - block_data.size = block->size; - block_data.data_type = SST_DATA_M; - block_data.data = (void *)block + sizeof(*block); - block_data.data_offset = block_data.data - fw->dma_buf; + mod->size = block->size; + mod->data = (void *)block + sizeof(*block); + mod->data_offset = mod->data - fw->dma_buf; - dev_dbg(dsp->dev, "copy firmware block %d type 0x%x " + dev_dbg(dsp->dev, "module block %d type 0x%x " "size 0x%x ==> ram %p offset 0x%x\n", - count, block->type, block->size, ram, + count, mod->type, block->size, ram, block->ram_offset); - sst_module_insert_fixed_block(mod, &block_data); + ret = sst_module_alloc_blocks(mod); + if (ret < 0) { + dev_err(dsp->dev, "error: could not allocate blocks for module %d\n", + count); + sst_module_free(mod); + return ret; + } block = (void *)block + sizeof(*block) + block->size; } + return 0; } static int hsw_parse_fw_image(struct sst_fw *sst_fw) { struct fw_header *header; - struct sst_module *scratch; struct fw_module_header *module; struct sst_dsp *dsp = sst_fw->dsp; - struct sst_hsw *hsw = sst_fw->private; int ret, count; /* Read the header information from the data pointer */ @@ -204,12 +207,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw) module = (void *)module + sizeof(*module) + module->mod_size; } - /* allocate persistent/scratch mem regions */ - scratch = sst_mem_block_alloc_scratch(dsp); - if (scratch == NULL) - return -ENOMEM; - - sst_hsw_set_scratch_module(hsw, scratch); + /* allocate scratch mem regions */ + sst_block_alloc_scratch(dsp); return 0; } @@ -467,12 +466,16 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) region = lp_region; region_count = ARRAY_SIZE(lp_region); sst->addr.iram_offset = SST_LP_IRAM_OFFSET; + sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET; + sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET; sst->addr.shim_offset = SST_LP_SHIM_OFFSET; break; case SST_DEV_ID_WILDCAT_POINT: region = wpt_region; region_count = ARRAY_SIZE(wpt_region); sst->addr.iram_offset = SST_WPT_IRAM_OFFSET; + sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET; + sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET; sst->addr.shim_offset = SST_WPT_SHIM_OFFSET; break; default: diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 4799768..770d467 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1351,10 +1351,11 @@ int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream, } int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id, - u32 entry_point) + struct sst_hsw_stream *stream, struct sst_module_runtime *runtime) { struct sst_hsw_module_map *map = &stream->request.map; + struct sst_dsp *dsp = sst_hsw_get_dsp(hsw); + struct sst_module *module = runtime->module; if (stream->commited) { dev_err(hsw->dev, "error: stream committed for set module\n"); @@ -1363,36 +1364,25 @@ int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, /* only support initial module atm */ map->module_entries_count = 1; - map->module_entries[0].module_id = module_id; - map->module_entries[0].entry_point = entry_point; - - return 0; -} - -int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 offset, u32 size) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set pmem\n"); - return -EINVAL; - } - - stream->request.persistent_mem.offset = offset; - stream->request.persistent_mem.size = size; - - return 0; -} - -int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 offset, u32 size) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set smem\n"); - return -EINVAL; - } - - stream->request.scratch_mem.offset = offset; - stream->request.scratch_mem.size = size; + map->module_entries[0].module_id = module->id; + map->module_entries[0].entry_point = module->entry; + + stream->request.persistent_mem.offset = + sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM); + stream->request.persistent_mem.size = module->persistent_size; + + stream->request.scratch_mem.offset = + sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM); + stream->request.scratch_mem.size = dsp->scratch_size; + + dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id, + runtime->id); + dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n", + stream->request.persistent_mem.offset, + stream->request.persistent_mem.size); + dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n", + stream->request.scratch_mem.offset, + stream->request.scratch_mem.size); return 0; } @@ -1673,32 +1663,48 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n", dx->entries_no, state); - memcpy(&hsw->dx, dx, sizeof(*dx)); - return 0; + return ret; } -/* Used to save state into hsw->dx_reply */ -int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item, - u32 *offset, u32 *size, u32 *source) +struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, + int mod_id, int offset) { - struct sst_hsw_ipc_dx_memory_item *dx_mem; - struct sst_hsw_ipc_dx_reply *dx_reply; - int entry_no; + struct sst_dsp *dsp = hsw->dsp; + struct sst_module *module; + struct sst_module_runtime *runtime; + int err; - dx_reply = &hsw->dx; - entry_no = dx_reply->entries_no; + module = sst_module_get_from_id(dsp, mod_id); + if (module == NULL) { + dev_err(dsp->dev, "error: failed to get module %d for pcm\n", + mod_id); + return NULL; + } - trace_ipc_request("PM get Dx state", entry_no); + runtime = sst_module_runtime_new(module, mod_id, NULL); + if (runtime == NULL) { + dev_err(dsp->dev, "error: failed to create module %d runtime\n", + mod_id); + return NULL; + } - if (item >= entry_no) - return -EINVAL; + err = sst_module_runtime_alloc_blocks(runtime, offset); + if (err < 0) { + dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n", + mod_id); + sst_module_runtime_free(runtime); + return NULL; + } - dx_mem = &dx_reply->mem_info[item]; - *offset = dx_mem->offset; - *size = dx_mem->size; - *source = dx_mem->source; + dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id, + mod_id); + return runtime; +} - return 0; +void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime) +{ + sst_module_runtime_free_blocks(runtime); + sst_module_runtime_free(runtime); } static int msg_empty_list_init(struct sst_hsw *hsw) @@ -1718,12 +1724,6 @@ static int msg_empty_list_init(struct sst_hsw *hsw) return 0; } -void sst_hsw_set_scratch_module(struct sst_hsw *hsw, - struct sst_module *scratch) -{ - hsw->scratch = scratch; -} - struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) { return hsw->dsp; diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 063dd6b..fe6e63f 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -40,6 +40,7 @@ struct sst_hsw_stream; struct sst_hsw_log_stream; struct sst_pdata; struct sst_module; +struct sst_module_runtime; extern struct sst_ops haswell_ops; /* Stream Allocate Path ID */ @@ -432,8 +433,7 @@ int sst_hsw_stream_set_map_config(struct sst_hsw *hsw, int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream, enum sst_hsw_interleaving style); int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id, - u32 entry_point); + struct sst_hsw_stream *stream, struct sst_module_runtime *runtime); int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, struct sst_hsw_stream *stream, u32 offset, u32 size); int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, @@ -486,7 +486,10 @@ int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item, int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); -void sst_hsw_set_scratch_module(struct sst_hsw *hsw, - struct sst_module *scratch); + +/* runtime module management */ +struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, + int mod_id, int offset); +void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime); #endif diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 32a6470..522edef 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -89,16 +89,23 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = { .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE, }; +struct hsw_pcm_module_map { + int dai_id; + enum sst_hsw_module_id mod_id; +}; + /* private data for each PCM DSP stream */ struct hsw_pcm_data { int dai_id; struct sst_hsw_stream *stream; + struct sst_module_runtime *runtime; u32 volume[2]; struct snd_pcm_substream *substream; struct snd_compr_stream *cstream; unsigned int wpos; struct mutex mutex; bool allocated; + int persistent_offset; }; /* private data for the driver */ @@ -472,28 +479,8 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* we use hardcoded memory offsets atm, will be updated for new FW */ - if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) { - sst_hsw_stream_set_module_info(hsw, pcm_data->stream, - SST_HSW_MODULE_PCM_CAPTURE, module_data->entry); - sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, - 0x449400, 0x4000); - sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, - 0x400000, 0); - } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */ - sst_hsw_stream_set_module_info(hsw, pcm_data->stream, - SST_HSW_MODULE_PCM_SYSTEM, module_data->entry); - - sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, - module_data->offset, module_data->size); - sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, - 0x44d400, 0x3800); - - sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, - module_data->offset, module_data->size); - sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, - 0x400000, 0); - } + sst_hsw_stream_set_module_info(hsw, pcm_data->stream, + pcm_data->runtime); ret = sst_hsw_stream_commit(hsw, pcm_data->stream); if (ret < 0) { @@ -654,6 +641,55 @@ static struct snd_pcm_ops hsw_pcm_ops = { .page = snd_pcm_sgbuf_ops_page, }; +/* static mappings between PCMs and modules - may be dynamic in future */ +static struct hsw_pcm_module_map mod_map[] = { + {0, SST_HSW_MODULE_PCM_SYSTEM}, /* "System Pin" */ + {1, SST_HSW_MODULE_PCM}, /* "Offload0 Pin" */ + {2, SST_HSW_MODULE_PCM}, /* "Offload1 Pin" */ + {3, SST_HSW_MODULE_PCM_REFERENCE}, /* "Loopback Pin" */ + {4, SST_HSW_MODULE_PCM_CAPTURE}, /* "Capture Pin" */ +}; + +static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) +{ + struct sst_hsw *hsw = pdata->hsw; + struct hsw_pcm_data *pcm_data; + int i; + + for (i = 0; i < ARRAY_SIZE(mod_map); i++) { + pcm_data = &pdata->pcm[i]; + + pcm_data->runtime = sst_hsw_runtime_module_create(hsw, + mod_map[i].mod_id, pcm_data->persistent_offset); + if (pcm_data->runtime == NULL) + goto err; + pcm_data->persistent_offset = + pcm_data->runtime->persistent_offset; + } + + return 0; + +err: + for (--i; i >= 0; i--) { + pcm_data = &pdata->pcm[i]; + sst_hsw_runtime_module_free(pcm_data->runtime); + } + + return -ENODEV; +} + +static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) +{ + struct hsw_pcm_data *pcm_data; + int i; + + for (i = 0; i < ARRAY_SIZE(mod_map); i++) { + pcm_data = &pdata->pcm[i]; + + sst_hsw_runtime_module_free(pcm_data->runtime); + } +} + static void hsw_pcm_free(struct snd_pcm *pcm) { snd_pcm_lib_preallocate_free_for_all(pcm); @@ -797,6 +833,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) } } + /* allocate runtime modules */ + hsw_pcm_create_modules(priv_data); + return 0; err: -- cgit v0.10.2 From 4e44923847b0b2597eaef07d5e700f5dbed2162e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 28 Oct 2014 17:08:40 +0100 Subject: ASoC: cs42l51: make driver user-selectable Since we are removing the Armada 370 DB audio machine driver to use the 'simple-card' Device Tree binding, we can no longer select the CS42L51 codec driver using a Kconfig 'select', and we instead need it to be user-selectable. Therefore, this commit adds a prompt to make the CS42L51 I2C codec driver user-selectable. Signed-off-by: Thomas Petazzoni Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..f4fb12f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -336,7 +336,7 @@ config SND_SOC_CS42L51 tristate config SND_SOC_CS42L51_I2C - tristate + tristate "Cirrus Logic CS42L51 CODEC (I2C)" select SND_SOC_CS42L51 config SND_SOC_CS42L52 -- cgit v0.10.2 From e6f6ebc1f8f60d6d44f6be22c6386c238d6a9d97 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 22 Oct 2014 16:11:39 +0800 Subject: ASoC: rt5677: Add TDM channel mapping function It is for channel to slot mapping, and it is not only for 8 channels mapping, but also in 2, 4 and 6 channels mapping. If we want to use the 2 channels in the stereo2 adc path, we need to set the item "2/1/3/4" or "2/3/1/4". It also adds for stereo channel swap. It can map the sterero channels "L/R" to "R/L", "L/L" or R/R. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 413bccb..ca264f8 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -1834,6 +1834,93 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_if4_adc_mux = SOC_DAPM_ENUM("IF4 ADC Source", rt5677_if4_adc_enum); +/* TDM IF1/2 ADC Data Selection */ /* MX-3B MX-40 [7:6][5:4][3:2][1:0] */ +static const char * const rt5677_if12_adc_swap_src[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc1_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC1_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc1_swap_mux = + SOC_DAPM_ENUM("IF1 ADC1 Swap Source", rt5677_if1_adc1_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc2_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc2_swap_mux = + SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if1_adc2_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc3_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc3_swap_mux = + SOC_DAPM_ENUM("IF1 ADC3 Swap Source", rt5677_if1_adc3_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc4_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc4_swap_mux = + SOC_DAPM_ENUM("IF1 ADC4 Swap Source", rt5677_if1_adc4_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc1_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc1_swap_mux = + SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if2_adc1_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc2_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc2_swap_mux = + SOC_DAPM_ENUM("IF2 ADC2 Swap Source", rt5677_if2_adc2_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc3_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc3_swap_mux = + SOC_DAPM_ENUM("IF2 ADC3 Swap Source", rt5677_if2_adc3_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc4_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc4_swap_mux = + SOC_DAPM_ENUM("IF2 ADC4 Swap Source", rt5677_if2_adc4_swap_enum); + +/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */ +static const char * const rt5677_if1_adc_tdm_swap_src[] = { + "1/2/3/4", "2/1/3/4", "2/3/1/4", "4/1/2/3", "1/3/2/4", "1/4/2/3", + "3/1/2/4", "3/4/1/2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc_tdm_swap_enum, RT5677_TDM1_CTRL2, + RT5677_IF1_ADC_CTRL_SFT, rt5677_if1_adc_tdm_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc_tdm_swap_mux = + SOC_DAPM_ENUM("IF1 ADC TDM Swap Source", rt5677_if1_adc_tdm_swap_enum); + +/* TDM IF2 ADC Data Selection */ /* MX-41[2:0] */ +static const char * const rt5677_if2_adc_tdm_swap_src[] = { + "1/2/3/4", "2/1/3/4", "3/1/2/4", "4/1/2/3", "1/3/2/4", "1/4/2/3", + "2/3/1/4", "3/4/1/2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc_tdm_swap_enum, RT5677_TDM2_CTRL2, + RT5677_IF2_ADC_CTRL_SFT, rt5677_if2_adc_tdm_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc_tdm_swap_mux = + SOC_DAPM_ENUM("IF2 ADC TDM Swap Source", rt5677_if2_adc_tdm_swap_enum); + static int rt5677_bst1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1945,6 +2032,52 @@ static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + unsigned int value; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_read(rt5677->regmap, RT5677_TDM1_CTRL2, &value); + if (value & RT5677_IF1_ADC_CTRL_MASK) + regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC_MODE_MASK, + RT5677_IF1_ADC_MODE_TDM); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + unsigned int value; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_read(rt5677->regmap, RT5677_TDM2_CTRL2, &value); + if (value & RT5677_IF2_ADC_CTRL_MASK) + regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC_MODE_MASK, + RT5677_IF2_ADC_MODE_TDM); + break; + + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT, 0, rt5677_set_pll1_event, SND_SOC_DAPM_POST_PMU), @@ -2104,10 +2237,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_PGA("Stereo4 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), /* DSP */ SND_SOC_DAPM_MUX("IB9 Mux", SND_SOC_NOPM, 0, 0, @@ -2230,6 +2361,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { &rt5677_if1_adc3_mux), SND_SOC_DAPM_MUX("IF1 ADC4 Mux", SND_SOC_NOPM, 0, 0, &rt5677_if1_adc4_mux), + SND_SOC_DAPM_MUX("IF1 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc1_swap_mux), + SND_SOC_DAPM_MUX("IF1 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc2_swap_mux), + SND_SOC_DAPM_MUX("IF1 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc3_swap_mux), + SND_SOC_DAPM_MUX("IF1 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc4_swap_mux), + SND_SOC_DAPM_MUX_E("IF1 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc_tdm_swap_mux, rt5677_if1_adc_tdm_event, + SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_MUX("IF2 ADC1 Mux", SND_SOC_NOPM, 0, 0, &rt5677_if2_adc1_mux), SND_SOC_DAPM_MUX("IF2 ADC2 Mux", SND_SOC_NOPM, 0, 0, @@ -2238,6 +2380,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { &rt5677_if2_adc3_mux), SND_SOC_DAPM_MUX("IF2 ADC4 Mux", SND_SOC_NOPM, 0, 0, &rt5677_if2_adc4_mux), + SND_SOC_DAPM_MUX("IF2 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc1_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc2_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc3_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc4_swap_mux), + SND_SOC_DAPM_MUX_E("IF2 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc_tdm_swap_mux, rt5677_if2_adc_tdm_event, + SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5677_if3_adc_mux), SND_SOC_DAPM_MUX("IF4 ADC Mux", SND_SOC_NOPM, 0, 0, @@ -2621,11 +2774,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "IF1 ADC4 Mux", "OB67", "OB67" }, { "IF1 ADC4 Mux", "OB01", "OB01 Bypass Mux" }, + { "IF1 ADC1 Swap Mux", "L/R", "IF1 ADC1 Mux" }, + { "IF1 ADC1 Swap Mux", "R/L", "IF1 ADC1 Mux" }, + { "IF1 ADC1 Swap Mux", "L/L", "IF1 ADC1 Mux" }, + { "IF1 ADC1 Swap Mux", "R/R", "IF1 ADC1 Mux" }, + + { "IF1 ADC2 Swap Mux", "L/R", "IF1 ADC2 Mux" }, + { "IF1 ADC2 Swap Mux", "R/L", "IF1 ADC2 Mux" }, + { "IF1 ADC2 Swap Mux", "L/L", "IF1 ADC2 Mux" }, + { "IF1 ADC2 Swap Mux", "R/R", "IF1 ADC2 Mux" }, + + { "IF1 ADC3 Swap Mux", "L/R", "IF1 ADC3 Mux" }, + { "IF1 ADC3 Swap Mux", "R/L", "IF1 ADC3 Mux" }, + { "IF1 ADC3 Swap Mux", "L/L", "IF1 ADC3 Mux" }, + { "IF1 ADC3 Swap Mux", "R/R", "IF1 ADC3 Mux" }, + + { "IF1 ADC4 Swap Mux", "L/R", "IF1 ADC4 Mux" }, + { "IF1 ADC4 Swap Mux", "R/L", "IF1 ADC4 Mux" }, + { "IF1 ADC4 Swap Mux", "L/L", "IF1 ADC4 Mux" }, + { "IF1 ADC4 Swap Mux", "R/R", "IF1 ADC4 Mux" }, + + { "IF1 ADC", NULL, "IF1 ADC1 Swap Mux" }, + { "IF1 ADC", NULL, "IF1 ADC2 Swap Mux" }, + { "IF1 ADC", NULL, "IF1 ADC3 Swap Mux" }, + { "IF1 ADC", NULL, "IF1 ADC4 Swap Mux" }, + + { "IF1 ADC TDM Swap Mux", "1/2/3/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "2/1/3/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "2/3/1/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "4/1/2/3", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "1/3/2/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "1/4/2/3", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "3/1/2/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "3/4/1/2", "IF1 ADC" }, + { "AIF1TX", NULL, "I2S1" }, - { "AIF1TX", NULL, "IF1 ADC1 Mux" }, - { "AIF1TX", NULL, "IF1 ADC2 Mux" }, - { "AIF1TX", NULL, "IF1 ADC3 Mux" }, - { "AIF1TX", NULL, "IF1 ADC4 Mux" }, + { "AIF1TX", NULL, "IF1 ADC TDM Swap Mux" }, { "IF2 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, { "IF2 ADC1 Mux", "OB01", "OB01 Bypass Mux" }, @@ -2642,11 +2826,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "IF2 ADC4 Mux", "OB67", "OB67" }, { "IF2 ADC4 Mux", "OB01", "OB01 Bypass Mux" }, + { "IF2 ADC1 Swap Mux", "L/R", "IF2 ADC1 Mux" }, + { "IF2 ADC1 Swap Mux", "R/L", "IF2 ADC1 Mux" }, + { "IF2 ADC1 Swap Mux", "L/L", "IF2 ADC1 Mux" }, + { "IF2 ADC1 Swap Mux", "R/R", "IF2 ADC1 Mux" }, + + { "IF2 ADC2 Swap Mux", "L/R", "IF2 ADC2 Mux" }, + { "IF2 ADC2 Swap Mux", "R/L", "IF2 ADC2 Mux" }, + { "IF2 ADC2 Swap Mux", "L/L", "IF2 ADC2 Mux" }, + { "IF2 ADC2 Swap Mux", "R/R", "IF2 ADC2 Mux" }, + + { "IF2 ADC3 Swap Mux", "L/R", "IF2 ADC3 Mux" }, + { "IF2 ADC3 Swap Mux", "R/L", "IF2 ADC3 Mux" }, + { "IF2 ADC3 Swap Mux", "L/L", "IF2 ADC3 Mux" }, + { "IF2 ADC3 Swap Mux", "R/R", "IF2 ADC3 Mux" }, + + { "IF2 ADC4 Swap Mux", "L/R", "IF2 ADC4 Mux" }, + { "IF2 ADC4 Swap Mux", "R/L", "IF2 ADC4 Mux" }, + { "IF2 ADC4 Swap Mux", "L/L", "IF2 ADC4 Mux" }, + { "IF2 ADC4 Swap Mux", "R/R", "IF2 ADC4 Mux" }, + + { "IF2 ADC", NULL, "IF2 ADC1 Swap Mux" }, + { "IF2 ADC", NULL, "IF2 ADC2 Swap Mux" }, + { "IF2 ADC", NULL, "IF2 ADC3 Swap Mux" }, + { "IF2 ADC", NULL, "IF2 ADC4 Swap Mux" }, + + { "IF2 ADC TDM Swap Mux", "1/2/3/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "2/1/3/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "3/1/2/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "4/1/2/3", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "1/3/2/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "1/4/2/3", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "2/3/1/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "3/4/1/2", "IF2 ADC" }, + { "AIF2TX", NULL, "I2S2" }, - { "AIF2TX", NULL, "IF2 ADC1 Mux" }, - { "AIF2TX", NULL, "IF2 ADC2 Mux" }, - { "AIF2TX", NULL, "IF2 ADC3 Mux" }, - { "AIF2TX", NULL, "IF2 ADC4 Mux" }, + { "AIF2TX", NULL, "IF2 ADC TDM Swap Mux" }, { "IF3 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, { "IF3 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index d2c743c..2f5b8c6 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -799,7 +799,21 @@ #define RT5677_PDM2_I2C_EXE (0x1 << 1) #define RT5677_PDM2_I2C_BUSY (0x1 << 0) -/* MX3C TDM1 control 1 (0x3c) */ +/* MX3B TDM1 control 1 (0x3b) */ +#define RT5677_IF1_ADC_MODE_MASK (0x1 << 12) +#define RT5677_IF1_ADC_MODE_SFT 12 +#define RT5677_IF1_ADC_MODE_I2S (0x0 << 12) +#define RT5677_IF1_ADC_MODE_TDM (0x1 << 12) +#define RT5677_IF1_ADC1_SWAP_MASK (0x3 << 6) +#define RT5677_IF1_ADC1_SWAP_SFT 6 +#define RT5677_IF1_ADC2_SWAP_MASK (0x3 << 4) +#define RT5677_IF1_ADC2_SWAP_SFT 4 +#define RT5677_IF1_ADC3_SWAP_MASK (0x3 << 2) +#define RT5677_IF1_ADC3_SWAP_SFT 2 +#define RT5677_IF1_ADC4_SWAP_MASK (0x3 << 0) +#define RT5677_IF1_ADC4_SWAP_SFT 0 + +/* MX3C TDM1 control 2 (0x3c) */ #define RT5677_IF1_ADC4_MASK (0x3 << 10) #define RT5677_IF1_ADC4_SFT 10 #define RT5677_IF1_ADC3_MASK (0x3 << 8) @@ -808,8 +822,24 @@ #define RT5677_IF1_ADC2_SFT 6 #define RT5677_IF1_ADC1_MASK (0x3 << 4) #define RT5677_IF1_ADC1_SFT 4 - -/* MX41 TDM2 control 1 (0x41) */ +#define RT5677_IF1_ADC_CTRL_MASK (0x7 << 0) +#define RT5677_IF1_ADC_CTRL_SFT 0 + +/* MX40 TDM2 control 1 (0x40) */ +#define RT5677_IF2_ADC_MODE_MASK (0x1 << 12) +#define RT5677_IF2_ADC_MODE_SFT 12 +#define RT5677_IF2_ADC_MODE_I2S (0x0 << 12) +#define RT5677_IF2_ADC_MODE_TDM (0x1 << 12) +#define RT5677_IF2_ADC1_SWAP_MASK (0x3 << 6) +#define RT5677_IF2_ADC1_SWAP_SFT 6 +#define RT5677_IF2_ADC2_SWAP_MASK (0x3 << 4) +#define RT5677_IF2_ADC2_SWAP_SFT 4 +#define RT5677_IF2_ADC3_SWAP_MASK (0x3 << 2) +#define RT5677_IF2_ADC3_SWAP_SFT 2 +#define RT5677_IF2_ADC4_SWAP_MASK (0x3 << 0) +#define RT5677_IF2_ADC4_SWAP_SFT 0 + +/* MX41 TDM2 control 2 (0x41) */ #define RT5677_IF2_ADC4_MASK (0x3 << 10) #define RT5677_IF2_ADC4_SFT 10 #define RT5677_IF2_ADC3_MASK (0x3 << 8) @@ -818,6 +848,8 @@ #define RT5677_IF2_ADC2_SFT 6 #define RT5677_IF2_ADC1_MASK (0x3 << 4) #define RT5677_IF2_ADC1_SFT 4 +#define RT5677_IF2_ADC_CTRL_MASK (0x7 << 0) +#define RT5677_IF2_ADC_CTRL_SFT 0 /* Digital Microphone Control 1 (0x50) */ #define RT5677_DMIC_1_EN_MASK (0x1 << 15) -- cgit v0.10.2 From 63ae1fe7739ec81eb63ad241b4e217d1fa0e8e53 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 Oct 2014 10:33:31 +0000 Subject: ASoC: Intel: Add dependency on DesignWare DMA controller We have calls into the controller so we need to ensure it is being built. Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 2a3af88..ae7f872 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -20,6 +20,7 @@ config SND_SOC_INTEL_SST tristate "ASoC support for Intel(R) Smart Sound Technology" select SND_SOC_INTEL_SST_ACPI if ACPI depends on (X86 || COMPILE_TEST) + depends on DW_DMAC_CORE help This adds support for Intel(R) Smart Sound Technology (SST). Say Y if you have such a device -- cgit v0.10.2 From 7077148fb50a120d20a50516a332ed6eb9233c16 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 28 Oct 2014 22:15:31 +0000 Subject: ASoC: core: Split ops out of soc-core.c The main ASoC source file is getting quite large and the standard ops don't really have anything to do with the rest of the file so split them out into a separate file. Signed-off-by: Mark Brown diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 534714a..a384d14 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,5 +1,5 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o -snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o +snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 47c378a..a2b51ed 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2334,925 +2334,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); /** - * snd_soc_info_enum_double - enumerated double mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a double enumerated - * mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - - return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2, - e->items, e->texts); -} -EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); - -/** - * snd_soc_get_enum_double - enumerated double mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a double enumerated mixer. - * - * Returns 0 for success. - */ -int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, item; - unsigned int reg_val; - int ret; - - ret = snd_soc_component_read(component, e->reg, ®_val); - if (ret) - return ret; - val = (reg_val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); - ucontrol->value.enumerated.item[0] = item; - if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); - ucontrol->value.enumerated.item[1] = item; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); - -/** - * snd_soc_put_enum_double - enumerated double mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a double enumerated mixer. - * - * Returns 0 for success. - */ -int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int *item = ucontrol->value.enumerated.item; - unsigned int val; - unsigned int mask; - - if (item[0] >= e->items) - return -EINVAL; - val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { - if (item[1] >= e->items) - return -EINVAL; - val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; - mask |= e->mask << e->shift_r; - } - - return snd_soc_component_update_bits(component, e->reg, mask, val); -} -EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); - -/** - * snd_soc_read_signed - Read a codec register and interprete as signed value - * @component: component - * @reg: Register to read - * @mask: Mask to use after shifting the register value - * @shift: Right shift of register value - * @sign_bit: Bit that describes if a number is negative or not. - * @signed_val: Pointer to where the read value should be stored - * - * This functions reads a codec register. The register value is shifted right - * by 'shift' bits and masked with the given 'mask'. Afterwards it translates - * the given registervalue into a signed integer if sign_bit is non-zero. - * - * Returns 0 on sucess, otherwise an error value - */ -static int snd_soc_read_signed(struct snd_soc_component *component, - unsigned int reg, unsigned int mask, unsigned int shift, - unsigned int sign_bit, int *signed_val) -{ - int ret; - unsigned int val; - - ret = snd_soc_component_read(component, reg, &val); - if (ret < 0) - return ret; - - val = (val >> shift) & mask; - - if (!sign_bit) { - *signed_val = val; - return 0; - } - - /* non-negative number */ - if (!(val & BIT(sign_bit))) { - *signed_val = val; - return 0; - } - - ret = val; - - /* - * The register most probably does not contain a full-sized int. - * Instead we have an arbitrary number of bits in a signed - * representation which has to be translated into a full-sized int. - * This is done by filling up all bits above the sign-bit. - */ - ret |= ~((int)(BIT(sign_bit) - 1)); - - *signed_val = ret; - - return 0; -} - -/** - * snd_soc_info_volsw - single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a single mixer control, or a double - * mixer control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int platform_max; - - if (!mc->platform_max) - mc->platform_max = mc->max; - platform_max = mc->platform_max; - - if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max - mc->min; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw); - -/** - * snd_soc_get_volsw - single mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a single mixer control, or a double mixer - * control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - int sign_bit = mc->sign_bit; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - int val; - int ret; - - if (sign_bit) - mask = BIT(sign_bit + 1) - 1; - - ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = val - min; - if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; - - if (snd_soc_volsw_is_stereo(mc)) { - if (reg == reg2) - ret = snd_soc_read_signed(component, reg, mask, rshift, - sign_bit, &val); - else - ret = snd_soc_read_signed(component, reg2, mask, shift, - sign_bit, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[1] = val - min; - if (invert) - ucontrol->value.integer.value[1] = - max - ucontrol->value.integer.value[1]; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw); - -/** - * snd_soc_put_volsw - single mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a single mixer control, or a double mixer - * control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - unsigned int sign_bit = mc->sign_bit; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - int err; - bool type_2r = false; - unsigned int val2 = 0; - unsigned int val, val_mask; - - if (sign_bit) - mask = BIT(sign_bit + 1) - 1; - - val = ((ucontrol->value.integer.value[0] + min) & mask); - if (invert) - val = max - val; - val_mask = mask << shift; - val = val << shift; - if (snd_soc_volsw_is_stereo(mc)) { - val2 = ((ucontrol->value.integer.value[1] + min) & mask); - if (invert) - val2 = max - val2; - if (reg == reg2) { - val_mask |= mask << rshift; - val |= val2 << rshift; - } else { - val2 = val2 << shift; - type_2r = true; - } - } - err = snd_soc_component_update_bits(component, reg, val_mask, val); - if (err < 0) - return err; - - if (type_2r) - err = snd_soc_component_update_bits(component, reg2, val_mask, - val2); - - return err; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw); - -/** - * snd_soc_get_volsw_sx - single mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a single mixer control, or a double mixer - * control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - int mask = (1 << (fls(min + max) - 1)) - 1; - unsigned int val; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret < 0) - return ret; - - ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask; - - if (snd_soc_volsw_is_stereo(mc)) { - ret = snd_soc_component_read(component, reg2, &val); - if (ret < 0) - return ret; - - val = ((val >> rshift) - min) & mask; - ucontrol->value.integer.value[1] = val; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); - -/** - * snd_soc_put_volsw_sx - double mixer set callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to set the value of a double mixer control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - int mask = (1 << (fls(min + max) - 1)) - 1; - int err = 0; - unsigned int val, val_mask, val2 = 0; - - val_mask = mask << shift; - val = (ucontrol->value.integer.value[0] + min) & mask; - val = val << shift; - - err = snd_soc_component_update_bits(component, reg, val_mask, val); - if (err < 0) - return err; - - if (snd_soc_volsw_is_stereo(mc)) { - val_mask = mask << rshift; - val2 = (ucontrol->value.integer.value[1] + min) & mask; - val2 = val2 << rshift; - - err = snd_soc_component_update_bits(component, reg2, val_mask, - val2); - } - return err; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); - -/** - * snd_soc_info_volsw_range - single mixer info callback with range. - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information, within a range, about a single - * mixer control. - * - * returns 0 for success. - */ -int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int platform_max; - int min = mc->min; - - if (!mc->platform_max) - mc->platform_max = mc->max; - platform_max = mc->platform_max; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max - min; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); - -/** - * snd_soc_put_volsw_range - single mixer put value callback with range. - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value, within a range, for a single mixer control. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - unsigned int rreg = mc->rreg; - unsigned int shift = mc->shift; - int min = mc->min; - int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - unsigned int val, val_mask; - int ret; - - if (invert) - val = (max - ucontrol->value.integer.value[0]) & mask; - else - val = ((ucontrol->value.integer.value[0] + min) & mask); - val_mask = mask << shift; - val = val << shift; - - ret = snd_soc_component_update_bits(component, reg, val_mask, val); - if (ret < 0) - return ret; - - if (snd_soc_volsw_is_stereo(mc)) { - if (invert) - val = (max - ucontrol->value.integer.value[1]) & mask; - else - val = ((ucontrol->value.integer.value[1] + min) & mask); - val_mask = mask << shift; - val = val << shift; - - ret = snd_soc_component_update_bits(component, rreg, val_mask, - val); - } - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); - -/** - * snd_soc_get_volsw_range - single mixer get callback with range - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value, within a range, of a single mixer control. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int rreg = mc->rreg; - unsigned int shift = mc->shift; - int min = mc->min; - int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - unsigned int val; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = (val >> shift) & mask; - if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; - else - ucontrol->value.integer.value[0] = - ucontrol->value.integer.value[0] - min; - - if (snd_soc_volsw_is_stereo(mc)) { - ret = snd_soc_component_read(component, rreg, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[1] = (val >> shift) & mask; - if (invert) - ucontrol->value.integer.value[1] = - max - ucontrol->value.integer.value[1]; - else - ucontrol->value.integer.value[1] = - ucontrol->value.integer.value[1] - min; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); - -/** - * snd_soc_limit_volume - Set new limit to an existing volume control. - * - * @codec: where to look for the control - * @name: Name of the control - * @max: new maximum limit - * - * Return 0 for success, else error. - */ -int snd_soc_limit_volume(struct snd_soc_codec *codec, - const char *name, int max) -{ - struct snd_card *card = codec->component.card->snd_card; - struct snd_kcontrol *kctl; - struct soc_mixer_control *mc; - int found = 0; - int ret = -EINVAL; - - /* Sanity check for name and max */ - if (unlikely(!name || max <= 0)) - return -EINVAL; - - list_for_each_entry(kctl, &card->controls, list) { - if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { - found = 1; - break; - } - } - if (found) { - mc = (struct soc_mixer_control *)kctl->private_value; - if (max <= mc->max) { - mc->platform_max = max; - ret = 0; - } - } - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_limit_volume); - -int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_bytes *params = (void *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = params->num_regs * component->val_bytes; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_info); - -int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_bytes *params = (void *)kcontrol->private_value; - int ret; - - if (component->regmap) - ret = regmap_raw_read(component->regmap, params->base, - ucontrol->value.bytes.data, - params->num_regs * component->val_bytes); - else - ret = -EINVAL; - - /* Hide any masked bytes to ensure consistent data reporting */ - if (ret == 0 && params->mask) { - switch (component->val_bytes) { - case 1: - ucontrol->value.bytes.data[0] &= ~params->mask; - break; - case 2: - ((u16 *)(&ucontrol->value.bytes.data))[0] - &= cpu_to_be16(~params->mask); - break; - case 4: - ((u32 *)(&ucontrol->value.bytes.data))[0] - &= cpu_to_be32(~params->mask); - break; - default: - return -EINVAL; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_get); - -int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_bytes *params = (void *)kcontrol->private_value; - int ret, len; - unsigned int val, mask; - void *data; - - if (!component->regmap || !params->num_regs) - return -EINVAL; - - len = params->num_regs * component->val_bytes; - - data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); - if (!data) - return -ENOMEM; - - /* - * If we've got a mask then we need to preserve the register - * bits. We shouldn't modify the incoming data so take a - * copy. - */ - if (params->mask) { - ret = regmap_read(component->regmap, params->base, &val); - if (ret != 0) - goto out; - - val &= params->mask; - - switch (component->val_bytes) { - case 1: - ((u8 *)data)[0] &= ~params->mask; - ((u8 *)data)[0] |= val; - break; - case 2: - mask = ~params->mask; - ret = regmap_parse_val(component->regmap, - &mask, &mask); - if (ret != 0) - goto out; - - ((u16 *)data)[0] &= mask; - - ret = regmap_parse_val(component->regmap, - &val, &val); - if (ret != 0) - goto out; - - ((u16 *)data)[0] |= val; - break; - case 4: - mask = ~params->mask; - ret = regmap_parse_val(component->regmap, - &mask, &mask); - if (ret != 0) - goto out; - - ((u32 *)data)[0] &= mask; - - ret = regmap_parse_val(component->regmap, - &val, &val); - if (ret != 0) - goto out; - - ((u32 *)data)[0] |= val; - break; - default: - ret = -EINVAL; - goto out; - } - } - - ret = regmap_raw_write(component->regmap, params->base, - data, len); - -out: - kfree(data); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_put); - -int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *ucontrol) -{ - struct soc_bytes_ext *params = (void *)kcontrol->private_value; - - ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; - ucontrol->count = params->max; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); - -int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct soc_bytes_ext *params = (void *)kcontrol->private_value; - unsigned int count = size < params->max ? size : params->max; - int ret = -ENXIO; - - switch (op_flag) { - case SNDRV_CTL_TLV_OP_READ: - if (params->get) - ret = params->get(tlv, count); - break; - case SNDRV_CTL_TLV_OP_WRITE: - if (params->put) - ret = params->put(tlv, count); - break; - } - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback); - -/** - * snd_soc_info_xr_sx - signed multi register info callback - * @kcontrol: mreg control - * @uinfo: control element information - * - * Callback to provide information of a control that can - * span multiple codec registers which together - * forms a single signed value in a MSB/LSB manner. - * - * Returns 0 for success. - */ -int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mreg_control *mc = - (struct soc_mreg_control *)kcontrol->private_value; - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = mc->min; - uinfo->value.integer.max = mc->max; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); - -/** - * snd_soc_get_xr_sx - signed multi register get callback - * @kcontrol: mreg control - * @ucontrol: control element information - * - * Callback to get the value of a control that can span - * multiple codec registers which together forms a single - * signed value in a MSB/LSB manner. The control supports - * specifying total no of bits used to allow for bitfields - * across the multiple codec registers. - * - * Returns 0 for success. - */ -int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mreg_control *mc = - (struct soc_mreg_control *)kcontrol->private_value; - unsigned int regbase = mc->regbase; - unsigned int regcount = mc->regcount; - unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; - unsigned int regwmask = (1<invert; - unsigned long mask = (1UL<nbits)-1; - long min = mc->min; - long max = mc->max; - long val = 0; - unsigned int regval; - unsigned int i; - int ret; - - for (i = 0; i < regcount; i++) { - ret = snd_soc_component_read(component, regbase+i, ®val); - if (ret) - return ret; - val |= (regval & regwmask) << (regwshift*(regcount-i-1)); - } - val &= mask; - if (min < 0 && val > max) - val |= ~mask; - if (invert) - val = max - val; - ucontrol->value.integer.value[0] = val; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); - -/** - * snd_soc_put_xr_sx - signed multi register get callback - * @kcontrol: mreg control - * @ucontrol: control element information - * - * Callback to set the value of a control that can span - * multiple codec registers which together forms a single - * signed value in a MSB/LSB manner. The control supports - * specifying total no of bits used to allow for bitfields - * across the multiple codec registers. - * - * Returns 0 for success. - */ -int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mreg_control *mc = - (struct soc_mreg_control *)kcontrol->private_value; - unsigned int regbase = mc->regbase; - unsigned int regcount = mc->regcount; - unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; - unsigned int regwmask = (1<invert; - unsigned long mask = (1UL<nbits)-1; - long max = mc->max; - long val = ucontrol->value.integer.value[0]; - unsigned int i, regval, regmask; - int err; - - if (invert) - val = max - val; - val &= mask; - for (i = 0; i < regcount; i++) { - regval = (val >> (regwshift*(regcount-i-1))) & regwmask; - regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; - err = snd_soc_component_update_bits(component, regbase+i, - regmask, regval); - if (err < 0) - return err; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); - -/** - * snd_soc_get_strobe - strobe get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback get the value of a strobe mixer control. - * - * Returns 0 for success. - */ -int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = 1 << shift; - unsigned int invert = mc->invert != 0; - unsigned int val; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret) - return ret; - - val &= mask; - - if (shift != 0 && val != 0) - val = val >> shift; - ucontrol->value.enumerated.item[0] = val ^ invert; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_strobe); - -/** - * snd_soc_put_strobe - strobe put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback strobe a register bit to high then low (or the inverse) - * in one pass of a single mixer enum control. - * - * Returns 1 for success. - */ -int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = 1 << shift; - unsigned int invert = mc->invert != 0; - unsigned int strobe = ucontrol->value.enumerated.item[0] != 0; - unsigned int val1 = (strobe ^ invert) ? mask : 0; - unsigned int val2 = (strobe ^ invert) ? 0 : mask; - int err; - - err = snd_soc_component_update_bits(component, reg, mask, val1); - if (err < 0) - return err; - - return snd_soc_component_update_bits(component, reg, mask, val2); -} -EXPORT_SYMBOL_GPL(snd_soc_put_strobe); - -/** * snd_soc_dai_set_sysclk - configure DAI system or master clock. * @dai: DAI * @clk_id: DAI specific clock ID diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c new file mode 100644 index 0000000..100d92b --- /dev/null +++ b/sound/soc/soc-ops.c @@ -0,0 +1,952 @@ +/* + * soc-ops.c -- Generic ASoC operations + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * Copyright (C) 2010 Slimlogic Ltd. + * Copyright (C) 2010 Texas Instruments Inc. + * + * Author: Liam Girdwood + * with code, comments and ideas from :- + * Richard Purdie + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * snd_soc_info_enum_double - enumerated double mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a double enumerated + * mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2, + e->items, e->texts); +} +EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); + +/** + * snd_soc_get_enum_double - enumerated double mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a double enumerated mixer. + * + * Returns 0 for success. + */ +int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, item; + unsigned int reg_val; + int ret; + + ret = snd_soc_component_read(component, e->reg, ®_val); + if (ret) + return ret; + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[0] = item; + if (e->shift_l != e->shift_r) { + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[1] = item; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); + +/** + * snd_soc_put_enum_double - enumerated double mixer put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a double enumerated mixer. + * + * Returns 0 for success. + */ +int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val; + unsigned int mask; + + if (item[0] >= e->items) + return -EINVAL; + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + mask = e->mask << e->shift_l; + if (e->shift_l != e->shift_r) { + if (item[1] >= e->items) + return -EINVAL; + val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; + mask |= e->mask << e->shift_r; + } + + return snd_soc_component_update_bits(component, e->reg, mask, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); + +/** + * snd_soc_read_signed - Read a codec register and interprete as signed value + * @component: component + * @reg: Register to read + * @mask: Mask to use after shifting the register value + * @shift: Right shift of register value + * @sign_bit: Bit that describes if a number is negative or not. + * @signed_val: Pointer to where the read value should be stored + * + * This functions reads a codec register. The register value is shifted right + * by 'shift' bits and masked with the given 'mask'. Afterwards it translates + * the given registervalue into a signed integer if sign_bit is non-zero. + * + * Returns 0 on sucess, otherwise an error value + */ +static int snd_soc_read_signed(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int shift, + unsigned int sign_bit, int *signed_val) +{ + int ret; + unsigned int val; + + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; + + val = (val >> shift) & mask; + + if (!sign_bit) { + *signed_val = val; + return 0; + } + + /* non-negative number */ + if (!(val & BIT(sign_bit))) { + *signed_val = val; + return 0; + } + + ret = val; + + /* + * The register most probably does not contain a full-sized int. + * Instead we have an arbitrary number of bits in a signed + * representation which has to be translated into a full-sized int. + * This is done by filling up all bits above the sign-bit. + */ + ret |= ~((int)(BIT(sign_bit) - 1)); + + *signed_val = ret; + + return 0; +} + +/** + * snd_soc_info_volsw - single mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a single mixer control, or a double + * mixer control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int platform_max; + + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; + + if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + + uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = platform_max - mc->min; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw); + +/** + * snd_soc_get_volsw - single mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a single mixer control, or a double mixer + * control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int sign_bit = mc->sign_bit; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + int val; + int ret; + + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = val - min; + if (invert) + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + + if (snd_soc_volsw_is_stereo(mc)) { + if (reg == reg2) + ret = snd_soc_read_signed(component, reg, mask, rshift, + sign_bit, &val); + else + ret = snd_soc_read_signed(component, reg2, mask, shift, + sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = val - min; + if (invert) + ucontrol->value.integer.value[1] = + max - ucontrol->value.integer.value[1]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw); + +/** + * snd_soc_put_volsw - single mixer put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a single mixer control, or a double mixer + * control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + unsigned int sign_bit = mc->sign_bit; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + int err; + bool type_2r = false; + unsigned int val2 = 0; + unsigned int val, val_mask; + + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + val = ((ucontrol->value.integer.value[0] + min) & mask); + if (invert) + val = max - val; + val_mask = mask << shift; + val = val << shift; + if (snd_soc_volsw_is_stereo(mc)) { + val2 = ((ucontrol->value.integer.value[1] + min) & mask); + if (invert) + val2 = max - val2; + if (reg == reg2) { + val_mask |= mask << rshift; + val |= val2 << rshift; + } else { + val2 = val2 << shift; + type_2r = true; + } + } + err = snd_soc_component_update_bits(component, reg, val_mask, val); + if (err < 0) + return err; + + if (type_2r) + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); + + return err; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw); + +/** + * snd_soc_get_volsw_sx - single mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a single mixer control, or a double mixer + * control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mask = (1 << (fls(min + max) - 1)) - 1; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; + + ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask; + + if (snd_soc_volsw_is_stereo(mc)) { + ret = snd_soc_component_read(component, reg2, &val); + if (ret < 0) + return ret; + + val = ((val >> rshift) - min) & mask; + ucontrol->value.integer.value[1] = val; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); + +/** + * snd_soc_put_volsw_sx - double mixer set callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a double mixer control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mask = (1 << (fls(min + max) - 1)) - 1; + int err = 0; + unsigned int val, val_mask, val2 = 0; + + val_mask = mask << shift; + val = (ucontrol->value.integer.value[0] + min) & mask; + val = val << shift; + + err = snd_soc_component_update_bits(component, reg, val_mask, val); + if (err < 0) + return err; + + if (snd_soc_volsw_is_stereo(mc)) { + val_mask = mask << rshift; + val2 = (ucontrol->value.integer.value[1] + min) & mask; + val2 = val2 << rshift; + + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); + +/** + * snd_soc_info_volsw_range - single mixer info callback with range. + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information, within a range, about a single + * mixer control. + * + * returns 0 for success. + */ +int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int platform_max; + int min = mc->min; + + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = platform_max - min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); + +/** + * snd_soc_put_volsw_range - single mixer put value callback with range. + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value, within a range, for a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int rreg = mc->rreg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val, val_mask; + int ret; + + if (invert) + val = (max - ucontrol->value.integer.value[0]) & mask; + else + val = ((ucontrol->value.integer.value[0] + min) & mask); + val_mask = mask << shift; + val = val << shift; + + ret = snd_soc_component_update_bits(component, reg, val_mask, val); + if (ret < 0) + return ret; + + if (snd_soc_volsw_is_stereo(mc)) { + if (invert) + val = (max - ucontrol->value.integer.value[1]) & mask; + else + val = ((ucontrol->value.integer.value[1] + min) & mask); + val_mask = mask << shift; + val = val << shift; + + ret = snd_soc_component_update_bits(component, rreg, val_mask, + val); + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); + +/** + * snd_soc_get_volsw_range - single mixer get callback with range + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value, within a range, of a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int rreg = mc->rreg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + else + ucontrol->value.integer.value[0] = + ucontrol->value.integer.value[0] - min; + + if (snd_soc_volsw_is_stereo(mc)) { + ret = snd_soc_component_read(component, rreg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[1] = + max - ucontrol->value.integer.value[1]; + else + ucontrol->value.integer.value[1] = + ucontrol->value.integer.value[1] - min; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); + +/** + * snd_soc_limit_volume - Set new limit to an existing volume control. + * + * @codec: where to look for the control + * @name: Name of the control + * @max: new maximum limit + * + * Return 0 for success, else error. + */ +int snd_soc_limit_volume(struct snd_soc_codec *codec, + const char *name, int max) +{ + struct snd_card *card = codec->component.card->snd_card; + struct snd_kcontrol *kctl; + struct soc_mixer_control *mc; + int found = 0; + int ret = -EINVAL; + + /* Sanity check for name and max */ + if (unlikely(!name || max <= 0)) + return -EINVAL; + + list_for_each_entry(kctl, &card->controls, list) { + if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { + found = 1; + break; + } + } + if (found) { + mc = (struct soc_mixer_control *)kctl->private_value; + if (max <= mc->max) { + mc->platform_max = max; + ret = 0; + } + } + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_limit_volume); + +int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes *params = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = params->num_regs * component->val_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_info); + +int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes *params = (void *)kcontrol->private_value; + int ret; + + if (component->regmap) + ret = regmap_raw_read(component->regmap, params->base, + ucontrol->value.bytes.data, + params->num_regs * component->val_bytes); + else + ret = -EINVAL; + + /* Hide any masked bytes to ensure consistent data reporting */ + if (ret == 0 && params->mask) { + switch (component->val_bytes) { + case 1: + ucontrol->value.bytes.data[0] &= ~params->mask; + break; + case 2: + ((u16 *)(&ucontrol->value.bytes.data))[0] + &= cpu_to_be16(~params->mask); + break; + case 4: + ((u32 *)(&ucontrol->value.bytes.data))[0] + &= cpu_to_be32(~params->mask); + break; + default: + return -EINVAL; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_get); + +int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes *params = (void *)kcontrol->private_value; + int ret, len; + unsigned int val, mask; + void *data; + + if (!component->regmap || !params->num_regs) + return -EINVAL; + + len = params->num_regs * component->val_bytes; + + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + /* + * If we've got a mask then we need to preserve the register + * bits. We shouldn't modify the incoming data so take a + * copy. + */ + if (params->mask) { + ret = regmap_read(component->regmap, params->base, &val); + if (ret != 0) + goto out; + + val &= params->mask; + + switch (component->val_bytes) { + case 1: + ((u8 *)data)[0] &= ~params->mask; + ((u8 *)data)[0] |= val; + break; + case 2: + mask = ~params->mask; + ret = regmap_parse_val(component->regmap, + &mask, &mask); + if (ret != 0) + goto out; + + ((u16 *)data)[0] &= mask; + + ret = regmap_parse_val(component->regmap, + &val, &val); + if (ret != 0) + goto out; + + ((u16 *)data)[0] |= val; + break; + case 4: + mask = ~params->mask; + ret = regmap_parse_val(component->regmap, + &mask, &mask); + if (ret != 0) + goto out; + + ((u32 *)data)[0] &= mask; + + ret = regmap_parse_val(component->regmap, + &val, &val); + if (ret != 0) + goto out; + + ((u32 *)data)[0] |= val; + break; + default: + ret = -EINVAL; + goto out; + } + } + + ret = regmap_raw_write(component->regmap, params->base, + data, len); + +out: + kfree(data); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_put); + +int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); + +int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + unsigned int count = size < params->max ? size : params->max; + int ret = -ENXIO; + + switch (op_flag) { + case SNDRV_CTL_TLV_OP_READ: + if (params->get) + ret = params->get(tlv, count); + break; + case SNDRV_CTL_TLV_OP_WRITE: + if (params->put) + ret = params->put(tlv, count); + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback); + +/** + * snd_soc_info_xr_sx - signed multi register info callback + * @kcontrol: mreg control + * @uinfo: control element information + * + * Callback to provide information of a control that can + * span multiple codec registers which together + * forms a single signed value in a MSB/LSB manner. + * + * Returns 0 for success. + */ +int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); + +/** + * snd_soc_get_xr_sx - signed multi register get callback + * @kcontrol: mreg control + * @ucontrol: control element information + * + * Callback to get the value of a control that can span + * multiple codec registers which together forms a single + * signed value in a MSB/LSB manner. The control supports + * specifying total no of bits used to allow for bitfields + * across the multiple codec registers. + * + * Returns 0 for success. + */ +int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + unsigned int regbase = mc->regbase; + unsigned int regcount = mc->regcount; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; + unsigned int regwmask = (1<invert; + unsigned long mask = (1UL<nbits)-1; + long min = mc->min; + long max = mc->max; + long val = 0; + unsigned int regval; + unsigned int i; + int ret; + + for (i = 0; i < regcount; i++) { + ret = snd_soc_component_read(component, regbase+i, ®val); + if (ret) + return ret; + val |= (regval & regwmask) << (regwshift*(regcount-i-1)); + } + val &= mask; + if (min < 0 && val > max) + val |= ~mask; + if (invert) + val = max - val; + ucontrol->value.integer.value[0] = val; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); + +/** + * snd_soc_put_xr_sx - signed multi register get callback + * @kcontrol: mreg control + * @ucontrol: control element information + * + * Callback to set the value of a control that can span + * multiple codec registers which together forms a single + * signed value in a MSB/LSB manner. The control supports + * specifying total no of bits used to allow for bitfields + * across the multiple codec registers. + * + * Returns 0 for success. + */ +int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + unsigned int regbase = mc->regbase; + unsigned int regcount = mc->regcount; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; + unsigned int regwmask = (1<invert; + unsigned long mask = (1UL<nbits)-1; + long max = mc->max; + long val = ucontrol->value.integer.value[0]; + unsigned int i, regval, regmask; + int err; + + if (invert) + val = max - val; + val &= mask; + for (i = 0; i < regcount; i++) { + regval = (val >> (regwshift*(regcount-i-1))) & regwmask; + regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; + err = snd_soc_component_update_bits(component, regbase+i, + regmask, regval); + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); + +/** + * snd_soc_get_strobe - strobe get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback get the value of a strobe mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = 1 << shift; + unsigned int invert = mc->invert != 0; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + val &= mask; + + if (shift != 0 && val != 0) + val = val >> shift; + ucontrol->value.enumerated.item[0] = val ^ invert; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_strobe); + +/** + * snd_soc_put_strobe - strobe put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback strobe a register bit to high then low (or the inverse) + * in one pass of a single mixer enum control. + * + * Returns 1 for success. + */ +int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = 1 << shift; + unsigned int invert = mc->invert != 0; + unsigned int strobe = ucontrol->value.enumerated.item[0] != 0; + unsigned int val1 = (strobe ^ invert) ? mask : 0; + unsigned int val2 = (strobe ^ invert) ? 0 : mask; + int err; + + err = snd_soc_component_update_bits(component, reg, mask, val1); + if (err < 0) + return err; + + return snd_soc_component_update_bits(component, reg, mask, val2); +} +EXPORT_SYMBOL_GPL(snd_soc_put_strobe); -- cgit v0.10.2 From 36bcecd0a73eb4a11c9748bc96c2d254d5364d12 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Oct 2014 13:55:44 +0200 Subject: ASoC: davinci-mcasp: Correct TX start sequence Follow the sequence described in the TRMs when starting TX. This sequence will make sure that we are not facing with initial channel swap caused by no data available in McASP for transmit. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 0eed9b1..e1c1f40 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -183,31 +183,24 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) static void mcasp_start_tx(struct davinci_mcasp *mcasp) { - u8 offset = 0, i; u32 cnt; + /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + /* Activate serializer(s) */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); - 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; - } - } - - /* wait for TX ready */ + /* wait for XDATA to be cleared */ cnt = 0; - while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) & - TXSTATE) && (cnt < 100000)) + while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & + ~XRDATA) && (cnt < 100000)) cnt++; - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); + /* Release TX state machine */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); + /* Release Frame Sync generator */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); } static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 98fbc45..9737108 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -253,6 +253,12 @@ #define TXFSRST BIT(12) /* Frame Sync Generator Reset */ /* + * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits + * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits + */ +#define XRDATA BIT(5) /* Transmit/Receive data ready */ + +/* * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits */ #define MUTENA(val) (val) -- cgit v0.10.2 From 4498273551d4e27c93d3585bc7e4676623c46da8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Oct 2014 13:55:45 +0200 Subject: ASoC: davinci-mcasp: Correct RX start sequence Follow the sequence described in the TRMs when starting RX. Write to RXBUF register was not correct and there is no need to release the RX state machine/Receive frame sync generator twice. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index e1c1f40..142da94 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -154,9 +154,9 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) static void mcasp_start_rx(struct davinci_mcasp *mcasp) { + /* Start clocks */ 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 @@ -167,16 +167,12 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); } + /* Activate serializer(s) */ 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); - + /* Release RX state machine */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + /* Release Frame Sync generator */ 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); } -- cgit v0.10.2 From 0380866a9131646787dc60d19a6d5d2c22dffdd1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Oct 2014 13:55:46 +0200 Subject: ASoC: davinci-mcasp: When stopping TX/RX stop the AFIFO as the last step The AFIFO should not be stopped (or started for that matter) when McASP is running since it can cause unpredictable issues because we are switching off AFIFO for the direction which was handling the requests from McASP and was generating DMA request toward the system DMA. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 142da94..002351f 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -233,6 +233,12 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + + if (mcasp->rxnumevt) { /* disable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + } } static void mcasp_stop_tx(struct davinci_mcasp *mcasp) @@ -248,27 +254,22 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + + if (mcasp->txnumevt) { /* disable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + } } static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) { - u32 reg; - mcasp->streams--; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (mcasp->txnumevt) { /* disable FIFO */ - reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - } + if (stream == SNDRV_PCM_STREAM_PLAYBACK) mcasp_stop_tx(mcasp); - } else { - if (mcasp->rxnumevt) { /* disable FIFO */ - reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - } + else mcasp_stop_rx(mcasp); - } } static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, -- cgit v0.10.2 From bb372af0f7040fb637bfe0859aaa0ba49018506b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Oct 2014 13:55:47 +0200 Subject: ASoC: davinci-mcasp: Move the AFIFO related code under start_tx/rx functions In this way the start code for tx/rx going to be located at the same place. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 002351f..6b1bfd9 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -154,6 +154,13 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) static void mcasp_start_rx(struct davinci_mcasp *mcasp) { + if (mcasp->rxnumevt) { /* enable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); + } + /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); @@ -181,6 +188,13 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) { u32 cnt; + if (mcasp->txnumevt) { /* enable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); + } + /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); @@ -201,25 +215,12 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) { - u32 reg; - mcasp->streams++; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - 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); - } + if (stream == SNDRV_PCM_STREAM_PLAYBACK) mcasp_start_tx(mcasp); - } else { - 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); - } + else mcasp_start_rx(mcasp); - } } static void mcasp_stop_rx(struct davinci_mcasp *mcasp) -- cgit v0.10.2 From 137f6d541ae75b3769c4c67e61c25340789b3cbc Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Wed, 29 Oct 2014 15:40:27 +0000 Subject: ASoC: Intel: dw_pdata can be static sound/soc/intel/sst-firmware.c:172:29: sparse: symbol 'dw_pdata' was not declared. Should it be static? Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index 692a6ae..35788ad 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -169,7 +169,7 @@ err: return ret; } -struct dw_dma_platform_data dw_pdata = { +static struct dw_dma_platform_data dw_pdata = { .is_private = 1, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, .chan_priority = CHAN_PRIORITY_ASCENDING, -- cgit v0.10.2 From d96c53a193dd65380452c8e9f6dcf15cf829c7dc Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 29 Oct 2014 15:40:28 +0000 Subject: ASoC: Intel: Add generic support for DSP wake, sleep and stall Add generic functions to support DSP sleep, wake and stall. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h index be81b86..b9da030 100644 --- a/sound/soc/intel/sst-dsp-priv.h +++ b/sound/soc/intel/sst-dsp-priv.h @@ -36,6 +36,9 @@ struct sst_ops { /* DSP core boot / reset */ void (*boot)(struct sst_dsp *); void (*reset)(struct sst_dsp *); + int (*wake)(struct sst_dsp *); + void (*sleep)(struct sst_dsp *); + void (*stall)(struct sst_dsp *); /* Shim IO */ void (*write)(void __iomem *addr, u32 offset, u32 value); diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c index d0fc685..86e4108 100644 --- a/sound/soc/intel/sst-dsp.c +++ b/sound/soc/intel/sst-dsp.c @@ -245,6 +245,29 @@ int sst_dsp_boot(struct sst_dsp *sst) } EXPORT_SYMBOL_GPL(sst_dsp_boot); +int sst_dsp_wake(struct sst_dsp *sst) +{ + if (sst->ops->wake) + return sst->ops->wake(sst); + + return 0; +} +EXPORT_SYMBOL_GPL(sst_dsp_wake); + +void sst_dsp_sleep(struct sst_dsp *sst) +{ + if (sst->ops->sleep) + sst->ops->sleep(sst); +} +EXPORT_SYMBOL_GPL(sst_dsp_sleep); + +void sst_dsp_stall(struct sst_dsp *sst) +{ + if (sst->ops->stall) + sst->ops->stall(sst); +} +EXPORT_SYMBOL_GPL(sst_dsp_stall); + void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) { sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY); diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h index 17ee923..7bb4820 100644 --- a/sound/soc/intel/sst-dsp.h +++ b/sound/soc/intel/sst-dsp.h @@ -245,6 +245,10 @@ void sst_memcpy_fromio_32(struct sst_dsp *sst, /* DSP reset & boot */ void sst_dsp_reset(struct sst_dsp *sst); int sst_dsp_boot(struct sst_dsp *sst); +int sst_dsp_wake(struct sst_dsp *sst); +void sst_dsp_sleep(struct sst_dsp *sst); +void sst_dsp_stall(struct sst_dsp *sst); + /* DMA */ int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id); void sst_dsp_dma_put_channel(struct sst_dsp *dsp); -- cgit v0.10.2 From 6b7b4b8941b727af5fdc73b6a0910ede4c06cf11 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 29 Oct 2014 17:40:41 +0000 Subject: ASoC: Intel: Add PM support to the HSW/BDW DSP core. Add support for PM wake, sleep and stall calls to the core HSW/BDW driver. This includes reworking the reset and boot code and adding new calls for setting D3/D0 state. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c index 5058dc8..86aea34 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -247,8 +247,67 @@ static irqreturn_t hsw_irq(int irq, void *context) return ret; } -static void hsw_boot(struct sst_dsp *sst) +static void hsw_set_dsp_D3(struct sst_dsp *sst) +{ + u32 val; + + /* switch off audio PLL, DRAM & IRAM blocks */ + val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); + val |= SST_VDRTCL0_APLLSE_MASK | SST_VDRTCL0_DSRAMPGE_MASK | + SST_VDRTCL0_ISRAMPGE_MASK; + writel(val, sst->addr.pci_cfg + SST_VDRTCTL0); + + /* Set D3 state */ + val = readl(sst->addr.pci_cfg + SST_PMCS); + val |= SST_PMCS_PS_MASK; + writel(val, sst->addr.pci_cfg + SST_PMCS); +} + +static void hsw_reset(struct sst_dsp *sst) +{ + /* put DSP into reset and stall */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, + SST_CSR_RST | SST_CSR_STALL, + SST_CSR_RST | SST_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, + SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL); +} + +static int hsw_set_dsp_D0(struct sst_dsp *sst) { + int tries = 10; + u32 reg; + + /* Set D0 state */ + reg = readl(sst->addr.pci_cfg + SST_PMCS); + reg &= ~SST_PMCS_PS_MASK; + writel(reg, sst->addr.pci_cfg + SST_PMCS); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK; + if (reg == 0) + goto finish; + + msleep(1); + } + + return -ENODEV; + +finish: + hsw_reset(sst); + + /* switch on audio PLL, DRAM & IRAM blocks */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0); + reg &= ~(SST_VDRTCL0_APLLSE_MASK | SST_VDRTCL0_DSRAMPGE_MASK | + SST_VDRTCL0_ISRAMPGE_MASK); + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0); + /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0); @@ -267,30 +326,73 @@ static void hsw_boot(struct sst_dsp *sst) sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1, SST_CSR2_SDFD_SSP1); - /* enable DMA engine 0,1 all channels to access host memory */ - sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC, - SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff), - SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff)); + /* set on-demond mode on engine 0,1 for all channels */ + sst_dsp_shim_update_bits(sst, SST_HMDC, + SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, + SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH); + + /* Enable Interrupt from both sides */ + sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE), + 0x0); + sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY | + SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0); + + /* clear IPC registers */ + sst_dsp_shim_write(sst, SST_IPCX, 0x0); + sst_dsp_shim_write(sst, SST_IPCD, 0x0); + sst_dsp_shim_write(sst, 0x80, 0x6); + sst_dsp_shim_write(sst, 0xe0, 0x300a); /* disable all clock gating */ writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2); + return 0; +} + +static void hsw_boot(struct sst_dsp *sst) +{ + /* set oportunistic mode on engine 0,1 for all channels */ + sst_dsp_shim_update_bits(sst, SST_HMDC, + SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0); + /* set DSP to RUN */ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0); } -static void hsw_reset(struct sst_dsp *sst) +static void hsw_stall(struct sst_dsp *sst) { + /* stall DSP */ + sst_dsp_shim_update_bits(sst, SST_CSR, + SST_CSR_24MHZ_LPCS | SST_CSR_STALL, + SST_CSR_STALL | SST_CSR_24MHZ_LPCS); +} + +static void hsw_sleep(struct sst_dsp *sst) +{ + dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n"); + /* put DSP into reset and stall */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, - SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL); + sst_dsp_shim_update_bits(sst, SST_CSR, + SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL, + SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS); - /* keep in reset for 10ms */ - mdelay(10); + hsw_set_dsp_D3(sst); + dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n"); +} - /* take DSP out of reset and keep stalled for FW loading */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, - SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL); +static int hsw_wake(struct sst_dsp *sst) +{ + int ret; + + dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n"); + + ret = hsw_set_dsp_D0(sst); + if (ret < 0) + return ret; + + dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n"); + + return 0; } struct sst_adsp_memregion { @@ -431,27 +533,6 @@ static struct sst_block_ops sst_hsw_ops = { .disable = hsw_block_disable, }; -static int hsw_enable_shim(struct sst_dsp *sst) -{ - int tries = 10; - u32 reg; - - /* enable shim */ - reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG); - writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG); - - /* check that ADSP shim is enabled */ - while (tries--) { - reg = sst_dsp_shim_read_unlocked(sst, SST_CSR); - if (reg != 0xffffffff) - return 0; - - msleep(1); - } - - return -ENODEV; -} - static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) { const struct sst_adsp_memregion *region; @@ -490,7 +571,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) } /* enable the DSP SHIM */ - ret = hsw_enable_shim(sst); + ret = hsw_set_dsp_D0(sst); if (ret < 0) { dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n"); return ret; @@ -500,10 +581,6 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) if (ret) return ret; - /* Enable Interrupt from both sides */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0); - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD, - (0x3 | 0x1 << 16 | 0x3 << 21), 0x0); /* register DSP memory blocks - ideally we should get this from ACPI */ for (i = 0; i < region_count; i++) { @@ -535,6 +612,9 @@ static void hsw_free(struct sst_dsp *sst) struct sst_ops haswell_ops = { .reset = hsw_reset, .boot = hsw_boot, + .stall = hsw_stall, + .wake = hsw_wake, + .sleep = hsw_sleep, .write = sst_shim32_write, .read = sst_shim32_read, .write64 = sst_shim32_write64, -- cgit v0.10.2 From aed3c7b77c85ed7060f1f56bfa909d2a86ab2a20 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 29 Oct 2014 17:40:42 +0000 Subject: ASoC: Intel: Add PM support to HSW/BDW IPC driver Add PM and RTD3 support to the HSW/BDW IPC driver. This patch saves and restores the DSP context, loads and unloads FW and drops any pending IPC messages after suspend. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 770d467..b37d3ee 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "sst-haswell-ipc.h" #include "sst-dsp.h" @@ -276,6 +277,7 @@ struct sst_hsw { struct sst_hsw_ipc_fw_version version; struct sst_module *scratch; bool fw_done; + struct sst_fw *sst_fw; /* stream */ struct list_head stream_list; @@ -289,6 +291,8 @@ struct sst_hsw { /* DX */ struct sst_hsw_ipc_dx_reply dx; + void *dx_context; + dma_addr_t dx_context_paddr; /* boot */ wait_queue_head_t boot_wait; @@ -1707,6 +1711,237 @@ void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime) sst_module_runtime_free(runtime); } +#ifdef CONFIG_PM_RUNTIME +static int sst_hsw_dx_state_dump(struct sst_hsw *hsw) +{ + struct sst_dsp *sst = hsw->dsp; + u32 item, offset, size; + int ret = 0; + + trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS); + + if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) { + dev_err(hsw->dev, + "error: number of FW context regions greater than %d\n", + SST_HSW_MAX_DX_REGIONS); + memset(&hsw->dx, 0, sizeof(hsw->dx)); + return -EINVAL; + } + + ret = sst_dsp_dma_get_channel(sst, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret); + return ret; + } + + /* set on-demond mode on engine 0 channel 3 */ + sst_dsp_shim_update_bits(sst, SST_HMDC, + SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, + SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH); + + for (item = 0; item < hsw->dx.entries_no; item++) { + if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP + && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET + && hsw->dx.mem_info[item].offset < + DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) { + + offset = hsw->dx.mem_info[item].offset + - DSP_DRAM_ADDR_OFFSET; + size = (hsw->dx.mem_info[item].size + 3) & (~3); + + ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset, + sst->addr.lpe_base + offset, size); + if (ret < 0) { + dev_err(hsw->dev, + "error: FW context dump failed\n"); + memset(&hsw->dx, 0, sizeof(hsw->dx)); + goto out; + } + } + } + +out: + sst_dsp_dma_put_channel(sst); + return ret; +} + +static int sst_hsw_dx_state_restore(struct sst_hsw *hsw) +{ + struct sst_dsp *sst = hsw->dsp; + u32 item, offset, size; + int ret; + + for (item = 0; item < hsw->dx.entries_no; item++) { + if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP + && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET + && hsw->dx.mem_info[item].offset < + DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) { + + offset = hsw->dx.mem_info[item].offset + - DSP_DRAM_ADDR_OFFSET; + size = (hsw->dx.mem_info[item].size + 3) & (~3); + + ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset, + hsw->dx_context_paddr + offset, size); + if (ret < 0) { + dev_err(hsw->dev, + "error: FW context restore failed\n"); + return ret; + } + } + } + + return 0; +} + +static void sst_hsw_drop_all(struct sst_hsw *hsw) +{ + struct ipc_message *msg, *tmp; + unsigned long flags; + int tx_drop_cnt = 0, rx_drop_cnt = 0; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&hsw->dsp->spinlock, flags); + + list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) { + list_move(&msg->list, &hsw->empty_list); + tx_drop_cnt++; + } + + list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) { + list_move(&msg->list, &hsw->empty_list); + rx_drop_cnt++; + } + + spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); + + if (tx_drop_cnt || rx_drop_cnt) + dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n", + tx_drop_cnt, rx_drop_cnt); +} + +int sst_hsw_dsp_load(struct sst_hsw *hsw) +{ + struct sst_dsp *dsp = hsw->dsp; + int ret; + + dev_dbg(hsw->dev, "loading audio DSP...."); + + ret = sst_dsp_wake(dsp); + if (ret < 0) { + dev_err(hsw->dev, "error: failed to wake audio DSP\n"); + return -ENODEV; + } + + ret = sst_dsp_dma_get_channel(dsp, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret); + return ret; + } + + ret = sst_fw_reload(hsw->sst_fw); + if (ret < 0) { + dev_err(hsw->dev, "error: SST FW reload failed\n"); + sst_dsp_dma_put_channel(dsp); + return -ENOMEM; + } + + sst_dsp_dma_put_channel(dsp); + return 0; +} + +static int sst_hsw_dsp_restore(struct sst_hsw *hsw) +{ + struct sst_dsp *dsp = hsw->dsp; + int ret; + + dev_dbg(hsw->dev, "restoring audio DSP...."); + + ret = sst_dsp_dma_get_channel(dsp, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret); + return ret; + } + + ret = sst_hsw_dx_state_restore(hsw); + if (ret < 0) { + dev_err(hsw->dev, "error: SST FW context restore failed\n"); + sst_dsp_dma_put_channel(dsp); + return -ENOMEM; + } + sst_dsp_dma_put_channel(dsp); + + /* wait for DSP boot completion */ + sst_dsp_boot(dsp); + + return ret; +} + +int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw) +{ + int ret; + + dev_dbg(hsw->dev, "audio dsp runtime suspend\n"); + + ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx); + if (ret < 0) + return ret; + + sst_dsp_stall(hsw->dsp); + + ret = sst_hsw_dx_state_dump(hsw); + if (ret < 0) + return ret; + + sst_hsw_drop_all(hsw); + + return 0; +} + +int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw) +{ + sst_fw_unload(hsw->sst_fw); + sst_block_free_scratch(hsw->dsp); + + hsw->boot_complete = false; + + sst_dsp_sleep(hsw->dsp); + + return 0; +} + +int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) +{ + struct device *dev = hsw->dev; + int ret; + + dev_dbg(dev, "audio dsp runtime resume\n"); + + if (hsw->boot_complete) + return 1; /* tell caller no action is required */ + + ret = sst_hsw_dsp_restore(hsw); + if (ret < 0) + dev_err(dev, "error: audio DSP boot failure\n"); + + ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, + msecs_to_jiffies(IPC_BOOT_MSECS)); + if (ret == 0) { + dev_err(hsw->dev, "error: audio DSP boot timeout\n"); + return -EIO; + } + + /* Set ADSP SSP port settings */ + ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0, + SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, + SST_HSW_DEVICE_CLOCK_MASTER, 9); + if (ret < 0) + dev_err(dev, "error: SSP re-initialization failed\n"); + + return ret; +} +#endif + static int msg_empty_list_init(struct sst_hsw *hsw) { int i; @@ -1738,7 +1973,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_hsw_ipc_fw_version version; struct sst_hsw *hsw; - struct sst_fw *hsw_sst_fw; int ret; dev_dbg(dev, "initialising Audio DSP IPC\n"); @@ -1780,12 +2014,19 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) goto dsp_err; } + /* allocate DMA buffer for context storage */ + hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev, + SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL); + if (hsw->dx_context == NULL) { + ret = -ENOMEM; + goto dma_err; + } + /* keep the DSP in reset state for base FW loading */ sst_dsp_reset(hsw->dsp); - hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); - - if (hsw_sst_fw == NULL) { + hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); + if (hsw->sst_fw == NULL) { ret = -ENODEV; dev_err(dev, "error: failed to load firmware\n"); goto fw_err; @@ -1816,8 +2057,11 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) boot_err: sst_dsp_reset(hsw->dsp); - sst_fw_free(hsw_sst_fw); + sst_fw_free(hsw->sst_fw); fw_err: + dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, + hsw->dx_context, hsw->dx_context_paddr); +dma_err: sst_dsp_free(hsw->dsp); dsp_err: kthread_stop(hsw->tx_thread); @@ -1834,6 +2078,8 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata) sst_dsp_reset(hsw->dsp); sst_fw_free_all(hsw->dsp); + dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, + hsw->dx_context, hsw->dx_context_paddr); sst_dsp_free(hsw->dsp); kfree(hsw->scratch); kthread_stop(hsw->tx_thread); diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index fe6e63f..afd0ae1 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -23,6 +23,7 @@ #define SST_HSW_NO_CHANNELS 2 #define SST_HSW_MAX_DX_REGIONS 14 +#define SST_HSW_DX_CONTEXT_SIZE (640 * 1024) #define SST_HSW_FW_LOG_CONFIG_DWORDS 12 #define SST_HSW_GLOBAL_LOG 15 @@ -492,4 +493,10 @@ struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, int mod_id, int offset); void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime); +/* PM */ +int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw); +int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw); +int sst_hsw_dsp_load(struct sst_hsw *hsw); +int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw); + #endif -- cgit v0.10.2 From 2e4f75919e5a02d605b0d84425251654d48fb056 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 29 Oct 2014 17:40:43 +0000 Subject: ASoC: Intel: Add PM support to HSW/BDW PCM driver Add PM and RTD3 support to the HSW/BDW PCM driver. The PCM driver will now save DSP context and then power off the DSP when it's not in use. DSP power and context is then restored when it's next used. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h index 7bb4820..2753b85 100644 --- a/sound/soc/intel/sst-dsp.h +++ b/sound/soc/intel/sst-dsp.h @@ -30,6 +30,9 @@ #define SST_DMA_TYPE_DW 1 #define SST_DMA_TYPE_MID 2 +/* autosuspend delay 5s*/ +#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000) + /* SST Shim register map * The register naming can differ between products. Some products also * contain extra functionality. diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 522edef..4489a35 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,13 @@ static const u32 volume_map[] = { #define HSW_PCM_PERIODS_MAX 64 #define HSW_PCM_PERIODS_MIN 2 +#define HSW_PCM_DAI_ID_SYSTEM 0 +#define HSW_PCM_DAI_ID_OFFLOAD0 1 +#define HSW_PCM_DAI_ID_OFFLOAD1 2 +#define HSW_PCM_DAI_ID_LOOPBACK 3 +#define HSW_PCM_DAI_ID_CAPTURE 4 + + static const struct snd_pcm_hardware hsw_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -99,6 +107,8 @@ struct hsw_pcm_data { int dai_id; struct sst_hsw_stream *stream; struct sst_module_runtime *runtime; + struct sst_module_runtime_context context; + struct snd_pcm *hsw_pcm; u32 volume[2]; struct snd_pcm_substream *substream; struct snd_compr_stream *cstream; @@ -108,10 +118,18 @@ struct hsw_pcm_data { int persistent_offset; }; +enum hsw_pm_state { + HSW_PM_STATE_D3 = 0, + HSW_PM_STATE_D0 = 1, +}; + /* private data for the driver */ struct hsw_priv_data { /* runtime DSP */ struct sst_hsw *hsw; + struct device *dev; + enum hsw_pm_state pm_state; + struct snd_soc_card *soc_card; /* page tables */ struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; @@ -145,21 +163,25 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value) static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(platform); struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; struct sst_hsw *hsw = pdata->hsw; u32 volume; mutex_lock(&pcm_data->mutex); + pm_runtime_get_sync(pdata->dev); if (!pcm_data->stream) { pcm_data->volume[0] = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); pcm_data->volume[1] = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); mutex_unlock(&pcm_data->mutex); return 0; } @@ -175,6 +197,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume); } + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); mutex_unlock(&pcm_data->mutex); return 0; } @@ -182,21 +206,25 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(platform); struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; struct sst_hsw *hsw = pdata->hsw; u32 volume; mutex_lock(&pcm_data->mutex); + pm_runtime_get_sync(pdata->dev); if (!pcm_data->stream) { ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(pcm_data->volume[0]); ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(pcm_data->volume[1]); + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); mutex_unlock(&pcm_data->mutex); return 0; } @@ -205,6 +233,9 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume); ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); + + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); mutex_unlock(&pcm_data->mutex); return 0; @@ -213,11 +244,13 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, static int hsw_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); struct sst_hsw *hsw = pdata->hsw; u32 volume; + pm_runtime_get_sync(pdata->dev); + if (ucontrol->value.integer.value[0] == ucontrol->value.integer.value[1]) { @@ -232,23 +265,28 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol, sst_hsw_mixer_set_volume(hsw, 0, 1, volume); } + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); return 0; } static int hsw_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); struct sst_hsw *hsw = pdata->hsw; unsigned int volume = 0; + pm_runtime_get_sync(pdata->dev); sst_hsw_mixer_get_volume(hsw, 0, 0, &volume); ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); sst_hsw_mixer_get_volume(hsw, 0, 1, &volume); ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); return 0; } @@ -577,6 +615,7 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) pcm_data = &pdata->pcm[rtd->cpu_dai->id]; mutex_lock(&pcm_data->mutex); + pm_runtime_get_sync(pdata->dev); snd_soc_pcm_set_drvdata(rtd, pcm_data); pcm_data->substream = substream; @@ -587,6 +626,8 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) hsw_notify_pointer, pcm_data); if (pcm_data->stream == NULL) { dev_err(rtd->dev, "error: failed to create stream\n"); + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); mutex_unlock(&pcm_data->mutex); return -EINVAL; } @@ -626,6 +667,8 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream) pcm_data->stream = NULL; out: + pm_runtime_mark_last_busy(pdata->dev); + pm_runtime_put_autosuspend(pdata->dev); mutex_unlock(&pcm_data->mutex); return ret; } @@ -643,11 +686,11 @@ static struct snd_pcm_ops hsw_pcm_ops = { /* static mappings between PCMs and modules - may be dynamic in future */ static struct hsw_pcm_module_map mod_map[] = { - {0, SST_HSW_MODULE_PCM_SYSTEM}, /* "System Pin" */ - {1, SST_HSW_MODULE_PCM}, /* "Offload0 Pin" */ - {2, SST_HSW_MODULE_PCM}, /* "Offload1 Pin" */ - {3, SST_HSW_MODULE_PCM_REFERENCE}, /* "Loopback Pin" */ - {4, SST_HSW_MODULE_PCM_CAPTURE}, /* "Capture Pin" */ + {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM}, + {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM}, + {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM}, + {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE}, + {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE}, }; static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) @@ -659,6 +702,7 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) for (i = 0; i < ARRAY_SIZE(mod_map); i++) { pcm_data = &pdata->pcm[i]; + /* create new runtime module, use same offset if recreated */ pcm_data->runtime = sst_hsw_runtime_module_create(hsw, mod_map[i].mod_id, pcm_data->persistent_offset); if (pcm_data->runtime == NULL) @@ -700,6 +744,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) struct snd_pcm *pcm = rtd->pcm; struct snd_soc_platform *platform = rtd->platform; struct sst_pdata *pdata = dev_get_platdata(platform->dev); + struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev); struct device *dev = pdata->dma_dev; int ret = 0; @@ -716,6 +761,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) return ret; } } + priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm; return ret; } @@ -728,6 +774,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_driver hsw_dais[] = { { .name = "System Pin", + .id = HSW_PCM_DAI_ID_SYSTEM, .playback = { .stream_name = "System Playback", .channels_min = 2, @@ -739,6 +786,7 @@ static struct snd_soc_dai_driver hsw_dais[] = { { /* PCM */ .name = "Offload0 Pin", + .id = HSW_PCM_DAI_ID_OFFLOAD0, .playback = { .stream_name = "Offload0 Playback", .channels_min = 2, @@ -750,6 +798,7 @@ static struct snd_soc_dai_driver hsw_dais[] = { { /* PCM */ .name = "Offload1 Pin", + .id = HSW_PCM_DAI_ID_OFFLOAD1, .playback = { .stream_name = "Offload1 Playback", .channels_min = 2, @@ -760,6 +809,7 @@ static struct snd_soc_dai_driver hsw_dais[] = { }, { .name = "Loopback Pin", + .id = HSW_PCM_DAI_ID_LOOPBACK, .capture = { .stream_name = "Loopback Capture", .channels_min = 2, @@ -770,6 +820,7 @@ static struct snd_soc_dai_driver hsw_dais[] = { }, { .name = "Capture Pin", + .id = HSW_PCM_DAI_ID_CAPTURE, .capture = { .stream_name = "Analog Capture", .channels_min = 2, @@ -808,9 +859,21 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) { struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform); struct sst_pdata *pdata = dev_get_platdata(platform->dev); - struct device *dma_dev = pdata->dma_dev; + struct device *dma_dev, *dev; int i, ret = 0; + if (!pdata) + return -ENODEV; + + dev = platform->dev; + dma_dev = pdata->dma_dev; + + priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL); + priv_data->hsw = pdata->dsp; + priv_data->dev = platform->dev; + priv_data->pm_state = HSW_PM_STATE_D0; + priv_data->soc_card = platform->component.card; + /* allocate DSP buffer page tables */ for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { @@ -836,6 +899,13 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) /* allocate runtime modules */ hsw_pcm_create_modules(priv_data); + /* enable runtime PM with auto suspend */ + pm_runtime_set_autosuspend_delay(platform->dev, + SST_RUNTIME_SUSPEND_DELAY); + pm_runtime_use_autosuspend(platform->dev); + pm_runtime_enable(platform->dev); + pm_runtime_idle(platform->dev); + return 0; err: @@ -854,6 +924,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform) snd_soc_platform_get_drvdata(platform); int i; + pm_runtime_disable(platform->dev); + hsw_pcm_free_modules(priv_data); + for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { if (hsw_dais[i].playback.channels_min) snd_dma_free_pages(&priv_data->dmab[i][0]); @@ -931,10 +1004,180 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +#ifdef CONFIG_PM_RUNTIME + +static int hsw_pcm_runtime_idle(struct device *dev) +{ + return 0; +} + +static int hsw_pcm_runtime_suspend(struct device *dev) +{ + struct hsw_priv_data *pdata = dev_get_drvdata(dev); + struct sst_hsw *hsw = pdata->hsw; + + if (pdata->pm_state == HSW_PM_STATE_D3) + return 0; + + sst_hsw_dsp_runtime_suspend(hsw); + sst_hsw_dsp_runtime_sleep(hsw); + pdata->pm_state = HSW_PM_STATE_D3; + + return 0; +} + +static int hsw_pcm_runtime_resume(struct device *dev) +{ + struct hsw_priv_data *pdata = dev_get_drvdata(dev); + struct sst_hsw *hsw = pdata->hsw; + int ret; + + if (pdata->pm_state == HSW_PM_STATE_D0) + return 0; + + ret = sst_hsw_dsp_load(hsw); + if (ret < 0) { + dev_err(dev, "failed to reload %d\n", ret); + return ret; + } + + ret = hsw_pcm_create_modules(pdata); + if (ret < 0) { + dev_err(dev, "failed to create modules %d\n", ret); + return ret; + } + + ret = sst_hsw_dsp_runtime_resume(hsw); + if (ret < 0) + return ret; + else if (ret == 1) /* no action required */ + return 0; + + pdata->pm_state = HSW_PM_STATE_D0; + return ret; +} + +static void hsw_pcm_complete(struct device *dev) +{ + struct hsw_priv_data *pdata = dev_get_drvdata(dev); + struct sst_hsw *hsw = pdata->hsw; + struct hsw_pcm_data *pcm_data; + int i, err; + + if (pdata->pm_state == HSW_PM_STATE_D0) + return; + + err = sst_hsw_dsp_load(hsw); + if (err < 0) { + dev_err(dev, "failed to reload %d\n", err); + return; + } + + err = hsw_pcm_create_modules(pdata); + if (err < 0) { + dev_err(dev, "failed to create modules %d\n", err); + return; + } + + for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { + pcm_data = &pdata->pcm[i]; + + if (!pcm_data->substream) + continue; + + err = sst_module_runtime_restore(pcm_data->runtime, + &pcm_data->context); + if (err < 0) + dev_err(dev, "failed to restore context for PCM %d\n", i); + } + + snd_soc_resume(pdata->soc_card->dev); + + err = sst_hsw_dsp_runtime_resume(hsw); + if (err < 0) + return; + else if (err == 1) /* no action required */ + return; + + pdata->pm_state = HSW_PM_STATE_D0; + return; +} + +static int hsw_pcm_prepare(struct device *dev) +{ + struct hsw_priv_data *pdata = dev_get_drvdata(dev); + struct sst_hsw *hsw = pdata->hsw; + struct hsw_pcm_data *pcm_data; + int i, err; + + if (pdata->pm_state == HSW_PM_STATE_D3) + return 0; + /* suspend all active streams */ + for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { + pcm_data = &pdata->pcm[i]; + + if (!pcm_data->substream) + continue; + dev_dbg(dev, "suspending pcm %d\n", i); + snd_pcm_suspend_all(pcm_data->hsw_pcm); + + /* We need to wait until the DSP FW stops the streams */ + msleep(2); + } + + snd_soc_suspend(pdata->soc_card->dev); + snd_soc_poweroff(pdata->soc_card->dev); + + /* enter D3 state and stall */ + sst_hsw_dsp_runtime_suspend(hsw); + + /* preserve persistent memory */ + for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { + pcm_data = &pdata->pcm[i]; + + if (!pcm_data->substream) + continue; + + dev_dbg(dev, "saving context pcm %d\n", i); + err = sst_module_runtime_save(pcm_data->runtime, + &pcm_data->context); + if (err < 0) + dev_err(dev, "failed to save context for PCM %d\n", i); + } + + /* put the DSP to sleep */ + sst_hsw_dsp_runtime_sleep(hsw); + pdata->pm_state = HSW_PM_STATE_D3; + + return 0; +} + +#else +#define hsw_pcm_runtime_idle NULL +#define hsw_pcm_runtime_suspend NULL +#define hsw_pcm_runtime_resume NULL +#define hsw_pcm_runtime_complete NULL +#define hsw_pcm_runtime_prepare NULL +#endif + +static const struct dev_pm_ops hsw_pcm_pm = { + .runtime_idle = hsw_pcm_runtime_idle, + .runtime_suspend = hsw_pcm_runtime_suspend, + .runtime_resume = hsw_pcm_runtime_resume, + .prepare = hsw_pcm_prepare, + .complete = hsw_pcm_complete, +}; +#else +#define hsw_pcm_pm NULL +#endif + static struct platform_driver hsw_pcm_driver = { .driver = { .name = "haswell-pcm-audio", .owner = THIS_MODULE, + .pm = &hsw_pcm_pm, + }, .probe = hsw_pcm_dev_probe, -- cgit v0.10.2 From 35c0a8c0178ad3f6f14e1dd76f0317156deaae51 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 30 Oct 2014 21:21:52 +0800 Subject: ASoC: Intel: Fix block is enabled multiple times issue During FW parsing and loading, block_list_prepare() may be called for each raw data block copying and this may made the hsw_block_enable() called mutiple times, which increase block->users many times. The result of this is hsw_block_disable() can't power gated the related block when trying to free the blocks during suspend, and the power gating status also confused. Here check the block user status, only calling enable() for those blocks who has no user yet. Remember that this works correctlly on current case, where there are enough SRAM memory so different module won't share a memory block. For further usage, we may need restructure the struct sst_mem_block to save the module list who is using it. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index 35788ad..c451398 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -149,7 +149,7 @@ static int block_list_prepare(struct sst_dsp *dsp, /* enable each block so that's it'e ready for data */ list_for_each_entry(block, block_list, module_list) { - if (block->ops && block->ops->enable) { + if (block->ops && block->ops->enable && !block->users) { ret = block->ops->enable(block); if (ret < 0) { dev_err(dsp->dev, -- cgit v0.10.2 From b891f62fcd28a46ab0818cd9acbb5bbb20542ab6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 30 Oct 2014 14:34:00 +0000 Subject: ASoC: Intel: Add debug output when boot fails. Add the debug output from IPCD and IPCX when booting fails. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index b37d3ee..0ea7c3d 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1927,7 +1927,9 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, msecs_to_jiffies(IPC_BOOT_MSECS)); if (ret == 0) { - dev_err(hsw->dev, "error: audio DSP boot timeout\n"); + dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n", + sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD), + sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX)); return -EIO; } @@ -2038,7 +2040,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) msecs_to_jiffies(IPC_BOOT_MSECS)); if (ret == 0) { ret = -EIO; - dev_err(hsw->dev, "error: ADSP boot timeout\n"); + dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n", + sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD), + sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX)); goto boot_err; } -- cgit v0.10.2 From 35e03a884c41b8fecf77e20de89759deb7c9078a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 30 Oct 2014 14:58:19 +0000 Subject: ASoC: Intel: fix build with runtime PM disabled. Fix the following errors: All error/warnings: >> sound/soc/intel/sst-haswell-pcm.c:1168:13: error: 'hsw_pcm_prepare' undeclared here (not in a function) .prepare = hsw_pcm_prepare, ^ >> sound/soc/intel/sst-haswell-pcm.c:1169:14: error: 'hsw_pcm_complete' undeclared here (not in a function) .complete = hsw_pcm_complete, ^ Reported-by: kbuild test robot Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 0ea7c3d..ffd5728 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1711,7 +1711,7 @@ void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime) sst_module_runtime_free(runtime); } -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int sst_hsw_dx_state_dump(struct sst_hsw *hsw) { struct sst_dsp *sst = hsw->dsp; diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 4489a35..cd54dd9 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -1058,6 +1058,12 @@ static int hsw_pcm_runtime_resume(struct device *dev) return ret; } +#else +#define hsw_pcm_runtime_idle NULL +#define hsw_pcm_runtime_suspend NULL +#define hsw_pcm_runtime_resume NULL +#endif + static void hsw_pcm_complete(struct device *dev) { struct hsw_priv_data *pdata = dev_get_drvdata(dev); @@ -1153,14 +1159,6 @@ static int hsw_pcm_prepare(struct device *dev) return 0; } -#else -#define hsw_pcm_runtime_idle NULL -#define hsw_pcm_runtime_suspend NULL -#define hsw_pcm_runtime_resume NULL -#define hsw_pcm_runtime_complete NULL -#define hsw_pcm_runtime_prepare NULL -#endif - static const struct dev_pm_ops hsw_pcm_pm = { .runtime_idle = hsw_pcm_runtime_idle, .runtime_suspend = hsw_pcm_runtime_suspend, -- cgit v0.10.2 From 0d2135ecadb0b2eec5338a7587ba29724ddf612b Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 30 Oct 2014 22:57:58 +0800 Subject: ASoC: Intel: Work around to fix HW D3 potential crash issue When using clock gatings to save power, there are some known issues: 1. core clock gating (DCLCGE) must be disabled during D0 and D3 entry and updating SRAM banks (VDRTCTL0). 2. DSP trunk clock gating (DTCGE) can cause FW crashes, disable it in D0. To align with the new W/A flow from FW team, we must set VDRTCTL0.D3PGD to 1 (D3 power gating disabled) at first startup and keep it all the time. ADSP will be in D0 on first boot by BIOS part of WA. Required delays must be preserved (waiting for HW to stabilize, after enabling CCG, changing SRAM PG, D3PG). D3->D0: 1. Disable core clock gating (VDRTCTL2.DCLCGE = 0) 2. Enable other CG apart from DTCG and DCLCG (VDRTCTL2. DCLCGE and DTCGE = 0) 3. Disable D3PG (VDRTCTL0.D3PGD = 1) 4. Power up necessary SRAM and wait at least for 18 clock cycles for every bank you have powered up 5. Set D0 state(PMCS.PS = 0), wait for HW 6. Restore MCLK (clkctl.smos, disabled in D3 entry point 4) 7. Stall and reset core, set CSR 8. Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us 9. Unreset core 10.Load FW, configure PLL and other necessary things 11.Unstall core Changing SRAM PG during D0: 1. Disable core clock gating (VDRTCTL2.DCLCGE = 0) 2. Set PG mask 3. Wait at least for 18 clock cycles for every bank you have powered up 4. Enable core clock gating, delay 50 us D0->D3: 1. Disable core clock gating (DCLCGE = 0) 2. Stall and reset core 3. Power down entire SRAM and wait at least for 18 clock cycles for every bank (Enable SRAM PG (ISRAMPGE = 0x3FF, DSRAMPGE = 0xFFFFF, D3SRAMPGD = 0), remember about preserving VDRTCTL0.D3PGD = 1) 4. Shutdown PLL, disable MCLK(clkctl.smos = 0), Enable DTCG to save power 5. Set D3 state(PMCS.PS = 3), delay 50 us 6. Enable core clock gating, delay 50 us Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h index 2753b85..f291e32 100644 --- a/sound/soc/intel/sst-dsp.h +++ b/sound/soc/intel/sst-dsp.h @@ -159,12 +159,18 @@ #define SST_VDRTCTL3 0xaC /* VDRTCTL0 */ -#define SST_VDRTCL0_APLLSE_MASK 1 -#define SST_VDRTCL0_DSRAMPGE_SHIFT 16 -#define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT) -#define SST_VDRTCL0_ISRAMPGE_SHIFT 6 +#define SST_VDRTCL0_D3PGD (1 << 0) +#define SST_VDRTCL0_D3SRAMPGD (1 << 1) +#define SST_VDRTCL0_DSRAMPGE_SHIFT 12 +#define SST_VDRTCL0_DSRAMPGE_MASK (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT) +#define SST_VDRTCL0_ISRAMPGE_SHIFT 2 #define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT) +/* VDRTCTL2 */ +#define SST_VDRTCL2_DCLCGE (1 << 1) +#define SST_VDRTCL2_DTCGE (1 << 10) +#define SST_VDRTCL2_APLLSE_MASK (1 << 31) + /* PMCS */ #define SST_PMCS 0x84 #define SST_PMCS_PS_MASK 0x3 diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c index 86aea34..57039b0 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -250,17 +250,42 @@ static irqreturn_t hsw_irq(int irq, void *context) static void hsw_set_dsp_D3(struct sst_dsp *sst) { u32 val; + u32 reg; + + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE); + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); - /* switch off audio PLL, DRAM & IRAM blocks */ + /* enable power gating and switch off DRAM & IRAM blocks */ val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); - val |= SST_VDRTCL0_APLLSE_MASK | SST_VDRTCL0_DSRAMPGE_MASK | + val |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK; + val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD); writel(val, sst->addr.pci_cfg + SST_VDRTCTL0); - /* Set D3 state */ + /* switch off audio PLL */ + val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + val |= SST_VDRTCL2_APLLSE_MASK; + writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); + + /* disable MCLK(clkctl.smos = 0) */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL, + SST_CLKCTL_MASK, 0); + + /* Set D3 state, delay 50 us */ val = readl(sst->addr.pci_cfg + SST_PMCS); val |= SST_PMCS_PS_MASK; writel(val, sst->addr.pci_cfg + SST_PMCS); + udelay(50); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE; + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); + + udelay(50); + } static void hsw_reset(struct sst_dsp *sst) @@ -283,6 +308,16 @@ static int hsw_set_dsp_D0(struct sst_dsp *sst) int tries = 10; u32 reg; + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE); + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); + + /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0); + reg |= SST_VDRTCL0_D3PGD; + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0); + /* Set D0 state */ reg = readl(sst->addr.pci_cfg + SST_PMCS); reg &= ~SST_PMCS_PS_MASK; @@ -300,14 +335,6 @@ static int hsw_set_dsp_D0(struct sst_dsp *sst) return -ENODEV; finish: - hsw_reset(sst); - - /* switch on audio PLL, DRAM & IRAM blocks */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0); - reg &= ~(SST_VDRTCL0_APLLSE_MASK | SST_VDRTCL0_DSRAMPGE_MASK | - SST_VDRTCL0_ISRAMPGE_MASK); - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0); - /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0); @@ -322,6 +349,28 @@ finish: SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0, SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0); + /* Stall and reset core, set CSR */ + hsw_reset(sst); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE; + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); + + udelay(50); + + /* switch on audio PLL */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + reg &= ~SST_VDRTCL2_APLLSE_MASK; + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); + + /* set default power gating control, enable power gating control for all blocks. that is, + can't be accessed, please enable each block before accessing. */ + reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0); + reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK; + writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0); + + /* disable DMA finish function for SSP0 & SSP1 */ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1, SST_CSR2_SDFD_SSP1); @@ -343,9 +392,6 @@ finish: sst_dsp_shim_write(sst, 0x80, 0x6); sst_dsp_shim_write(sst, 0xe0, 0x300a); - /* disable all clock gating */ - writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2); - return 0; } @@ -497,6 +543,11 @@ static int hsw_block_enable(struct sst_mem_block *block) dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n", block->type, block->index, block->offset); + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + val &= ~SST_VDRTCL2_DCLCGE; + writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); + val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); bit = hsw_block_get_bit(block); writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0); @@ -504,6 +555,13 @@ static int hsw_block_enable(struct sst_mem_block *block) /* wait 18 DSP clock ticks */ udelay(10); + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + val |= SST_VDRTCL2_DCLCGE; + writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); + + udelay(50); + /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/ sst_mem_block_dummy_read(block); return 0; @@ -521,10 +579,26 @@ static int hsw_block_disable(struct sst_mem_block *block) dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n", block->type, block->index, block->offset); + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + val &= ~SST_VDRTCL2_DCLCGE; + writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); + + val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); bit = hsw_block_get_bit(block); writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0); + /* wait 18 DSP clock ticks */ + udelay(10); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); + val |= SST_VDRTCL2_DCLCGE; + writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); + + udelay(50); + return 0; } -- cgit v0.10.2 From 6879db7648b6b995122afa98df31778c7af0855d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 31 Oct 2014 14:52:16 +0800 Subject: ASoC: rt286: reduce power consumption This patch will optimize the power consumption of rt286. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 4aa555c..97daa80 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -36,11 +36,13 @@ struct rt286_priv { struct regmap *regmap; + struct snd_soc_codec *codec; struct rt286_platform_data pdata; struct i2c_client *i2c; struct snd_soc_jack *jack; struct delayed_work jack_detect_work; int sys_clk; + int clk_id; struct reg_default *index_cache; }; @@ -298,7 +300,6 @@ static int rt286_support_power_controls[] = { static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) { unsigned int val, buf; - int i; *hp = false; *mic = false; @@ -309,67 +310,44 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) if (*hp) { /* power on HV,VERF */ regmap_update_bits(rt286->regmap, - RT286_POWER_CTRL1, 0x1001, 0x0); + RT286_DC_GAIN, 0x200, 0x200); + + snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, + "HV"); + snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, + "VREF"); /* power LDO1 */ - regmap_update_bits(rt286->regmap, - RT286_POWER_CTRL2, 0x4, 0x4); - regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24); - regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val); + snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, + "LDO1"); + snd_soc_dapm_sync(&rt286->codec->dapm); - msleep(200); - i = 40; - while (((val & 0x0800) == 0) && (i > 0)) { - regmap_read(rt286->regmap, - RT286_CBJ_CTRL2, &val); - i--; - msleep(20); - } + regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24); + msleep(50); - if (0x0400 == (val & 0x0700)) { - *mic = false; + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xfcc0, 0xd400); + msleep(300); + regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val); - regmap_write(rt286->regmap, - RT286_SET_MIC1, 0x20); - /* power off HV,VERF */ - regmap_update_bits(rt286->regmap, - RT286_POWER_CTRL1, 0x1001, 0x1001); - regmap_update_bits(rt286->regmap, - RT286_A_BIAS_CTRL3, 0xc000, 0x0000); - regmap_update_bits(rt286->regmap, - RT286_CBJ_CTRL1, 0x0030, 0x0000); - regmap_update_bits(rt286->regmap, - RT286_A_BIAS_CTRL2, 0xc000, 0x0000); - } else if ((0x0200 == (val & 0x0700)) || - (0x0100 == (val & 0x0700))) { + if (0x0070 == (val & 0x0070)) { *mic = true; - regmap_update_bits(rt286->regmap, - RT286_A_BIAS_CTRL3, 0xc000, 0x8000); - regmap_update_bits(rt286->regmap, - RT286_CBJ_CTRL1, 0x0030, 0x0020); - regmap_update_bits(rt286->regmap, - RT286_A_BIAS_CTRL2, 0xc000, 0x8000); } else { - *mic = false; + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xfcc0, 0xe400); + msleep(300); + regmap_read(rt286->regmap, + RT286_CBJ_CTRL2, &val); + if (0x0070 == (val & 0x0070)) + *mic = true; + else + *mic = false; } - - regmap_update_bits(rt286->regmap, - RT286_MISC_CTRL1, - 0x0060, 0x0000); - } else { - regmap_update_bits(rt286->regmap, - RT286_MISC_CTRL1, - 0x0060, 0x0020); - regmap_update_bits(rt286->regmap, - RT286_A_BIAS_CTRL3, - 0xc000, 0x8000); regmap_update_bits(rt286->regmap, - RT286_CBJ_CTRL1, - 0x0030, 0x0020); - regmap_update_bits(rt286->regmap, - RT286_A_BIAS_CTRL2, - 0xc000, 0x8000); + RT286_DC_GAIN, 0x200, 0x0); + } else { *mic = false; + regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20); } } else { regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); @@ -378,6 +356,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) *mic = buf & 0x80000000; } + snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV"); + snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF"); + if (!*hp) + snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1"); + snd_soc_dapm_sync(&rt286->codec->dapm); + return 0; } @@ -415,6 +399,17 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) } EXPORT_SYMBOL_GPL(rt286_mic_detect); +static int is_mclk_mode(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(source->codec); + + if (rt286->clk_id == RT286_SCLK_S_MCLK) + return 1; + else + return 0; +} + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); @@ -568,7 +563,84 @@ static int rt286_adc_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt286_vref_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + RT286_CBJ_CTRL1, 0x0400, 0x0000); + mdelay(50); + break; + default: + return 0; + } + + return 0; +} + +static int rt286_ldo2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30); + break; + default: + return 0; + } + + return 0; +} + +static int rt286_mic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL3, 0xc000, 0x8000); + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL2, 0xc000, 0x8000); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL3, 0xc000, 0x0000); + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL2, 0xc000, 0x0000); + break; + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1, + 12, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1, + 0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1, + 13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM, + 0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + /* Input Lines */ SND_SOC_DAPM_INPUT("DMIC1 Pin"), SND_SOC_DAPM_INPUT("DMIC2 Pin"), @@ -642,6 +714,25 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = { }; static const struct snd_soc_dapm_route rt286_dapm_routes[] = { + {"ADC 0", NULL, "MCLK MODE", is_mclk_mode}, + {"ADC 1", NULL, "MCLK MODE", is_mclk_mode}, + {"Front", NULL, "MCLK MODE", is_mclk_mode}, + {"Surround", NULL, "MCLK MODE", is_mclk_mode}, + + {"HP Power", NULL, "LDO1"}, + {"HP Power", NULL, "LDO2"}, + + {"MIC1", NULL, "LDO1"}, + {"MIC1", NULL, "LDO2"}, + {"MIC1", NULL, "HV"}, + {"MIC1", NULL, "VREF"}, + {"MIC1", NULL, "MIC1 Input Buffer"}, + + {"SPO", NULL, "LDO1"}, + {"SPO", NULL, "LDO2"}, + {"SPO", NULL, "HV"}, + {"SPO", NULL, "VREF"}, + {"DMIC1", NULL, "DMIC1 Pin"}, {"DMIC2", NULL, "DMIC2 Pin"}, {"DMIC1", NULL, "DMIC Receiver"}, @@ -880,6 +971,7 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai, } rt286->sys_clk = freq; + rt286->clk_id = clk_id; return 0; } @@ -915,13 +1007,18 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: mdelay(10); + snd_soc_update_bits(codec, + RT286_CBJ_CTRL1, 0x0400, 0x0400); + snd_soc_update_bits(codec, + RT286_DC_GAIN, 0x200, 0x0); + break; case SND_SOC_BIAS_STANDBY: snd_soc_write(codec, RT286_SET_AUDIO_POWER, AC_PWRST_D3); snd_soc_update_bits(codec, - RT286_DC_GAIN, 0x200, 0x0); + RT286_CBJ_CTRL1, 0x0400, 0x0000); break; default: @@ -962,6 +1059,7 @@ static int rt286_probe(struct snd_soc_codec *codec) { struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + rt286->codec = codec; codec->dapm.bias_level = SND_SOC_BIAS_OFF; if (rt286->i2c->irq) { @@ -1152,7 +1250,6 @@ static int rt286_i2c_probe(struct i2c_client *i2c, if (!rt286->pdata.cbj_en) { regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000); regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816); - regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000); regmap_update_bits(rt286->regmap, RT286_CBJ_CTRL1, 0xf000, 0xb000); } else { @@ -1169,8 +1266,10 @@ static int rt286_i2c_probe(struct i2c_client *i2c, mdelay(10); - /*Power down LDO2*/ - regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0); + regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000); + /*Power down LDO, VREF*/ + regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0); + regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001); /*Set depop parameter*/ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a); -- cgit v0.10.2 From d004ebbef7292848f5f7ecae50824c04780baaac Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Wed, 29 Oct 2014 16:25:38 +0300 Subject: ASoC: tlv320aic23: make codecs selectable in Kconfig Now that manual selection of drivers for audio subsystem components is preferred AIC23 codec must be selectable in Kconfig to make it possible. Signed-off-by: Max Filippov Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..7881b3c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -581,11 +581,11 @@ config SND_SOC_TLV320AIC23 tristate config SND_SOC_TLV320AIC23_I2C - tristate + tristate "Texas Instruments TLV320AIC23 audio CODEC - I2C" select SND_SOC_TLV320AIC23 config SND_SOC_TLV320AIC23_SPI - tristate + tristate "Texas Instruments TLV320AIC23 audio CODEC - SPI" select SND_SOC_TLV320AIC23 config SND_SOC_TLV320AIC26 -- cgit v0.10.2 From 1a6db0bd26a72027d6a5ea006d64d4021fd0326e Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 10:38:54 +0530 Subject: ASoC: Intel: mrfld: Fix runtime pm calls in sst_open_pcm_stream It's already done in open/close. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c index 183b1eb..4187057 100644 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ b/sound/soc/intel/sst/sst_drv_interface.c @@ -163,16 +163,11 @@ static int sst_open_pcm_stream(struct device *dev, if (!str_param) return -EINVAL; - retval = pm_runtime_get_sync(ctx->dev); - if (retval < 0) - return retval; retval = sst_get_stream(ctx, str_param); - if (retval > 0) { + if (retval > 0) ctx->stream_cnt++; - } else { + else dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval); - sst_pm_runtime_put(ctx); - } return retval; } @@ -212,7 +207,8 @@ put: stream->period_elapsed = NULL; ctx->stream_cnt--; - sst_pm_runtime_put(ctx); + if (retval) + dev_err(ctx->dev, "free stream returned err %d\n", retval); dev_dbg(ctx->dev, "Exit\n"); return 0; -- cgit v0.10.2 From d62f2a08b9d657344b2e271e8274f9d8f746e543 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 31 Oct 2014 12:38:20 +0530 Subject: ASoC: Intel: sst: add runtime power management handling This patch adds the runtime pm handlers, the driver already has code for get/put for runtime pm and only these handlers being missing. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index fa34217..7b8a110 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -152,6 +152,23 @@ static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context) return IRQ_HANDLED; } +static int sst_save_dsp_context_v2(struct intel_sst_drv *sst) +{ + int ret = 0; + + ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD, + IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL, + true, true, false, true); + + if (ret < 0) { + dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret); + return -EIO; + } + + return 0; +} + + static struct intel_sst_ops mrfld_ops = { .interrupt = intel_sst_interrupt_mrfld, .irq_thread = intel_sst_irq_thread_mrfld, @@ -160,6 +177,7 @@ static struct intel_sst_ops mrfld_ops = { .reset = intel_sst_reset_dsp_mrfld, .post_message = sst_post_message_mrfld, .process_reply = sst_process_reply_mrfld, + .save_dsp_context = sst_save_dsp_context_v2, .alloc_stream = sst_alloc_stream_mrfld, .post_download = sst_post_download_mrfld, }; @@ -418,6 +436,50 @@ static void intel_sst_remove(struct pci_dev *pci) pci_set_drvdata(pci, NULL); } +static int intel_sst_runtime_suspend(struct device *dev) +{ + int ret = 0; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->sst_state == SST_RESET) { + dev_dbg(dev, "LPE is already in RESET state, No action\n"); + return 0; + } + /* save fw context */ + if (ctx->ops->save_dsp_context(ctx)) + return -EBUSY; + + /* Move the SST state to Reset */ + sst_set_fw_state_locked(ctx, SST_RESET); + + synchronize_irq(ctx->irq_num); + flush_workqueue(ctx->post_msg_wq); + + return ret; +} + +static int intel_sst_runtime_resume(struct device *dev) +{ + int ret = 0; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + mutex_lock(&ctx->sst_lock); + if (ctx->sst_state == SST_RESET) { + ret = sst_load_fw(ctx); + if (ret) { + dev_err(dev, "FW download fail %d\n", ret); + ctx->sst_state = SST_RESET; + } + } + mutex_unlock(&ctx->sst_lock); + return ret; +} + +static const struct dev_pm_ops intel_sst_pm = { + .runtime_suspend = intel_sst_runtime_suspend, + .runtime_resume = intel_sst_runtime_resume, +}; + /* PCI Routines */ static struct pci_device_id intel_sst_ids[] = { { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, @@ -429,6 +491,11 @@ static struct pci_driver sst_driver = { .id_table = intel_sst_ids, .probe = intel_sst_probe, .remove = intel_sst_remove, +#ifdef CONFIG_PM + .driver = { + .pm = &intel_sst_pm, + }, +#endif }; module_pci_driver(sst_driver); -- cgit v0.10.2 From 45f31bfcda0c6e5f11168de10c85f3dd20337bdf Mon Sep 17 00:00:00 2001 From: Mythri P K Date: Fri, 31 Oct 2014 12:38:21 +0530 Subject: ASoC: Intel: use lock when changing SST state. SST state change should be done under sst_lock Signed-off-by: Mythri P K Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 7b8a110..04af246 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -463,15 +463,13 @@ static int intel_sst_runtime_resume(struct device *dev) int ret = 0; struct intel_sst_drv *ctx = dev_get_drvdata(dev); - mutex_lock(&ctx->sst_lock); if (ctx->sst_state == SST_RESET) { ret = sst_load_fw(ctx); if (ret) { dev_err(dev, "FW download fail %d\n", ret); - ctx->sst_state = SST_RESET; + sst_set_fw_state_locked(ctx, SST_RESET); } } - mutex_unlock(&ctx->sst_lock); return ret; } diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c index 2126f5b..484e609 100644 --- a/sound/soc/intel/sst/sst_ipc.c +++ b/sound/soc/intel/sst/sst_ipc.c @@ -230,7 +230,7 @@ static void process_fw_init(struct intel_sst_drv *sst_drv_ctx, dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n"); if (init->result) { - sst_drv_ctx->sst_state = SST_RESET; + sst_set_fw_state_locked(sst_drv_ctx, SST_RESET); dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n", init->result); retval = init->result; -- cgit v0.10.2 From 6e9b05607fe896627ab7c15efff01b6dcae71a56 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 30 Oct 2014 16:20:56 +0530 Subject: ASoC: Intel: sst: load firmware using async callback We would like the DSP firmware to be available in driver as soon as possible. So use the async callback in driver to probe to load the firmware as soon as usermode is up Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 04af246..fdada40 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -364,6 +364,21 @@ static int intel_sst_probe(struct pci_dev *pci, sst_set_fw_state_locked(sst_drv_ctx, SST_RESET); + snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name), + "%s%04x%s", "fw_sst_", + sst_drv_ctx->dev_id, ".bin"); + dev_dbg(sst_drv_ctx->dev, + "Requesting FW %s now...\n", sst_drv_ctx->firmware_name); + ret = request_firmware_nowait(THIS_MODULE, 1, + sst_drv_ctx->firmware_name, sst_drv_ctx->dev, + GFP_KERNEL, sst_drv_ctx, sst_firmware_load_cb); + + if (ret) { + dev_err(sst_drv_ctx->dev, + "Firmware load failed with error: %d\n", ret); + goto do_release_regions; + } + sst_drv_ctx->irq_num = pci->irq; /* Register the ISR */ ret = devm_request_threaded_irq(&pci->dev, pci->irq, -- cgit v0.10.2 From 5794b7ec62d85700d372b07d88eaf71e807f542f Mon Sep 17 00:00:00 2001 From: "Fang, Yang A" Date: Thu, 30 Oct 2014 16:20:57 +0530 Subject: ASoC: Intel: use correct firmware name The firmware name was used worngly, so fix it up Signed-off-by: Fang, Yang A Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c index 00f60c1..b580f96 100644 --- a/sound/soc/intel/sst/sst_loader.c +++ b/sound/soc/intel/sst/sst_loader.c @@ -344,12 +344,9 @@ void sst_firmware_load_cb(const struct firmware *fw, void *context) static int sst_request_fw(struct intel_sst_drv *sst) { int retval = 0; - char name[20]; const struct firmware *fw; - dev_dbg(sst->dev, "Requesting FW %s now...\n", name); - - retval = request_firmware(&fw, name, sst->dev); + retval = request_firmware(&fw, sst->firmware_name, sst->dev); if (fw == NULL) { dev_err(sst->dev, "fw is returning as null\n"); return -EINVAL; -- cgit v0.10.2 From fdcc4a039f0263f4674e363ebed14783b2f0543d Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 16:20:58 +0530 Subject: ASoC: mfld-compress: implement .power callback .power callback is required to invoked for compressed audio as well to turn on/off sst, so invoke them Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c index 5946777..3951689 100644 --- a/sound/soc/intel/sst-mfld-platform-compress.c +++ b/sound/soc/intel/sst-mfld-platform-compress.c @@ -67,8 +67,11 @@ static int sst_platform_compr_open(struct snd_compr_stream *cstream) goto out_ops; } stream->compr_ops = sst->compr_ops; - stream->id = 0; + + /* Turn on LPE */ + sst->compr_ops->power(sst->dev, true); + sst_set_stream_status(stream, SST_PLATFORM_INIT); runtime->private_data = stream; return 0; @@ -83,6 +86,9 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream) int ret_val = 0, str_id; stream = cstream->runtime->private_data; + /* Turn off LPE */ + sst->compr_ops->power(sst->dev, false); + /*need to check*/ str_id = stream->id; if (str_id) diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index d41d1c3..79c8d12 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -117,6 +117,7 @@ struct compress_sst_ops { int (*get_codec_caps)(struct snd_compr_codec_caps *codec); int (*set_metadata)(struct device *dev, unsigned int str_id, struct snd_compr_metadata *mdata); + int (*power)(struct device *dev, bool state); }; struct sst_ops { -- cgit v0.10.2 From 7adab122a57c5ade8efc2e4de67c72b084c31cda Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 30 Oct 2014 16:20:59 +0530 Subject: ASoC: Intel: sst - add compressed ops handling This patch add low level IPC handling for compressed stream operations Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c index 4187057..5f75ef3 100644 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ b/sound/soc/intel/sst/sst_drv_interface.c @@ -172,6 +172,273 @@ static int sst_open_pcm_stream(struct device *dev, return retval; } +static int sst_cdev_open(struct device *dev, + struct snd_sst_params *str_params, struct sst_compress_cb *cb) +{ + int str_id, retval; + struct stream_info *stream; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + retval = pm_runtime_get_sync(ctx->dev); + if (retval < 0) + return retval; + + str_id = sst_get_stream(ctx, str_params); + if (str_id > 0) { + dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id); + stream = &ctx->streams[str_id]; + stream->compr_cb = cb->compr_cb; + stream->compr_cb_param = cb->param; + stream->drain_notify = cb->drain_notify; + stream->drain_cb_param = cb->drain_cb_param; + } else { + dev_err(dev, "stream encountered error during alloc %d\n", str_id); + str_id = -EINVAL; + sst_pm_runtime_put(ctx); + } + return str_id; +} + +static int sst_cdev_close(struct device *dev, unsigned int str_id) +{ + int retval; + struct stream_info *stream; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + stream = get_stream_info(ctx, str_id); + if (!stream) { + dev_err(dev, "stream info is NULL for str %d!!!\n", str_id); + return -EINVAL; + } + + if (stream->status == STREAM_RESET) { + dev_dbg(dev, "stream in reset state...\n"); + stream->status = STREAM_UN_INIT; + + retval = 0; + goto put; + } + + retval = sst_free_stream(ctx, str_id); +put: + stream->compr_cb_param = NULL; + stream->compr_cb = NULL; + + if (retval) + dev_err(dev, "free stream returned err %d\n", retval); + + dev_dbg(dev, "End\n"); + return retval; + +} + +static int sst_cdev_ack(struct device *dev, unsigned int str_id, + unsigned long bytes) +{ + struct stream_info *stream; + struct snd_sst_tstamp fw_tstamp = {0,}; + int offset; + void __iomem *addr; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + stream = get_stream_info(ctx, str_id); + if (!stream) + return -EINVAL; + + /* update bytes sent */ + stream->cumm_bytes += bytes; + dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); + + memcpy_fromio(&fw_tstamp, + ((void *)(ctx->mailbox + ctx->tstamp) + +(str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + + fw_tstamp.bytes_copied = stream->cumm_bytes; + dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n", + fw_tstamp.bytes_copied, bytes); + + addr = ((void *)(ctx->mailbox + ctx->tstamp)) + + (str_id * sizeof(fw_tstamp)); + offset = offsetof(struct snd_sst_tstamp, bytes_copied); + sst_shim_write(addr, offset, fw_tstamp.bytes_copied); + return 0; +} + +static int sst_cdev_set_metadata(struct device *dev, + unsigned int str_id, struct snd_compr_metadata *metadata) +{ + int retval = 0; + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + dev_dbg(dev, "set metadata for stream %d\n", str_id); + + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + + dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id); + retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD, + IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id, + sizeof(*metadata), metadata, NULL, + true, true, true, false); + + return retval; +} + +static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + return sst_pause_stream(ctx, str_id); +} + +static int sst_cdev_stream_pause_release(struct device *dev, + unsigned int str_id) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + return sst_resume_stream(ctx, str_id); +} + +static int sst_cdev_stream_start(struct device *dev, unsigned int str_id) +{ + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + str_info->prev = str_info->status; + str_info->status = STREAM_RUNNING; + return sst_start_stream(ctx, str_id); +} + +static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + return sst_drop_stream(ctx, str_id); +} + +static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + return sst_drain_stream(ctx, str_id, false); +} + +static int sst_cdev_stream_partial_drain(struct device *dev, + unsigned int str_id) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + return sst_drain_stream(ctx, str_id, true); +} + +static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, + struct snd_compr_tstamp *tstamp) +{ + struct snd_sst_tstamp fw_tstamp = {0,}; + struct stream_info *stream; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + memcpy_fromio(&fw_tstamp, + ((void *)(ctx->mailbox + ctx->tstamp) + +(str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + + stream = get_stream_info(ctx, str_id); + if (!stream) + return -EINVAL; + dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter); + + tstamp->copied_total = fw_tstamp.ring_buffer_counter; + tstamp->pcm_frames = fw_tstamp.frames_decoded; + tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, + (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24))); + tstamp->sampling_rate = fw_tstamp.sampling_frequency; + + dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames); + dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n", + str_id, tstamp->copied_total, tstamp->pcm_frames); + dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames); + + return 0; +} + +static int sst_cdev_caps(struct snd_compr_caps *caps) +{ + caps->num_codecs = NUM_CODEC; + caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ + caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ + caps->min_fragments = MIN_FRAGMENT; + caps->max_fragments = MAX_FRAGMENT; + caps->codecs[0] = SND_AUDIOCODEC_MP3; + caps->codecs[1] = SND_AUDIOCODEC_AAC; + return 0; +} + +static struct snd_compr_codec_caps caps_mp3 = { + .num_descriptors = 1, + .descriptor[0].max_ch = 2, + .descriptor[0].sample_rates[0] = 48000, + .descriptor[0].sample_rates[1] = 44100, + .descriptor[0].sample_rates[2] = 32000, + .descriptor[0].sample_rates[3] = 16000, + .descriptor[0].sample_rates[4] = 8000, + .descriptor[0].num_sample_rates = 5, + .descriptor[0].bit_rate[0] = 320, + .descriptor[0].bit_rate[1] = 192, + .descriptor[0].num_bitrates = 2, + .descriptor[0].profiles = 0, + .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, + .descriptor[0].formats = 0, +}; + +static struct snd_compr_codec_caps caps_aac = { + .num_descriptors = 2, + .descriptor[1].max_ch = 2, + .descriptor[0].sample_rates[0] = 48000, + .descriptor[0].sample_rates[1] = 44100, + .descriptor[0].sample_rates[2] = 32000, + .descriptor[0].sample_rates[3] = 16000, + .descriptor[0].sample_rates[4] = 8000, + .descriptor[0].num_sample_rates = 5, + .descriptor[1].bit_rate[0] = 320, + .descriptor[1].bit_rate[1] = 192, + .descriptor[1].num_bitrates = 2, + .descriptor[1].profiles = 0, + .descriptor[1].modes = 0, + .descriptor[1].formats = + (SND_AUDIOSTREAMFORMAT_MP4ADTS | + SND_AUDIOSTREAMFORMAT_RAW), +}; + +static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec) +{ + if (codec->codec == SND_AUDIOCODEC_MP3) + *codec = caps_mp3; + else if (codec->codec == SND_AUDIOCODEC_AAC) + *codec = caps_aac; + else + return -EINVAL; + + return 0; +} + +void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id) +{ + struct stream_info *stream; + + dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n", + str_id); + stream = &ctx->streams[str_id]; + if (stream->compr_cb) + stream->compr_cb(stream->compr_cb_param); +} + /* * sst_close_pcm_stream - Close PCM interface * @@ -372,10 +639,28 @@ static struct sst_ops pcm_ops = { .power = sst_power_control, }; +static struct compress_sst_ops compr_ops = { + .open = sst_cdev_open, + .close = sst_cdev_close, + .stream_pause = sst_cdev_stream_pause, + .stream_pause_release = sst_cdev_stream_pause_release, + .stream_start = sst_cdev_stream_start, + .stream_drop = sst_cdev_stream_drop, + .stream_drain = sst_cdev_stream_drain, + .stream_partial_drain = sst_cdev_stream_partial_drain, + .tstamp = sst_cdev_tstamp, + .ack = sst_cdev_ack, + .get_caps = sst_cdev_caps, + .get_codec_caps = sst_cdev_codec_caps, + .set_metadata = sst_cdev_set_metadata, + .power = sst_power_control, +}; + static struct sst_device sst_dsp_device = { .name = "Intel(R) SST LPE", .dev = NULL, .ops = &pcm_ops, + .compr_ops = &compr_ops, }; /* -- cgit v0.10.2 From 3172fcddcea230f129e8916628672617ef3c836c Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 16:21:44 +0530 Subject: ASoC: Intel: mfld-pcm: Fix to Store device context in sst_data Some debug prints use dev context in sst_data. Store the device context for the same. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index e7cf18d..6032f18 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -706,6 +706,7 @@ static int sst_platform_probe(struct platform_device *pdev) pdata->pdev_strm_map = dpcm_strm_map; pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map); drv->pdata = pdata; + drv->pdev = pdev; mutex_init(&drv->lock); dev_set_drvdata(&pdev->dev, drv); -- cgit v0.10.2 From 7e73e4d80539d0392010dfac3116307e7c9cf33d Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 16:21:45 +0530 Subject: ASoC: Intel: move the driver wq init to a routine This will be used by ACPI code as well, so moving to common routine helps Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index fdada40..f9a6d6d 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -206,6 +206,22 @@ void sst_process_pending_msg(struct work_struct *work) ctx->ops->post_message(ctx, NULL, false); } +static int sst_workqueue_init(struct intel_sst_drv *ctx) +{ + INIT_LIST_HEAD(&ctx->memcpy_list); + INIT_LIST_HEAD(&ctx->rx_list); + INIT_LIST_HEAD(&ctx->ipc_dispatch_list); + INIT_LIST_HEAD(&ctx->block_list); + INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg); + init_waitqueue_head(&ctx->wait_queue); + + ctx->post_msg_wq = + create_singlethread_workqueue("sst_post_msg_wq"); + if (!ctx->post_msg_wq) + return -EBUSY; + return 0; +} + /* * intel_sst_probe - PCI probe function * @@ -254,24 +270,13 @@ static int intel_sst_probe(struct pci_dev *pci, sst_drv_ctx->use_dma = 0; sst_drv_ctx->use_lli = 0; - INIT_LIST_HEAD(&sst_drv_ctx->memcpy_list); - INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list); - INIT_LIST_HEAD(&sst_drv_ctx->block_list); - INIT_LIST_HEAD(&sst_drv_ctx->rx_list); - - sst_drv_ctx->post_msg_wq = - create_singlethread_workqueue("sst_post_msg_wq"); - if (!sst_drv_ctx->post_msg_wq) { - ret = -EINVAL; - goto do_free_drv_ctx; - } - INIT_WORK(&sst_drv_ctx->ipc_post_msg_wq, sst_process_pending_msg); - init_waitqueue_head(&sst_drv_ctx->wait_queue); - spin_lock_init(&sst_drv_ctx->ipc_spin_lock); spin_lock_init(&sst_drv_ctx->block_lock); spin_lock_init(&sst_drv_ctx->rx_msg_lock); + if (sst_workqueue_init(sst_drv_ctx)) + return -EINVAL; + dev_info(sst_drv_ctx->dev, "Got drv data max stream %d\n", sst_drv_ctx->info.max_streams); for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) { @@ -414,7 +419,6 @@ do_release_regions: pci_release_regions(pci); do_free_mem: destroy_workqueue(sst_drv_ctx->post_msg_wq); -do_free_drv_ctx: dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); return ret; } -- cgit v0.10.2 From 54adc0ad647792b3a8557520477a40f76d99a007 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 16:21:46 +0530 Subject: ASoC: Intel: move the lock and wq initialization to routine This will be used by ACPI code as well, so moving to common routine helps Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index f9a6d6d..0863471 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -222,6 +222,14 @@ static int sst_workqueue_init(struct intel_sst_drv *ctx) return 0; } +static void sst_init_locks(struct intel_sst_drv *ctx) +{ + mutex_init(&ctx->sst_lock); + spin_lock_init(&ctx->rx_msg_lock); + spin_lock_init(&ctx->ipc_spin_lock); + spin_lock_init(&ctx->block_lock); +} + /* * intel_sst_probe - PCI probe function * @@ -259,7 +267,7 @@ static int intel_sst_probe(struct pci_dev *pci, return -EINVAL; ops = sst_drv_ctx->ops; - mutex_init(&sst_drv_ctx->sst_lock); + sst_init_locks(sst_drv_ctx); /* pvt_id 0 reserved for async messages */ sst_drv_ctx->pvt_id = 1; @@ -270,10 +278,6 @@ static int intel_sst_probe(struct pci_dev *pci, sst_drv_ctx->use_dma = 0; sst_drv_ctx->use_lli = 0; - spin_lock_init(&sst_drv_ctx->ipc_spin_lock); - spin_lock_init(&sst_drv_ctx->block_lock); - spin_lock_init(&sst_drv_ctx->rx_msg_lock); - if (sst_workqueue_init(sst_drv_ctx)) return -EINVAL; -- cgit v0.10.2 From 2559d9928f36f3c0bfb4ded9bb47d47b36337b09 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 16:21:47 +0530 Subject: ASoC: Intel: move the driver context allocation to routine This will be used by ACPI code as well, so moving to common routine helps Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 0863471..55bb1f7 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -230,6 +230,20 @@ static void sst_init_locks(struct intel_sst_drv *ctx) spin_lock_init(&ctx->block_lock); } +int sst_alloc_drv_context(struct intel_sst_drv **ctx, + struct device *dev, unsigned int dev_id) +{ + *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL); + if (!(*ctx)) + return -ENOMEM; + + (*ctx)->dev = dev; + (*ctx)->dev_id = dev_id; + + return 0; +} + + /* * intel_sst_probe - PCI probe function * @@ -247,12 +261,11 @@ static int intel_sst_probe(struct pci_dev *pci, int ddr_base; dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); - sst_drv_ctx = devm_kzalloc(&pci->dev, sizeof(*sst_drv_ctx), GFP_KERNEL); - if (!sst_drv_ctx) - return -ENOMEM; - sst_drv_ctx->dev = &pci->dev; - sst_drv_ctx->dev_id = pci->device; + ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); + if (ret < 0) + return ret; + if (!sst_pdata) return -EINVAL; -- cgit v0.10.2 From 250454d8fe65680b26f2917b806e2caf49126a01 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 16:21:48 +0530 Subject: ASoC: Intel: modularize driver probe and remove The driver probe which initializes driver and remove which cleans up can be shared with APCI as well, so move them to common init_context and cleanup_context routines which can be used by ACPI as well Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 55bb1f7..09d367a 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -243,6 +243,94 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx, return 0; } +int sst_context_init(struct intel_sst_drv *ctx) +{ + int ret = 0, i; + + if (!ctx->pdata) + return -EINVAL; + + if (!ctx->pdata->probe_data) + return -EINVAL; + + memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info)); + + ret = sst_driver_ops(ctx); + if (ret != 0) + return -EINVAL; + + sst_init_locks(ctx); + + /* pvt_id 0 reserved for async messages */ + ctx->pvt_id = 1; + ctx->stream_cnt = 0; + ctx->fw_in_mem = NULL; + /* we use memcpy, so set to 0 */ + ctx->use_dma = 0; + ctx->use_lli = 0; + + if (sst_workqueue_init(ctx)) + return -EINVAL; + + ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off; + ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset; + ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset; + + dev_info(ctx->dev, "Got drv data max stream %d\n", + ctx->info.max_streams); + + for (i = 1; i <= ctx->info.max_streams; i++) { + struct stream_info *stream = &ctx->streams[i]; + + memset(stream, 0, sizeof(*stream)); + stream->pipe_id = PIPE_RSVD; + mutex_init(&stream->lock); + } + + /* Register the ISR */ + ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt, + ctx->ops->irq_thread, 0, SST_DRV_NAME, + ctx); + if (ret) + goto do_free_mem; + + dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num); + + /* default intr are unmasked so set this as masked */ + sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038); + + ctx->qos = devm_kzalloc(ctx->dev, + sizeof(struct pm_qos_request), GFP_KERNEL); + if (!ctx->qos) { + ret = -ENOMEM; + goto do_free_mem; + } + pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + return 0; + +do_free_mem: + destroy_workqueue(ctx->post_msg_wq); + return ret; +} + +void sst_context_cleanup(struct intel_sst_drv *ctx) +{ + pm_runtime_get_noresume(ctx->dev); + pm_runtime_forbid(ctx->dev); + sst_unregister(ctx->dev); + sst_set_fw_state_locked(ctx, SST_SHUTDOWN); + flush_scheduled_work(); + destroy_workqueue(ctx->post_msg_wq); + pm_qos_remove_request(ctx->qos); + kfree(ctx->fw_sg_list.src); + kfree(ctx->fw_sg_list.dst); + ctx->fw_sg_list.list_len = 0; + kfree(ctx->fw_in_mem); + ctx->fw_in_mem = NULL; + sst_memcpy_free_resources(ctx); + ctx = NULL; +} /* * intel_sst_probe - PCI probe function @@ -254,9 +342,8 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx, static int intel_sst_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { - int i, ret = 0; + int ret = 0; struct intel_sst_drv *sst_drv_ctx; - struct intel_sst_ops *ops; struct sst_platform_info *sst_pdata = pci->dev.platform_data; int ddr_base; @@ -266,43 +353,12 @@ static int intel_sst_probe(struct pci_dev *pci, if (ret < 0) return ret; - if (!sst_pdata) - return -EINVAL; - sst_drv_ctx->pdata = sst_pdata; - if (!sst_drv_ctx->pdata->probe_data) - return -EINVAL; - - memcpy(&sst_drv_ctx->info, sst_drv_ctx->pdata->probe_data, - sizeof(sst_drv_ctx->info)); - - if (0 != sst_driver_ops(sst_drv_ctx)) - return -EINVAL; - - ops = sst_drv_ctx->ops; - sst_init_locks(sst_drv_ctx); - - /* pvt_id 0 reserved for async messages */ - sst_drv_ctx->pvt_id = 1; - sst_drv_ctx->stream_cnt = 0; - sst_drv_ctx->fw_in_mem = NULL; - - /* we use memcpy, so set to 0 */ - sst_drv_ctx->use_dma = 0; - sst_drv_ctx->use_lli = 0; - - if (sst_workqueue_init(sst_drv_ctx)) - return -EINVAL; - dev_info(sst_drv_ctx->dev, "Got drv data max stream %d\n", - sst_drv_ctx->info.max_streams); - for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) { - struct stream_info *stream = &sst_drv_ctx->streams[i]; + ret = sst_context_init(sst_drv_ctx); + if (ret < 0) + goto do_free_drv_ctx; - memset(stream, 0, sizeof(*stream)); - stream->pipe_id = PIPE_RSVD; - mutex_init(&stream->lock); - } /* Init the device */ ret = pcim_enable_device(pci); @@ -402,18 +458,6 @@ static int intel_sst_probe(struct pci_dev *pci, } sst_drv_ctx->irq_num = pci->irq; - /* Register the ISR */ - ret = devm_request_threaded_irq(&pci->dev, pci->irq, - sst_drv_ctx->ops->interrupt, - sst_drv_ctx->ops->irq_thread, 0, SST_DRV_NAME, - sst_drv_ctx); - if (ret) - goto do_release_regions; - dev_dbg(sst_drv_ctx->dev, "Registered IRQ 0x%x\n", pci->irq); - - /* default intr are unmasked so set this as masked */ - if (sst_drv_ctx->dev_id == SST_MRFLD_PCI_ID) - sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, 0xFFFF0038); pci_set_drvdata(pci, sst_drv_ctx); pm_runtime_set_autosuspend_delay(sst_drv_ctx->dev, SST_SUSPEND_DELAY); @@ -421,14 +465,6 @@ static int intel_sst_probe(struct pci_dev *pci, pm_runtime_allow(sst_drv_ctx->dev); pm_runtime_put_noidle(sst_drv_ctx->dev); sst_register(sst_drv_ctx->dev); - sst_drv_ctx->qos = devm_kzalloc(&pci->dev, - sizeof(struct pm_qos_request), GFP_KERNEL); - if (!sst_drv_ctx->qos) { - ret = -ENOMEM; - goto do_release_regions; - } - pm_qos_add_request(sst_drv_ctx->qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); return ret; @@ -436,6 +472,7 @@ do_release_regions: pci_release_regions(pci); do_free_mem: destroy_workqueue(sst_drv_ctx->post_msg_wq); +do_free_drv_ctx: dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); return ret; } @@ -452,22 +489,8 @@ static void intel_sst_remove(struct pci_dev *pci) { struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); - pm_runtime_get_noresume(sst_drv_ctx->dev); - pm_runtime_forbid(sst_drv_ctx->dev); - sst_unregister(sst_drv_ctx->dev); + sst_context_cleanup(sst_drv_ctx); pci_dev_put(sst_drv_ctx->pci); - sst_set_fw_state_locked(sst_drv_ctx, SST_SHUTDOWN); - - flush_scheduled_work(); - destroy_workqueue(sst_drv_ctx->post_msg_wq); - pm_qos_remove_request(sst_drv_ctx->qos); - kfree(sst_drv_ctx->fw_sg_list.src); - kfree(sst_drv_ctx->fw_sg_list.dst); - sst_drv_ctx->fw_sg_list.list_len = 0; - kfree(sst_drv_ctx->fw_in_mem); - sst_drv_ctx->fw_in_mem = NULL; - sst_memcpy_free_resources(sst_drv_ctx); - sst_drv_ctx = NULL; pci_release_regions(pci); pci_set_drvdata(pci, NULL); } -- cgit v0.10.2 From 7fb73c74ffee65bda3b6d3b00c1b841f557a1191 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Oct 2014 16:21:49 +0530 Subject: ASoC: Intel: more probe modularization for sst Move the PCI BAR and resource initialization to a separate routine Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 09d367a..2bfb404 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -332,114 +332,132 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) ctx = NULL; } -/* -* intel_sst_probe - PCI probe function -* -* @pci: PCI device structure -* @pci_id: PCI device ID structure -* -*/ -static int intel_sst_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) +void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { - int ret = 0; - struct intel_sst_drv *sst_drv_ctx; - struct sst_platform_info *sst_pdata = pci->dev.platform_data; - int ddr_base; - - dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); - - ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); - if (ret < 0) - return ret; - - sst_drv_ctx->pdata = sst_pdata; - - ret = sst_context_init(sst_drv_ctx); - if (ret < 0) - goto do_free_drv_ctx; - + pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); + pm_runtime_use_autosuspend(ctx->dev); + pm_runtime_allow(ctx->dev); + pm_runtime_put_noidle(ctx->dev); +} - /* Init the device */ - ret = pcim_enable_device(pci); - if (ret) { - dev_err(sst_drv_ctx->dev, - "device can't be enabled. Returned err: %d\n", ret); - goto do_free_mem; - } - sst_drv_ctx->pci = pci_dev_get(pci); +static int sst_platform_get_resources(struct intel_sst_drv *ctx) +{ + int ddr_base, ret = 0; + struct pci_dev *pci = ctx->pci; ret = pci_request_regions(pci, SST_DRV_NAME); if (ret) - goto do_free_mem; + return ret; /* map registers */ /* DDR base */ - if (sst_drv_ctx->dev_id == SST_MRFLD_PCI_ID) { - sst_drv_ctx->ddr_base = pci_resource_start(pci, 0); + if (ctx->dev_id == SST_MRFLD_PCI_ID) { + ctx->ddr_base = pci_resource_start(pci, 0); /* check that the relocated IMR base matches with FW Binary */ - ddr_base = relocate_imr_addr_mrfld(sst_drv_ctx->ddr_base); - if (!sst_drv_ctx->pdata->lib_info) { - dev_err(sst_drv_ctx->dev, "lib_info pointer NULL\n"); + ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base); + if (!ctx->pdata->lib_info) { + dev_err(ctx->dev, "lib_info pointer NULL\n"); ret = -EINVAL; goto do_release_regions; } - if (ddr_base != sst_drv_ctx->pdata->lib_info->mod_base) { - dev_err(sst_drv_ctx->dev, + if (ddr_base != ctx->pdata->lib_info->mod_base) { + dev_err(ctx->dev, "FW LSP DDR BASE does not match with IFWI\n"); ret = -EINVAL; goto do_release_regions; } - sst_drv_ctx->ddr_end = pci_resource_end(pci, 0); + ctx->ddr_end = pci_resource_end(pci, 0); - sst_drv_ctx->ddr = pcim_iomap(pci, 0, + ctx->ddr = pcim_iomap(pci, 0, pci_resource_len(pci, 0)); - if (!sst_drv_ctx->ddr) { + if (!ctx->ddr) { ret = -EINVAL; goto do_release_regions; } - dev_dbg(sst_drv_ctx->dev, "sst: DDR Ptr %p\n", sst_drv_ctx->ddr); + dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr); } else { - sst_drv_ctx->ddr = NULL; + ctx->ddr = NULL; } - /* SHIM */ - sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1); - sst_drv_ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); - if (!sst_drv_ctx->shim) { + ctx->shim_phy_add = pci_resource_start(pci, 1); + ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); + if (!ctx->shim) { ret = -EINVAL; goto do_release_regions; } - dev_dbg(sst_drv_ctx->dev, "SST Shim Ptr %p\n", sst_drv_ctx->shim); + dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim); /* Shared SRAM */ - sst_drv_ctx->mailbox_add = pci_resource_start(pci, 2); - sst_drv_ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); - if (!sst_drv_ctx->mailbox) { + ctx->mailbox_add = pci_resource_start(pci, 2); + ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); + if (!ctx->mailbox) { ret = -EINVAL; goto do_release_regions; } - dev_dbg(sst_drv_ctx->dev, "SRAM Ptr %p\n", sst_drv_ctx->mailbox); + dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox); /* IRAM */ - sst_drv_ctx->iram_end = pci_resource_end(pci, 3); - sst_drv_ctx->iram_base = pci_resource_start(pci, 3); - sst_drv_ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); - if (!sst_drv_ctx->iram) { + ctx->iram_end = pci_resource_end(pci, 3); + ctx->iram_base = pci_resource_start(pci, 3); + ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); + if (!ctx->iram) { ret = -EINVAL; goto do_release_regions; } - dev_dbg(sst_drv_ctx->dev, "IRAM Ptr %p\n", sst_drv_ctx->iram); + dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram); /* DRAM */ - sst_drv_ctx->dram_end = pci_resource_end(pci, 4); - sst_drv_ctx->dram_base = pci_resource_start(pci, 4); - sst_drv_ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); - if (!sst_drv_ctx->dram) { + ctx->dram_end = pci_resource_end(pci, 4); + ctx->dram_base = pci_resource_start(pci, 4); + ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); + if (!ctx->dram) { ret = -EINVAL; goto do_release_regions; } - dev_dbg(sst_drv_ctx->dev, "DRAM Ptr %p\n", sst_drv_ctx->dram); + dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram); +do_release_regions: + pci_release_regions(pci); + return 0; +} +/* +* intel_sst_probe - PCI probe function +* +* @pci: PCI device structure +* @pci_id: PCI device ID structure +* +*/ +static int intel_sst_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + int ret = 0; + struct intel_sst_drv *sst_drv_ctx; + struct sst_platform_info *sst_pdata = pci->dev.platform_data; + + dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); + + ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); + if (ret < 0) + return ret; + + sst_drv_ctx->pdata = sst_pdata; + sst_drv_ctx->irq_num = pci->irq; + + ret = sst_context_init(sst_drv_ctx); + if (ret < 0) + goto do_free_drv_ctx; + + + /* Init the device */ + ret = pcim_enable_device(pci); + if (ret) { + dev_err(sst_drv_ctx->dev, + "device can't be enabled. Returned err: %d\n", ret); + goto do_destroy_wq; + } + sst_drv_ctx->pci = pci_dev_get(pci); + ret = sst_platform_get_resources(sst_drv_ctx); + if (ret < 0) + goto do_destroy_wq; sst_set_fw_state_locked(sst_drv_ctx, SST_RESET); snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name), @@ -457,20 +475,16 @@ static int intel_sst_probe(struct pci_dev *pci, goto do_release_regions; } - sst_drv_ctx->irq_num = pci->irq; pci_set_drvdata(pci, sst_drv_ctx); - pm_runtime_set_autosuspend_delay(sst_drv_ctx->dev, SST_SUSPEND_DELAY); - pm_runtime_use_autosuspend(sst_drv_ctx->dev); - pm_runtime_allow(sst_drv_ctx->dev); - pm_runtime_put_noidle(sst_drv_ctx->dev); + sst_configure_runtime_pm(sst_drv_ctx); sst_register(sst_drv_ctx->dev); return ret; do_release_regions: pci_release_regions(pci); -do_free_mem: +do_destroy_wq: destroy_workqueue(sst_drv_ctx->post_msg_wq); do_free_drv_ctx: dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); -- cgit v0.10.2 From c1e99c913be4294e63b5e74b197b8a8c86e6e67b Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 30 Oct 2014 21:16:23 +0800 Subject: ASoC: Intel: Add jack detection for Broadwell Add jack dectection and event reporting for Broadwell. It use combo jack on BDW platform, which including Mic Jack pin and Headphone jack pin. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/broadwell.c index 0e550f1..52cb764 100644 --- a/sound/soc/intel/broadwell.c +++ b/sound/soc/intel/broadwell.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "sst-dsp.h" @@ -26,8 +27,26 @@ #include "../codecs/rt286.h" +static struct snd_soc_jack broadwell_headset; +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin broadwell_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new broadwell_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), +}; + static const struct snd_soc_dapm_widget broadwell_widgets[] = { - SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("DMIC1", NULL), @@ -42,7 +61,7 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = { {"Speaker", NULL, "SPOL"}, /* HP jack connectors - unknown if we have jack deteck */ - {"Headphones", NULL, "HPO Pin"}, + {"Headphone Jack", NULL, "HPO Pin"}, /* other jacks */ {"MIC1", NULL, "Mic Jack"}, @@ -57,6 +76,27 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = { {"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, }; +static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret = 0; + ret = snd_soc_jack_new(codec, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset); + + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&broadwell_headset, + ARRAY_SIZE(broadwell_headset_pins), + broadwell_headset_pins); + if (ret) + return ret; + + rt286_mic_detect(codec, &broadwell_headset); + return 0; +} + + static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -116,7 +156,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) } /* always connected - check HP for jack detect */ - snd_soc_dapm_enable_pin(dapm, "Headphones"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); snd_soc_dapm_enable_pin(dapm, "Speaker"); snd_soc_dapm_enable_pin(dapm, "Mic Jack"); snd_soc_dapm_enable_pin(dapm, "Line Jack"); @@ -196,6 +236,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .no_pcm = 1, .codec_name = "i2c-INT343A:00", .codec_dai_name = "rt286-aif1", + .init = broadwell_rt286_codec_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ignore_suspend = 1, @@ -213,6 +254,8 @@ static struct snd_soc_card broadwell_rt286 = { .owner = THIS_MODULE, .dai_link = broadwell_rt286_dais, .num_links = ARRAY_SIZE(broadwell_rt286_dais), + .controls = broadwell_controls, + .num_controls = ARRAY_SIZE(broadwell_controls), .dapm_widgets = broadwell_widgets, .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets), .dapm_routes = broadwell_rt286_map, -- cgit v0.10.2 From 16af0ee16ca9391ef82e1c74c362d80551e769fe Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:00:58 +0100 Subject: ASoC: ad1980: Remove unused header The constants defined in the ad1980 header are not used. So remove the file. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c index 3450e8f..0fa81a5 100644 --- a/sound/soc/blackfin/bf5xx-ad1980.c +++ b/sound/soc/blackfin/bf5xx-ad1980.c @@ -46,8 +46,6 @@ #include #include -#include "../codecs/ad1980.h" - #include "bf5xx-ac97.h" static struct snd_soc_card bf5xx_board; diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 304d300..cc28dba 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -30,8 +30,6 @@ #include #include -#include "ad1980.h" - /* * AD1980 register cache */ diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h deleted file mode 100644 index eb0af44..0000000 --- a/sound/soc/codecs/ad1980.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * ad1980.h -- ad1980 Soc Audio driver - * - * WARNING: - * - * Because Analog Devices Inc. discontinued the ad1980 sound chip since - * Sep. 2009, this ad1980 driver is not maintained, tested and supported - * by ADI now. - */ - -#ifndef _AD1980_H -#define _AD1980_H -/* Bit definition of Power-Down Control/Status Register */ -#define ADC 0x0001 -#define DAC 0x0002 -#define ANL 0x0004 -#define REF 0x0008 -#define PR0 0x0100 -#define PR1 0x0200 -#define PR2 0x0400 -#define PR3 0x0800 -#define PR4 0x1000 -#define PR5 0x2000 -#define PR6 0x4000 - -#endif -- cgit v0.10.2 From e5adb6cddb17f8e76be404f23a2e0db102ee1bd1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:00:59 +0100 Subject: ASoC: ad1980: Cleanup printk usage Use dev_err()/dev_warn() instead of printk(KERN_ERR/KERN_WARNING. This is common practice and makes it easy to find out which device generated the message. While we are at it also align the error messages with the other AC'97 drivers. Also remove the info message that is printed when the driver is probed, this is just noise in bootlog. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index cc28dba..5f076c2 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -209,7 +209,8 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) return 0; } while (retry_cnt++ < 10); - printk(KERN_ERR "AD1980 AC97 reset failed\n"); + dev_err(codec->dev, "Failed to reset: AC97 link error\n"); + return -EIO; } @@ -219,19 +220,15 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) u16 vendor_id2; u16 ext_status; - printk(KERN_INFO "AD1980 SoC Audio Codec\n"); - ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) { - printk(KERN_ERR "ad1980: failed to register AC97 codec\n"); + dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } ret = ad1980_reset(codec, 0); - if (ret < 0) { - printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n"); + if (ret < 0) goto reset_err; - } /* Read out vendor ID to make sure it is ad1980 */ if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) { @@ -246,9 +243,8 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) ret = -ENODEV; goto reset_err; } else { - printk(KERN_WARNING "ad1980: " - "Found AD1981 - only 2/2 IN/OUT Channels " - "supported\n"); + dev_warn(codec->dev, + "Found AD1981 - only 2/2 IN/OUT Channels supported\n"); } } -- cgit v0.10.2 From 6ce13d61dc6cfc3cf6be6bd12faf75bfbc12ea91 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:00 +0100 Subject: ASoC: ad1980: Use table based control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 5f076c2..9ed4e12 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -259,9 +259,6 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) ext_status = ac97_read(codec, AC97_EXTENDED_STATUS); ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800); - snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls, - ARRAY_SIZE(ad1980_snd_ac97_controls)); - return 0; reset_err: @@ -285,6 +282,8 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1980 = { .write = ac97_write, .read = ac97_read, + .controls = ad1980_snd_ac97_controls, + .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls), .dapm_widgets = ad1980_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets), .dapm_routes = ad1980_dapm_routes, -- cgit v0.10.2 From 93932abaa3c84c2d76ce713bbbad08bad9162483 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:01 +0100 Subject: ASoC: stac9766: Cleanup printk usage Use dev_err() instead of printk(KERN_ERR. This is common practice and makes it easy to find out which device generated the message. While we are at it also align the error messages with the other AC'97 drivers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 9878534..e88d9ac 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -262,7 +262,7 @@ static int stac9766_codec_resume(struct snd_soc_codec *codec) /* give the codec an AC97 warm reset to start the link */ reset: if (reset > 5) { - printk(KERN_ERR "stac9766 failed to resume"); + dev_err(codec->dev, "Failed to resume\n"); return -EIO; } codec->ac97->bus->ops->warm_reset(codec->ac97); @@ -338,7 +338,7 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec) stac9766_reset(codec, 0); ret = stac9766_reset(codec, 1); if (ret < 0) { - printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n"); + dev_err(codec->dev, "Failed to reset: AC97 link error\n"); goto codec_err; } -- cgit v0.10.2 From 8865051d9941de905432f59f7a88662e824d5df9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:02 +0100 Subject: ASoC: stac9766: Use table based control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index e88d9ac..6c62d29 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -342,9 +342,6 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec) goto codec_err; } - snd_soc_add_codec_controls(codec, stac9766_snd_ac97_controls, - ARRAY_SIZE(stac9766_snd_ac97_controls)); - return 0; codec_err: @@ -359,6 +356,8 @@ static int stac9766_codec_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_stac9766 = { + .controls = stac9766_snd_ac97_controls, + .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls), .write = stac9766_ac97_write, .read = stac9766_ac97_read, .set_bias_level = stac9766_set_bias_level, -- cgit v0.10.2 From 9cf766f666cc4518e22f185159f285f4e3183230 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:03 +0100 Subject: ASoC: wm9705: Cleanup printk usage Use dev_err() instead of printk(KERN_ERR. This is common practice and makes it easy to find out which device generated the message. While we are at it also align the error messages with the other AC'97 drivers. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index c0b7f45..355b28d 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -300,6 +300,8 @@ static int wm9705_reset(struct snd_soc_codec *codec) return 0; /* Success */ } + dev_err(codec->dev, "Failed to reset: AC97 link error\n"); + return -EIO; } @@ -317,10 +319,8 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9705_reset(codec); - if (ret < 0) { - printk(KERN_ERR "could not reset AC97 codec\n"); + if (ret < 0) return ret; - } for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) { soc_ac97_ops->write(codec->ac97, i, cache[i>>1]); @@ -339,7 +339,7 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) { - printk(KERN_ERR "wm9705: failed to register AC97 codec\n"); + dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } -- cgit v0.10.2 From d7cabb08ba23c87757fb3be01e82f755aad426d1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:04 +0100 Subject: ASoC: wm9705: Use table based control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 355b28d..1650195 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -347,9 +347,6 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) if (ret) goto reset_err; - snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls, - ARRAY_SIZE(wm9705_snd_ac97_controls)); - return 0; reset_err: @@ -374,6 +371,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9705_reg, + + .controls = wm9705_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls), .dapm_widgets = wm9705_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets), .dapm_routes = wm9705_audio_map, -- cgit v0.10.2 From 12ced338ab8858d11ef5b11b65c3dc612d9551c9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:05 +0100 Subject: ASoC: wm9712: Cleanup printk usage Use dev_err() instead of printk(KERN_ERR. This is common practice and makes it easy to find out which device generated the message. While we are at it also align the error messages with the other AC'97 drivers. Also avoid printing two error messages when the reset fails. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index c5eb746..c389e56 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -595,7 +595,7 @@ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) return 0; err: - printk(KERN_ERR "WM9712 AC97 reset failed\n"); + dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; } @@ -611,10 +611,8 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9712_reset(codec, 1); - if (ret < 0) { - printk(KERN_ERR "could not reset AC97 codec\n"); + if (ret < 0) return ret; - } wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -637,15 +635,13 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) { - printk(KERN_ERR "wm9712: failed to register AC97 codec\n"); + dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } ret = wm9712_reset(codec, 0); - if (ret < 0) { - printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n"); + if (ret < 0) goto reset_err; - } /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); -- cgit v0.10.2 From 9a812c6b7a2092e20b4b78ed0ec6614a89e96dfd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:06 +0100 Subject: ASoC: wm9712: Use table based control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index c389e56..f3aab6e 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -647,8 +647,6 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls, - ARRAY_SIZE(wm9712_snd_ac97_controls)); return 0; @@ -675,6 +673,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9712_reg, + + .controls = wm9712_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls), .dapm_widgets = wm9712_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), .dapm_routes = wm9712_audio_map, -- cgit v0.10.2 From a6c2b07f11beaf5719f03c70a9c9597534b297a5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:07 +0100 Subject: ASoC: wm9713: Cleanup printk usage Use dev_err()/dev_warn() instead of printk(KERN_ERR/KERN_WARNING. This is common practice and makes it easy to find out which device generated the message. While we are at it also align the error messages with the other AC'97 drivers. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index bddee30..38e17d4 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -689,7 +689,8 @@ struct _pll_div { * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 22) * 10) -static void pll_factors(struct _pll_div *pll_div, unsigned int source) +static void pll_factors(struct snd_soc_codec *codec, + struct _pll_div *pll_div, unsigned int source) { u64 Kpart; unsigned int K, Ndiv, Nmod, target; @@ -724,7 +725,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source) Ndiv = target / source; if ((Ndiv < 5) || (Ndiv > 12)) - printk(KERN_WARNING + dev_warn(codec->dev, "WM9713 PLL N value %u out of recommended range!\n", Ndiv); @@ -768,7 +769,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, return 0; } - pll_factors(&pll_div, freq_in); + pll_factors(codec, &pll_div, freq_in); if (pll_div.k == 0) { reg = (pll_div.n << 12) | (pll_div.lf << 11) | @@ -1104,8 +1105,11 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm) soc_ac97_ops->reset(codec->ac97); if (soc_ac97_ops->warm_reset) soc_ac97_ops->warm_reset(codec->ac97); - if (ac97_read(codec, 0) != wm9713_reg[0]) + if (ac97_read(codec, 0) != wm9713_reg[0]) { + dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; + } + return 0; } EXPORT_SYMBOL_GPL(wm9713_reset); @@ -1163,10 +1167,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9713_reset(codec, 1); - if (ret < 0) { - printk(KERN_ERR "could not reset AC97 codec\n"); + if (ret < 0) return ret; - } wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -1205,10 +1207,8 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) * a warm reset followed by an optional cold reset for codec */ wm9713_reset(codec, 0); ret = wm9713_reset(codec, 1); - if (ret < 0) { - printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n"); + if (ret < 0) goto reset_err; - } wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -- cgit v0.10.2 From c1359ca303ee5125827c0d2a65f0c86d491dc993 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:08 +0100 Subject: ASoC: wm9713: Use table based control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 38e17d4..ba8c276 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1216,9 +1216,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) reg = ac97_read(codec, AC97_CD) & 0x7fff; ac97_write(codec, AC97_CD, reg); - snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls, - ARRAY_SIZE(wm9713_snd_ac97_controls)); - return 0; reset_err: @@ -1248,6 +1245,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9713_reg, + + .controls = wm9713_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), .dapm_widgets = wm9713_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), .dapm_routes = wm9713_audio_map, -- cgit v0.10.2 From 5efe89d9525f24f607079307d2d9510e30ba8590 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:09 +0100 Subject: ASoC: wm9713: Move driver state struct allocation to driver probe Resources for the device should be allocated in the device driver probe callback, rather than in the ASoC CODEC probe callback. E.g. one advantage is that we can use device managed allocations. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index ba8c276..2704783 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1191,17 +1191,11 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { - struct wm9713_priv *wm9713; int ret = 0, reg; - wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL); - if (wm9713 == NULL) - return -ENOMEM; - snd_soc_codec_set_drvdata(codec, wm9713); - ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) - goto codec_err; + return ret; /* do a cold reset for the controller and then try * a warm reset followed by an optional cold reset for codec */ @@ -1220,16 +1214,12 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) reset_err: snd_soc_free_ac97_codec(codec); -codec_err: - kfree(wm9713); return ret; } static int wm9713_soc_remove(struct snd_soc_codec *codec) { - struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); snd_soc_free_ac97_codec(codec); - kfree(wm9713); return 0; } @@ -1256,6 +1246,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { static int wm9713_probe(struct platform_device *pdev) { + struct wm9713_priv *wm9713; + + wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL); + if (wm9713 == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, wm9713); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)); } -- cgit v0.10.2 From 5bc39b50fd3f9e3585e0cb1cf7d7da979a063848 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 Oct 2014 21:01:10 +0100 Subject: ASoC: wm9713: Use virtual control instead of virtual register The wm9713 currently implements the virtual control for the Mic B Source MUX using a virtual register. Replace this by using SOC_ENUM_SINGLE_VIRT(). Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 2704783..ac13fc8 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -59,13 +59,12 @@ static const u16 wm9713_reg[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x0001, 0x0000, 0x574d, 0x4c13, - 0x0000, 0x0000, 0x0000 + 0x0000, 0x0000 }; /* virtual HP mixers regs */ #define HPL_MIXER 0x80 #define HPR_MIXER 0x82 -#define MICB_MUX 0x82 static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; @@ -110,7 +109,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ -SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */ +SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */ }; static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0); -- cgit v0.10.2 From e894beb8183dd9e3834983440900ceb632823676 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 31 Oct 2014 10:54:23 -0700 Subject: ASoC: cs42l51: depends on I2C Fix build errors when CONFIG_I2C is not enabled by making the driver depend on I2C. ../sound/soc/codecs/cs42l51-i2c.c:55:1: warning: data definition has no type or storage class [enabled by default] module_i2c_driver(cs42l51_i2c_driver); ^ ../sound/soc/codecs/cs42l51-i2c.c:55:1: error: type defaults to 'int' in declaration of 'module_i2c_driver' [-Werror=implicit-int] ../sound/soc/codecs/cs42l51-i2c.c:55:1: warning: parameter names (without types) in function declaration [enabled by default] ../sound/soc/codecs/cs42l51-i2c.c:45:26: warning: 'cs42l51_i2c_driver' defined but not used [-Wunused-variable] static struct i2c_driver cs42l51_i2c_driver = { ^ Signed-off-by: Randy Dunlap Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f4fb12f..02a36b0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -337,6 +337,7 @@ config SND_SOC_CS42L51 config SND_SOC_CS42L51_I2C tristate "Cirrus Logic CS42L51 CODEC (I2C)" + depends on I2C select SND_SOC_CS42L51 config SND_SOC_CS42L52 -- cgit v0.10.2 From 22a236b4d07b5c5cfdc5db9e87d479d32281cfe6 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Sun, 2 Nov 2014 12:04:41 +0530 Subject: ASoC: Intel: fix missing mutex on error in block prepare, we were returning the error code while still holding the mutex. We are releasing the mutex in this patch before return. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index c451398..4a5bde9 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -1120,6 +1120,7 @@ int sst_block_alloc_scratch(struct sst_dsp *dsp) ret = block_list_prepare(dsp, &dsp->scratch_block_list); if (ret < 0) { dev_err(dsp->dev, "error: scratch block prepare failed\n"); + mutex_unlock(&dsp->mutex); return ret; } -- cgit v0.10.2 From bf9706fe958469e7dfc6a9e16d9240892f055e62 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 3 Nov 2014 13:10:53 +0300 Subject: ASoC: tlv320aic23: add dependencies on I2C/SPI_MASTER This fixes build errors in configurations with I2C/SPI master disabled. Reported-by: Fengguang Wu Signed-off-by: Max Filippov Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7881b3c..1e7a417 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -582,10 +582,12 @@ config SND_SOC_TLV320AIC23 config SND_SOC_TLV320AIC23_I2C tristate "Texas Instruments TLV320AIC23 audio CODEC - I2C" + depends on I2C select SND_SOC_TLV320AIC23 config SND_SOC_TLV320AIC23_SPI tristate "Texas Instruments TLV320AIC23 audio CODEC - SPI" + depends on SPI_MASTER select SND_SOC_TLV320AIC23 config SND_SOC_TLV320AIC26 -- cgit v0.10.2 From 9ce63dbd5d2671a209a859ee90f7dc6b8f22f28e Mon Sep 17 00:00:00 2001 From: Jianqun Date: Sat, 1 Nov 2014 10:58:18 +0800 Subject: ASoC: rockchip: i2s: add text after tristate for SND_SOC_ROCKCHIP_I2S For SND_SOC_ROCKCHIP_I2S, adding some text after the tristate to make this directly user selectable. Signed-off-by: Jianqun Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 78fc159..b1fc0ca 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -8,4 +8,9 @@ config SND_SOC_ROCKCHIP select the audio interfaces to support below. config SND_SOC_ROCKCHIP_I2S - tristate + tristate "Rockchip I2S Device Driver" + depends on CLKDEV_LOOKUP + help + Say Y or M if you want to add support for I2S driver for + Rockchip I2S device. The device supports upto maximum of + 8 channels each for play and record. -- cgit v0.10.2 From dd63a9c2952ed142c64fd68c1a74d0d6fcac586f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 3 Nov 2014 10:31:47 +0100 Subject: ASoC: Remove snd_soc_platform_driver suspend/resume callbacks Those are unused and new drivers should use device driver suspend/resume. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index ad47e96..edbb07b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -857,8 +857,6 @@ struct snd_soc_platform_driver { int (*probe)(struct snd_soc_platform *); int (*remove)(struct snd_soc_platform *); - int (*suspend)(struct snd_soc_dai *dai); - int (*resume)(struct snd_soc_dai *dai); struct snd_soc_component_driver component_driver; /* pcm creation and destruction */ @@ -891,8 +889,6 @@ struct snd_soc_platform { struct device *dev; const struct snd_soc_platform_driver *driver; - unsigned int suspended:1; /* platform is suspended */ - struct list_head list; struct snd_soc_component component; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a2b51ed..0509d72 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -592,17 +592,12 @@ int snd_soc_suspend(struct device *dev) for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - struct snd_soc_platform *platform = card->rtd[i].platform; if (card->rtd[i].dai_link->ignore_suspend) continue; if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control) cpu_dai->driver->suspend(cpu_dai); - if (platform->driver->suspend && !platform->suspended) { - platform->driver->suspend(cpu_dai); - platform->suspended = 1; - } } /* close any waiting streams and save state */ @@ -775,17 +770,12 @@ static void soc_resume_deferred(struct work_struct *work) for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - struct snd_soc_platform *platform = card->rtd[i].platform; if (card->rtd[i].dai_link->ignore_suspend) continue; if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control) cpu_dai->driver->resume(cpu_dai); - if (platform->driver->resume && platform->suspended) { - platform->driver->resume(cpu_dai); - platform->suspended = 0; - } } if (card->resume_post) -- cgit v0.10.2 From 2a374b78f5c2b5f31d35f8a7cd004989d6936756 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 3 Nov 2014 10:31:48 +0100 Subject: ASoC: Remove platform field from snd_soc_dai Typically a DAI does not need direct access to the platform. Currently the only user of this field is in a platform driver where we have a more direct way of getting a pointer to the platform. This patch updates the driver to use the more direct way and then removes the platform field from the snd_soc_dai struct. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index e8b3080..45d0fa1 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -268,7 +268,6 @@ struct snd_soc_dai { unsigned int sample_bits; /* parent platform/codec */ - struct snd_soc_platform *platform; struct snd_soc_codec *codec; struct snd_soc_component *component; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0509d72..e20bb65 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1309,7 +1309,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int i, ret; @@ -1317,7 +1316,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) card->name, num, order); /* config components */ - cpu_dai->platform = platform; cpu_dai->card = card; for (i = 0; i < rtd->num_codecs; i++) rtd->codec_dais[i]->card = card; diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index cd71fd8..00b7e2d 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd) struct snd_card *card = rtd->card->snd_card; struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_pcm *pcm = rtd->pcm; - struct platform_device *pdev = to_platform_device(dai->platform->dev); + struct platform_device *pdev = to_platform_device(rtd->platform->dev); struct txx9aclc_soc_device *dev; struct resource *r; int i; -- cgit v0.10.2 From 4476159f0b73e58e8c4d750ce03843d70c13994c Mon Sep 17 00:00:00 2001 From: Jianqun Date: Sat, 1 Nov 2014 11:22:18 +0800 Subject: ASoC: simple-card: add "invert" property for detect GPIOs Since hardware may invert detect GPIO of headphone or mic, add one property to support software invert. Signed-off-by: Jianqun Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index cac95d7..cd49d50 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -29,7 +29,9 @@ struct simple_card_data { } *dai_props; unsigned int mclk_fs; int gpio_hp_det; + int gpio_hp_det_invert; int gpio_mic_det; + int gpio_mic_det_invert; struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ }; @@ -148,6 +150,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) simple_card_hp_jack_pins); simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det; + simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert; snd_soc_jack_add_gpios(&simple_card_hp_jack, 1, &simple_card_hp_jack_gpio); } @@ -159,6 +162,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) ARRAY_SIZE(simple_card_mic_jack_pins), simple_card_mic_jack_pins); simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det; + simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert; snd_soc_jack_add_gpios(&simple_card_mic_jack, 1, &simple_card_mic_jack_gpio); } @@ -374,6 +378,7 @@ static int asoc_simple_card_parse_of(struct device_node *node, struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); + enum of_gpio_flags flags; u32 val; int ret; @@ -429,13 +434,15 @@ static int asoc_simple_card_parse_of(struct device_node *node, return ret; } - priv->gpio_hp_det = of_get_named_gpio(node, - "simple-audio-card,hp-det-gpio", 0); + priv->gpio_hp_det = of_get_named_gpio_flags(node, + "simple-audio-card,hp-det-gpio", 0, &flags); + priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW); if (priv->gpio_hp_det == -EPROBE_DEFER) return -EPROBE_DEFER; - priv->gpio_mic_det = of_get_named_gpio(node, - "simple-audio-card,mic-det-gpio", 0); + priv->gpio_mic_det = of_get_named_gpio_flags(node, + "simple-audio-card,mic-det-gpio", 0, &flags); + priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW); if (priv->gpio_mic_det == -EPROBE_DEFER) return -EPROBE_DEFER; -- cgit v0.10.2 From eb58960e9ebf15cde8ca1248e15ecb0c8f3a28bd Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Fri, 24 Oct 2014 21:25:59 +0200 Subject: ASoC: atmel_ssc_dai: Match the CMR divider only in full duplex. The CMR divider register is shared by playback and capture. The SSC driver therefore tries to enforce rules so that the needed register content do not conflict during simultaneous playback/capture. However, the implementation also prevents changing the register content in half-duplex scenarios, which is needed when using the OSS API. Thus, only lock the divider if there is a stream in the other direction. Fixes the below program to not fail with the atmel ssc dai in master mode. int main(void) { int fd; int format; int channels; int speed; if ((fd = open("/dev/dsp", O_WRONLY, 0)) == -1) { perror("open"); return 1; } format = AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1) { perror("SNDCTL_DSP_SETFMT"); return 1; } channels = 2; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { perror("SNDCTL_DSP_CHANNELS"); return 1; } speed = 22025; if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) { perror("SNDCTL_DSP_SPEED"); return 1; } return 0; } Signed-off-by: Peter Rosin Acked-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f403f39..b1cc2a4 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, * transmit and receive, so if a value has already * been set, it must match this value. */ - if (ssc_p->cmr_div == 0) + if (ssc_p->dir_mask != + (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) + ssc_p->cmr_div = div; + else if (ssc_p->cmr_div == 0) ssc_p->cmr_div = div; else if (div != ssc_p->cmr_div) -- cgit v0.10.2 From f74e2c9cb03076d11e807088d2120a8a381a6f3c Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Mon, 3 Nov 2014 21:59:37 +0800 Subject: ASoC: Intel: Correct a macro for FW message For the broadwell official released FW(Since 8.4.1.43), the macro SST_HSW_NO_CHANNELS is changed and fixed to 4, so here change it to 4. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index afd0ae1..387511f 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -21,7 +21,7 @@ #include #include -#define SST_HSW_NO_CHANNELS 2 +#define SST_HSW_NO_CHANNELS 4 #define SST_HSW_MAX_DX_REGIONS 14 #define SST_HSW_DX_CONTEXT_SIZE (640 * 1024) -- cgit v0.10.2 From 0b2e4959ceacb26eb586698d9ceecc0a6bd30f72 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 4 Nov 2014 13:15:10 +0800 Subject: ASoC: rt5645: make bias level more reasonale This patah separate bias level off to standby and off. The standby level will provide the necessary power for JD and push button functions. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 57ba742..1423cb2 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2069,8 +2069,8 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { switch (level) { - case SND_SOC_BIAS_STANDBY: - if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_BG | RT5645_PWR_VREF2, @@ -2085,15 +2085,24 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, } break; + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, + RT5645_PWR_FV1 | RT5645_PWR_FV2); + break; + case SND_SOC_BIAS_OFF: snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100); snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128); - snd_soc_write(codec, RT5645_PWR_DIG1, 0x0000); - snd_soc_write(codec, RT5645_PWR_DIG2, 0x0000); - snd_soc_write(codec, RT5645_PWR_VOL, 0x0000); - snd_soc_write(codec, RT5645_PWR_MIXER, 0x0000); - snd_soc_write(codec, RT5645_PWR_ANLG1, 0x0000); - snd_soc_write(codec, RT5645_PWR_ANLG2, 0x0000); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2 | + RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0); break; default: -- cgit v0.10.2 From e648f6add20d1cfb5945e24b5bffe5843476645b Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 4 Nov 2014 14:45:24 +0800 Subject: ASoC: Intel: Fix the driver data not set issue The priv_data is allocated again here wrongly, and it is not set to the driver data after assignment. This make the pdata->dev is NULL and oops occurs on the first call to hsw_volume_put. The resource has been allocated in driver probe callback hsw_pcm_dev_probe, so here just remove this sencond allocation is OK. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index cd54dd9..093b939 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -868,7 +868,6 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) dev = platform->dev; dma_dev = pdata->dma_dev; - priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL); priv_data->hsw = pdata->dsp; priv_data->dev = platform->dev; priv_data->pm_state = HSW_PM_STATE_D0; -- cgit v0.10.2 From 4539441690cd31ae7d42e6f080033911a1788440 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 3 Nov 2014 19:33:02 +0100 Subject: ASoC: mioa701_wm9713: Don't opencode CODEC register access Properly use snd_soc_update_bits() instead of manually calling the CODEC driver's read and write callbacks. The later will stop working once the wm9713 driver has been converted to regmap. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 595eee3..a6b2be2 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -127,15 +127,12 @@ static const struct snd_soc_dapm_route audio_map[] = { static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - unsigned short reg; /* Prepare GPIO8 for rear speaker amplifier */ - reg = codec->driver->read(codec, AC97_GPIO_CFG); - codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100); + snd_soc_update_bits(codec, AC97_GPIO_CFG, 0x100, 0x100); /* Prepare MIC input */ - reg = codec->driver->read(codec, AC97_3D_CONTROL); - codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000); + snd_soc_update_bits(codec, AC97_3D_CONTROL, 0xc000, 0xc000); return 0; } -- cgit v0.10.2 From 313665b983fe30af9d0eb274f7e03276e05a1bbf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 4 Nov 2014 11:30:58 +0100 Subject: ASoC: Remove card field from snd_soc_dai struct The card field of the snd_soc_dai field is very rarely used. We can use dai->component->card instead and remove the card field from the snd_soc_dai struct. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 45d0fa1..373d177 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -275,8 +275,6 @@ struct snd_soc_dai { unsigned int tx_mask; unsigned int rx_mask; - struct snd_soc_card *card; - struct list_head list; }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ea1df20..f3216fc 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1315,11 +1315,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, num, order); - /* config components */ - cpu_dai->card = card; - for (i = 0; i < rtd->num_codecs; i++) - rtd->codec_dais[i]->card = card; - /* set default power off timeout */ rtd->pmdown_time = pmdown_time; @@ -2314,7 +2309,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls) { - struct snd_card *card = dai->card->snd_card; + struct snd_card *card = dai->component->card->snd_card; return snd_soc_add_controls(card, dai->dev, controls, num_controls, NULL, dai); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6bf2c97..c5136bb 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1043,7 +1043,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list) { - struct snd_soc_card *card = dai->card; + struct snd_soc_card *card = dai->component->card; struct snd_soc_dapm_widget *w; int paths; -- cgit v0.10.2 From 8e2be56273666614e24756d7ee551203b8a86809 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 4 Nov 2014 11:30:59 +0100 Subject: ASoC: Consolidate CPU and CODEC DAI probe CPU and CODEC DAI probe are performed in exactly the same way. Which means we can reuse the snd_soc_codec_dai_probe() for probing CPU DAIs as well. While we are at it also drop the unused card parameter form the function. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f3216fc..406925c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1241,25 +1241,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, return 0; } -static int soc_probe_codec_dai(struct snd_soc_card *card, - struct snd_soc_dai *codec_dai, - int order) +static int soc_probe_dai(struct snd_soc_dai *dai, int order) { int ret; - if (!codec_dai->probed && codec_dai->driver->probe_order == order) { - if (codec_dai->driver->probe) { - ret = codec_dai->driver->probe(codec_dai); + if (!dai->probed && dai->driver->probe_order == order) { + if (dai->driver->probe) { + ret = dai->driver->probe(dai); if (ret < 0) { - dev_err(codec_dai->dev, - "ASoC: failed to probe CODEC DAI %s: %d\n", - codec_dai->name, ret); + dev_err(dai->dev, + "ASoC: failed to probe DAI %s: %d\n", + dai->name, ret); return ret; } } - /* mark codec_dai as probed and add to card dai list */ - codec_dai->probed = 1; + dai->probed = 1; } return 0; @@ -1318,24 +1315,13 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) /* set default power off timeout */ rtd->pmdown_time = pmdown_time; - /* probe the cpu_dai */ - if (!cpu_dai->probed && - cpu_dai->driver->probe_order == order) { - if (cpu_dai->driver->probe) { - ret = cpu_dai->driver->probe(cpu_dai); - if (ret < 0) { - dev_err(cpu_dai->dev, - "ASoC: failed to probe CPU DAI %s: %d\n", - cpu_dai->name, ret); - return ret; - } - } - cpu_dai->probed = 1; - } + ret = soc_probe_dai(cpu_dai, order); + if (ret) + return ret; /* probe the CODEC DAI */ for (i = 0; i < rtd->num_codecs; i++) { - ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order); + ret = soc_probe_dai(rtd->codec_dais[i], order); if (ret) return ret; } -- cgit v0.10.2 From 7b8ef67a0b1edb37957a2aa71a5c0bbbcf2694e9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 4 Nov 2014 13:27:45 +0000 Subject: ASoC: Intel: Fix build with CONFIG_SLEEP enabled. Fix the following build error when CONFIG_SLEEP is enabled and CONFIG_RUNTIME is disabled. The BDW ADSP sleep PM functionality depends on the runtime pm calls for context save/restore. All error/warnings: >> ERROR: "snd_soc_suspend" undefined! >> ERROR: "snd_soc_resume" undefined! Reported-by: kbuild test robot Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 093b939..e7a3b6a 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -1003,7 +1003,6 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM #ifdef CONFIG_PM_RUNTIME static int hsw_pcm_runtime_idle(struct device *dev) @@ -1063,6 +1062,8 @@ static int hsw_pcm_runtime_resume(struct device *dev) #define hsw_pcm_runtime_resume NULL #endif +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME) + static void hsw_pcm_complete(struct device *dev) { struct hsw_priv_data *pdata = dev_get_drvdata(dev); @@ -1158,6 +1159,11 @@ static int hsw_pcm_prepare(struct device *dev) return 0; } +#else +#define hsw_pcm_prepare NULL +#define hsw_pcm_complete NULL +#endif + static const struct dev_pm_ops hsw_pcm_pm = { .runtime_idle = hsw_pcm_runtime_idle, .runtime_suspend = hsw_pcm_runtime_suspend, @@ -1165,9 +1171,6 @@ static const struct dev_pm_ops hsw_pcm_pm = { .prepare = hsw_pcm_prepare, .complete = hsw_pcm_complete, }; -#else -#define hsw_pcm_pm NULL -#endif static struct platform_driver hsw_pcm_driver = { .driver = { -- cgit v0.10.2 From defcd98b16461e123cb4a6cb6ef24a1d0085c1b2 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Mon, 3 Nov 2014 10:28:57 -0800 Subject: ASoC: max98090: Different comp tables for different pclks In addtion expand the table to handle other values of sysclk. Instead of making the table 3D, expand it to a more descriptive struct. The divisors are specified in Table 19 of the 98090 data sheet version 0p94. The dmic frequency was previously assumed. Instead make it explicit and configurable through device tree. This now handles independently set pclk and dmic frequency. Based on downstream work by Ralph Birt. Signed-off-by: Dylan Reid Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/max98090.txt b/Documentation/devicetree/bindings/sound/max98090.txt index c454e67..aa802a2 100644 --- a/Documentation/devicetree/bindings/sound/max98090.txt +++ b/Documentation/devicetree/bindings/sound/max98090.txt @@ -16,6 +16,8 @@ Optional properties: - clock-names: Should be "mclk" +- maxim,dmic-freq: Frequency at which to clock DMIC + Pins on the device (for linking into audio routes): * MIC1 diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 1229554..a65861c 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1826,27 +1826,155 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, return 0; } -static const int comp_pclk_rates[] = { - 11289600, 12288000, 12000000, 13000000, 19200000 -}; - -static const int dmic_micclk[] = { - 2, 2, 2, 2, 4, 2 -}; +static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 }; static const int comp_lrclk_rates[] = { 8000, 16000, 32000, 44100, 48000, 96000 }; -static const int dmic_comp[6][6] = { - {7, 8, 3, 3, 3, 3}, - {7, 8, 3, 3, 3, 3}, - {7, 8, 3, 3, 3, 3}, - {7, 8, 3, 1, 1, 1}, - {7, 8, 3, 1, 2, 2}, - {7, 8, 3, 3, 3, 3} +struct dmic_table { + int pclk; + struct { + int freq; + int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */ + } settings[6]; /* One for each dmic divisor. */ }; +static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */ + { + .pclk = 11289600, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + }, + }, + { + .pclk = 12000000, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + } + }, + { + .pclk = 12288000, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + } + }, + { + .pclk = 13000000, + .settings = { + { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + } + }, + { + .pclk = 19200000, + .settings = { + { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } }, + { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + } + }, +}; + +static int max98090_find_divisor(int target_freq, int pclk) +{ + int current_diff = INT_MAX; + int test_diff = INT_MAX; + int divisor_index = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) { + test_diff = abs(target_freq - (pclk / dmic_divisors[i])); + if (test_diff < current_diff) { + current_diff = test_diff; + divisor_index = i; + } + } + + return divisor_index; +} + +static int max98090_find_closest_pclk(int pclk) +{ + int m1; + int m2; + int i; + + for (i = 0; i < ARRAY_SIZE(dmic_table); i++) { + if (pclk == dmic_table[i].pclk) + return i; + if (pclk < dmic_table[i].pclk) { + if (i == 0) + return i; + m1 = pclk - dmic_table[i-1].pclk; + m2 = dmic_table[i].pclk - pclk; + if (m1 < m2) + return i - 1; + else + return i; + } + } + + return -EINVAL; +} + +static int max98090_configure_dmic(struct max98090_priv *max98090, + int target_dmic_clk, int pclk, int fs) +{ + int micclk_index; + int pclk_index; + int dmic_freq; + int dmic_comp; + int i; + + pclk_index = max98090_find_closest_pclk(pclk); + if (pclk_index < 0) + return pclk_index; + + micclk_index = max98090_find_divisor(target_dmic_clk, pclk); + + for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) { + if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2) + break; + } + + dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq; + dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i]; + + regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE, + M98090_MICCLK_MASK, + micclk_index << M98090_MICCLK_SHIFT); + + regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK, + dmic_comp << M98090_DMIC_COMP_SHIFT | + dmic_freq << M98090_DMIC_FREQ_SHIFT); + + return 0; +} + static int max98090_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1854,7 +1982,6 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); struct max98090_cdata *cdata; - int i, j; cdata = &max98090->dai[0]; max98090->bclk = snd_soc_params_to_bclk(params); @@ -1893,27 +2020,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, M98090_DHF_MASK, M98090_DHF_MASK); - /* Check for supported PCLK to LRCLK ratios */ - for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) { - if (comp_pclk_rates[j] == max98090->sysclk) { - break; - } - } - - for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) { - if (max98090->lrclk <= (comp_lrclk_rates[i] + - comp_lrclk_rates[i + 1]) / 2) { - break; - } - } - - snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE, - M98090_MICCLK_MASK, - dmic_micclk[j] << M98090_MICCLK_SHIFT); - - snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG, - M98090_DMIC_COMP_MASK, - dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT); + max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk, + max98090->lrclk); return 0; } @@ -1944,12 +2052,15 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, if ((freq >= 10000000) && (freq <= 20000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV1); + max98090->pclk = freq; } else if ((freq > 20000000) && (freq <= 40000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV2); + max98090->pclk = freq >> 1; } else if ((freq > 40000000) && (freq <= 60000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV4); + max98090->pclk = freq >> 2; } else { dev_err(codec->dev, "Invalid master clock frequency\n"); return -EINVAL; @@ -2324,6 +2435,7 @@ static int max98090_probe(struct snd_soc_codec *codec) /* Initialize private data */ max98090->sysclk = (unsigned)-1; + max98090->pclk = (unsigned)-1; max98090->master = false; cdata = &max98090->dai[0]; @@ -2463,6 +2575,11 @@ static int max98090_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max98090); max98090->pdata = i2c->dev.platform_data; + ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq", + &max98090->dmic_freq); + if (ret < 0) + max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ; + max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap); if (IS_ERR(max98090->regmap)) { ret = PTR_ERR(max98090->regmap); diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index a5f6bad..21ff743 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -12,6 +12,12 @@ #define _MAX98090_H /* + * The default operating frequency for a DMIC attached to the codec. + * This can be overridden by a device tree property. + */ +#define MAX98090_DEFAULT_DMIC_FREQ 2500000 + +/* * MAX98090 Register Definitions */ @@ -1518,8 +1524,10 @@ struct max98090_priv { struct max98090_pdata *pdata; struct clk *mclk; unsigned int sysclk; + unsigned int pclk; unsigned int bclk; unsigned int lrclk; + u32 dmic_freq; struct max98090_cdata dai[1]; int jack_state; struct delayed_work jack_work; -- cgit v0.10.2 From 0c239fa6ebd20dd55d8978502d78b7c17441351a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 10:46:31 +0100 Subject: ASoC: sn95031: Use table based control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index cf8fa40..6167c59 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -867,9 +867,6 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec) snd_soc_write(codec, SN95031_SSR2, 0x10); snd_soc_write(codec, SN95031_SSR3, 0x40); - snd_soc_add_codec_controls(codec, sn95031_snd_controls, - ARRAY_SIZE(sn95031_snd_controls)); - return 0; } @@ -886,6 +883,9 @@ static struct snd_soc_codec_driver sn95031_codec = { .remove = sn95031_codec_remove, .set_bias_level = sn95031_set_vaud_bias, .idle_bias_off = true, + + .controls = sn95031_snd_controls, + .num_controls = ARRAY_SIZE(sn95031_snd_controls), .dapm_widgets = sn95031_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets), .dapm_routes = sn95031_audio_map, -- cgit v0.10.2 From e3f1ff318e78990977dae91f7f17f02e9af38e7d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 10:46:32 +0100 Subject: ASoC: tas2552: Use table based DAPM setup Makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index f039dc8..b505212 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -345,7 +345,6 @@ static const struct reg_default tas2552_init_regs[] = { static int tas2552_codec_probe(struct snd_soc_codec *codec) { struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; tas2552->codec = codec; @@ -390,11 +389,6 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN | TAS2552_LIM_EN); - snd_soc_dapm_new_controls(dapm, tas2552_dapm_widgets, - ARRAY_SIZE(tas2552_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, tas2552_audio_map, - ARRAY_SIZE(tas2552_audio_map)); - return 0; patch_fail: @@ -462,6 +456,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { .resume = tas2552_resume, .controls = tas2552_snd_controls, .num_controls = ARRAY_SIZE(tas2552_snd_controls), + .dapm_widgets = tas2552_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets), + .dapm_routes = tas2552_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map), }; static const struct regmap_config tas2552_regmap_config = { -- cgit v0.10.2 From c8b5d089d6fd614dfc8a04e3cf087c97486898fb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 10:46:33 +0100 Subject: ASoC: wl1273: Use table based control setup Makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index f3d4e88..00aea41 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -452,7 +452,6 @@ static int wl1273_probe(struct snd_soc_codec *codec) { struct wl1273_core **core = codec->dev->platform_data; struct wl1273_priv *wl1273; - int r; dev_dbg(codec->dev, "%s.\n", __func__); @@ -470,12 +469,7 @@ static int wl1273_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, wl1273); - r = snd_soc_add_codec_controls(codec, wl1273_controls, - ARRAY_SIZE(wl1273_controls)); - if (r) - kfree(wl1273); - - return r; + return 0; } static int wl1273_remove(struct snd_soc_codec *codec) @@ -492,6 +486,8 @@ static struct snd_soc_codec_driver soc_codec_dev_wl1273 = { .probe = wl1273_probe, .remove = wl1273_remove, + .controls = wl1273_controls, + .num_controls = ARRAY_SIZE(wl1273_controls), .dapm_widgets = wl1273_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), .dapm_routes = wl1273_dapm_routes, -- cgit v0.10.2 From a6bf30698825718f22a689a54ea023cdf51a4c76 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 10:46:34 +0100 Subject: ASoC: wm8737: Use table based DAPM and control setup Makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 744a422..fe41dd2 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -277,17 +277,6 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF", NULL, "ADCR" }, }; -static int wm8737_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm8737_dapm_widgets, - ARRAY_SIZE(wm8737_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - return 0; -} - /* codec mclk clock divider coefficients */ static const struct { u32 mclk; @@ -593,10 +582,6 @@ static int wm8737_probe(struct snd_soc_codec *codec) /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); - snd_soc_add_codec_controls(codec, wm8737_snd_controls, - ARRAY_SIZE(wm8737_snd_controls)); - wm8737_add_widgets(codec); - return 0; err_enable: @@ -617,6 +602,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8737 = { .suspend = wm8737_suspend, .resume = wm8737_resume, .set_bias_level = wm8737_set_bias_level, + + .controls = wm8737_snd_controls, + .num_controls = ARRAY_SIZE(wm8737_snd_controls), + .dapm_widgets = wm8737_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), }; static const struct of_device_id wm8737_of_match[] = { -- cgit v0.10.2 From c4f50dbc56580bc5fc84667860e973ca24291697 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 10:46:35 +0100 Subject: ASoC: wm8961: Use table based DAPM and control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 41d23e9..e077bb2 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -835,7 +835,6 @@ static struct snd_soc_dai_driver wm8961_dai = { static int wm8961_probe(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; u16 reg; /* Enable class W */ @@ -873,12 +872,6 @@ static int wm8961_probe(struct snd_soc_codec *codec) wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_codec_controls(codec, wm8961_snd_controls, - ARRAY_SIZE(wm8961_snd_controls)); - snd_soc_dapm_new_controls(dapm, wm8961_dapm_widgets, - ARRAY_SIZE(wm8961_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); - return 0; } @@ -915,6 +908,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8961 = { .suspend = wm8961_suspend, .resume = wm8961_resume, .set_bias_level = wm8961_set_bias_level, + + .controls = wm8961_snd_controls, + .num_controls = ARRAY_SIZE(wm8961_snd_controls), + .dapm_widgets = wm8961_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets), + .dapm_routes = audio_paths, + .num_dapm_routes = ARRAY_SIZE(audio_paths), }; static const struct regmap_config wm8961_regmap = { -- cgit v0.10.2 From b131c02e99b9da672a2b0cf96bad48d74c39572e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 10:46:36 +0100 Subject: ASoC: wm8995: Use table based DAPM and control setup Makes the code a bit cleaner. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 1288ede..e40c8a6 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2102,13 +2102,6 @@ static int wm8995_probe(struct snd_soc_codec *codec) wm8995_update_class_w(codec); - snd_soc_add_codec_controls(codec, wm8995_snd_controls, - ARRAY_SIZE(wm8995_snd_controls)); - snd_soc_dapm_new_controls(&codec->dapm, wm8995_dapm_widgets, - ARRAY_SIZE(wm8995_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, wm8995_intercon, - ARRAY_SIZE(wm8995_intercon)); - return 0; err_reg_enable: @@ -2205,6 +2198,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8995 = { .remove = wm8995_remove, .set_bias_level = wm8995_set_bias_level, .idle_bias_off = true, + + .controls = wm8995_snd_controls, + .num_controls = ARRAY_SIZE(wm8995_snd_controls), + .dapm_widgets = wm8995_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets), + .dapm_routes = wm8995_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8995_intercon), }; static struct regmap_config wm8995_regmap = { -- cgit v0.10.2 From d65fd3a42e00d322448f2518db6a3f0eb12ce1bd Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 5 Nov 2014 13:42:52 +0800 Subject: ASoC: rt5677: Minor coding style and typo fix Minor coding style and typo fix Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h index 7528bfd..ec41b2b 100644 --- a/sound/soc/codecs/rt5677-spi.h +++ b/sound/soc/codecs/rt5677-spi.h @@ -9,8 +9,8 @@ * published by the Free Software Foundation. */ -#ifndef __RT5671_SPI_H__ -#define __RT5671_SPI_H__ +#ifndef __RT5677_SPI_H__ +#define __RT5677_SPI_H__ #define RT5677_SPI_BUF_LEN 240 #define RT5677_SPI_CMD_BURST_WRITE 0x05 diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index ca264f8..0d24dc4 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -1353,7 +1353,7 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_ib45_bypass_src_mux = SOC_DAPM_ENUM("IB45 Bypass Source", rt5677_ib45_bypass_src_enum); -/* Stereo ADC Source 2 */ /* MX-27 MX26 MX25 [11:10] */ +/* Stereo ADC Source 2 */ /* MX-27 MX26 MX25 [11:10] */ static const char * const rt5677_stereo_adc2_src[] = { "DD MIX1", "DMIC", "Stereo DAC MIX" }; @@ -1438,7 +1438,7 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_sto2_adc_lr_mux = SOC_DAPM_ENUM("Stereo2 ADC LR Source", rt5677_stereo2_adc_lr_enum); -/* Stereo1 ADC Source 1 */ /* MX-27 MX26 MX25 [13:12] */ +/* Stereo1 ADC Source 1 */ /* MX-27 MX26 MX25 [13:12] */ static const char * const rt5677_stereo_adc1_src[] = { "DD MIX1", "ADC1/2", "Stereo DAC MIX" }; @@ -1710,7 +1710,7 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_pdm2_r_mux = SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_r_enum); -/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0]*/ +/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0] */ static const char * const rt5677_if12_adc1_src[] = { "STO1 ADC MIX", "OB01", "VAD ADC" }; @@ -1788,7 +1788,7 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_slb_adc3_mux = SOC_DAPM_ENUM("SLB ADC3 Source", rt5677_slb_adc3_enum); -/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10] MX-08 [7:6] */ +/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10] MX-08 [7:6] */ static const char * const rt5677_if12_adc4_src[] = { "STO4 ADC MIX", "OB67", "OB01" }; @@ -1814,7 +1814,7 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_slb_adc4_mux = SOC_DAPM_ENUM("SLB ADC4 Source", rt5677_slb_adc4_enum); -/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4]*/ +/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4] */ static const char * const rt5677_if34_adc_src[] = { "STO1 ADC MIX", "STO2 ADC MIX", "STO3 ADC MIX", "STO4 ADC MIX", "MONO ADC MIX", "OB01", "OB23", "VAD ADC" @@ -1895,7 +1895,7 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_if2_adc4_swap_mux = SOC_DAPM_ENUM("IF2 ADC4 Swap Source", rt5677_if2_adc4_swap_enum); -/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */ +/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */ static const char * const rt5677_if1_adc_tdm_swap_src[] = { "1/2/3/4", "2/1/3/4", "2/3/1/4", "4/1/2/3", "1/3/2/4", "1/4/2/3", "3/1/2/4", "3/4/1/2" @@ -2442,7 +2442,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { rt5677_ob_7_mix, ARRAY_SIZE(rt5677_ob_7_mix)), /* Output Side */ - /* DAC mixer before sound effect */ + /* DAC mixer before sound effect */ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, rt5677_dac_l_mix, ARRAY_SIZE(rt5677_dac_l_mix)), SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, -- cgit v0.10.2 From 1b86a3fa4eb3c7a6d738fa21475b92493f8952b1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 17:19:53 +0100 Subject: ASoC: ad193x: Keep DAC output stage active in idle Setting the DAC power-down bit for the ad193x will also disable the DAC output amplifier. This will cause audible clicks and pops when starting or stopping playback. To prevent this a new widget is introduced that controls the DAC power-down bit. This widget is connected to both the DAC and a newly introduced VMID widget. This makes sure that the DAC power-down bit is not set as long as a audio sink is connected to the DAC output. At the same time the PLL and SYSCLK will still be disabled when no playback or capture stream is active. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 6844d0b..387530b 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -72,11 +72,13 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = { }; static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { - SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1), + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0), + SND_SOC_DAPM_VMID("VMID"), SND_SOC_DAPM_OUTPUT("DAC1OUT"), SND_SOC_DAPM_OUTPUT("DAC2OUT"), SND_SOC_DAPM_OUTPUT("DAC3OUT"), @@ -87,13 +89,15 @@ static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { static const struct snd_soc_dapm_route audio_paths[] = { { "DAC", NULL, "SYSCLK" }, + { "DAC Output", NULL, "DAC" }, + { "DAC Output", NULL, "VMID" }, { "ADC", NULL, "SYSCLK" }, { "DAC", NULL, "ADC_PWR" }, { "ADC", NULL, "ADC_PWR" }, - { "DAC1OUT", NULL, "DAC" }, - { "DAC2OUT", NULL, "DAC" }, - { "DAC3OUT", NULL, "DAC" }, - { "DAC4OUT", NULL, "DAC" }, + { "DAC1OUT", NULL, "DAC Output" }, + { "DAC2OUT", NULL, "DAC Output" }, + { "DAC3OUT", NULL, "DAC Output" }, + { "DAC4OUT", NULL, "DAC Output" }, { "ADC", NULL, "ADC1IN" }, { "ADC", NULL, "ADC2IN" }, { "SYSCLK", NULL, "PLL_PWR" }, -- cgit v0.10.2 From 6c67cde2aa88bb06cd039aa0f61b26df887075d7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 6 Nov 2014 09:59:59 +0800 Subject: ASoC: rt286: set combo jack by dmi This patch enables combo jack configuration according to dmi. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 97daa80..d4acb64 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1205,6 +1206,16 @@ static const struct acpi_device_id rt286_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, rt286_acpi_match); +static struct dmi_system_id force_combo_jack_table[] __initdata = { + { + .ident = "Intel Wilson Beach", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS") + } + }, + { } +}; + static int rt286_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1240,6 +1251,9 @@ static int rt286_i2c_probe(struct i2c_client *i2c, if (pdata) rt286->pdata = *pdata; + if (dmi_check_system(force_combo_jack_table)) + rt286->pdata.cbj_en = true; + regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3); for (i = 0; i < RT286_POWER_REG_LEN; i++) -- cgit v0.10.2 From f8c101bc357d509291f6accb6f62b8439158a203 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 6 Nov 2014 10:00:00 +0800 Subject: ASoC: rt286: fix comment style Adds spaces around the /* */. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index d4acb64..2e818aa 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -191,7 +191,7 @@ static int rt286_hw_write(void *context, unsigned int reg, unsigned int value) u8 data[4]; int ret, i; - /*handle index registers*/ + /* handle index registers */ if (reg <= 0xff) { rt286_hw_write(client, RT286_COEF_INDEX, reg); for (i = 0; i < INDEX_CACHE_SIZE; i++) { @@ -234,7 +234,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value) __be32 be_reg; unsigned int index, vid, buf = 0x0; - /*handle index registers*/ + /* handle index registers */ if (reg <= 0xff) { rt286_hw_write(client, RT286_COEF_INDEX, reg); reg = RT286_PROC_COEF; @@ -1281,11 +1281,11 @@ static int rt286_i2c_probe(struct i2c_client *i2c, mdelay(10); regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000); - /*Power down LDO, VREF*/ + /* Power down LDO, VREF */ regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0); regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001); - /*Set depop parameter*/ + /* Set depop parameter */ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a); regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737); regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f); -- cgit v0.10.2 From bb656add19764c7a3cf28b2b330ec0a189fe4f48 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 5 Nov 2014 15:02:08 +0800 Subject: ASoC: rt5645: Add JD function support rt5645 codec support jack detection function. The patch will set related registers if JD function is used. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h index a535271..937f421 100644 --- a/include/sound/rt5645.h +++ b/include/sound/rt5645.h @@ -23,6 +23,9 @@ struct rt5645_platform_data { unsigned int hp_det_gpio; bool gpio_hp_det_active_high; + + /* true if codec's jd function is used */ + bool en_jd_func; }; #endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 1423cb2..286438d 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2203,6 +2203,13 @@ static int rt5645_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200); + /* for JD function */ + if (rt5645->pdata.en_jd_func) { + snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); + snd_soc_dapm_sync(&codec->dapm); + } + return 0; } @@ -2436,6 +2443,19 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, } + if (rt5645->pdata.en_jd_func) { + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, + RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU, + RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, + RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); + regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3, + RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL, + RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL); + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT); + } + if (rt5645->i2c->irq) { ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 5ec2520..82f681b 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -1348,6 +1348,8 @@ #define RT5645_PWR_CLK25M_SFT 4 #define RT5645_PWR_CLK25M_PD (0x0 << 4) #define RT5645_PWR_CLK25M_PU (0x1 << 4) +#define RT5645_IRQ_CLK_MCLK (0x0 << 3) +#define RT5645_IRQ_CLK_INT (0x1 << 3) /* VAD Control 4 (0x9d) */ #define RT5645_VAD_SEL_MASK (0x3 << 8) @@ -2116,6 +2118,9 @@ enum { #define RT5645_RXDP2_SEL_ADC (0x1 << 3) #define RT5645_RXDP2_SEL_SFT (3) +/* General Control3 (0xfc) */ +#define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11) +#define RT5645_MICINDET_MANU (0x1 << 7) /* Vendor ID (0xfd) */ #define RT5645_VER_C 0x2 -- cgit v0.10.2 From 29e1812d761183a6dd27c53d1259169e9e7ba4e2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 4 Nov 2014 16:25:15 +0530 Subject: ASoC: Intel: mrfld - remove unnecessary check for pointer the 'platform' pointer in sst_map_modules_to_pipe() is deref in caller function so we need to check for it in this function Reported-by: Dan Carpenter Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 309a8f3..90aa5c0 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -1351,7 +1351,7 @@ static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) int ret = 0; list_for_each_entry(w, &platform->component.card->widgets, list) { - if (platform && is_sst_dapm_widget(w) && (w->priv)) { + if (is_sst_dapm_widget(w) && (w->priv)) { struct sst_ids *ids = w->priv; dev_dbg(platform->dev, "widget type=%d name=%s\n", -- cgit v0.10.2 From f533a035e4da2fdd5e7b0100c84b62fd73ecd6c7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 4 Nov 2014 16:25:16 +0530 Subject: ASoC: Intel: mrfld - create separate module for pci part Now the SST_IPC will support both ACPI and PCI, separate into core module and PCI module. This also move probe function into PCI module and exports the required symbols from core module Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index ae7f872..c963a5d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -3,7 +3,7 @@ config SND_MFLD_MACHINE depends on INTEL_SCU_IPC select SND_SOC_SN95031 select SND_SST_MFLD_PLATFORM - select SND_SST_IPC + select SND_SST_IPC_PCI help This adds support for ASoC machine driver for Intel(R) MID Medfield platform used as alsa device in audio substem in Intel(R) MID devices @@ -16,6 +16,10 @@ config SND_SST_MFLD_PLATFORM config SND_SST_IPC tristate +config SND_SST_IPC_PCI + tristate + select SND_SST_IPC + config SND_SOC_INTEL_SST tristate "ASoC support for Intel(R) Smart Sound Technology" select SND_SOC_INTEL_SST_ACPI if ACPI diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile index 4d0e79b..b8aa1d3 100644 --- a/sound/soc/intel/sst/Makefile +++ b/sound/soc/intel/sst/Makefile @@ -1,3 +1,7 @@ -snd-intel-sst-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o +snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o +snd-intel-sst-pci-objs += sst_pci.o + + +obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o +obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o -obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst.o diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 2bfb404..8753754 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -20,20 +20,14 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include -#include #include #include #include #include #include #include -#include -#include #include -#include #include -#include -#include #include #include "../sst-mfld-platform.h" #include "sst.h" @@ -242,6 +236,7 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx, return 0; } +EXPORT_SYMBOL_GPL(sst_alloc_drv_context); int sst_context_init(struct intel_sst_drv *ctx) { @@ -260,6 +255,7 @@ int sst_context_init(struct intel_sst_drv *ctx) return -EINVAL; sst_init_locks(ctx); + sst_set_fw_state_locked(ctx, SST_RESET); /* pvt_id 0 reserved for async messages */ ctx->pvt_id = 1; @@ -307,12 +303,22 @@ int sst_context_init(struct intel_sst_drv *ctx) } pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + + dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name); + ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name, + ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb); + if (ret) { + dev_err(ctx->dev, "Firmware download failed:%d\n", ret); + goto do_free_mem; + } + sst_register(ctx->dev); return 0; do_free_mem: destroy_workqueue(ctx->post_msg_wq); return ret; } +EXPORT_SYMBOL_GPL(sst_context_init); void sst_context_cleanup(struct intel_sst_drv *ctx) { @@ -331,6 +337,7 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) sst_memcpy_free_resources(ctx); ctx = NULL; } +EXPORT_SYMBOL_GPL(sst_context_cleanup); void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { @@ -339,175 +346,7 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) pm_runtime_allow(ctx->dev); pm_runtime_put_noidle(ctx->dev); } - -static int sst_platform_get_resources(struct intel_sst_drv *ctx) -{ - int ddr_base, ret = 0; - struct pci_dev *pci = ctx->pci; - ret = pci_request_regions(pci, SST_DRV_NAME); - if (ret) - return ret; - - /* map registers */ - /* DDR base */ - if (ctx->dev_id == SST_MRFLD_PCI_ID) { - ctx->ddr_base = pci_resource_start(pci, 0); - /* check that the relocated IMR base matches with FW Binary */ - ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base); - if (!ctx->pdata->lib_info) { - dev_err(ctx->dev, "lib_info pointer NULL\n"); - ret = -EINVAL; - goto do_release_regions; - } - if (ddr_base != ctx->pdata->lib_info->mod_base) { - dev_err(ctx->dev, - "FW LSP DDR BASE does not match with IFWI\n"); - ret = -EINVAL; - goto do_release_regions; - } - ctx->ddr_end = pci_resource_end(pci, 0); - - ctx->ddr = pcim_iomap(pci, 0, - pci_resource_len(pci, 0)); - if (!ctx->ddr) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr); - } else { - ctx->ddr = NULL; - } - /* SHIM */ - ctx->shim_phy_add = pci_resource_start(pci, 1); - ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); - if (!ctx->shim) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim); - - /* Shared SRAM */ - ctx->mailbox_add = pci_resource_start(pci, 2); - ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); - if (!ctx->mailbox) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox); - - /* IRAM */ - ctx->iram_end = pci_resource_end(pci, 3); - ctx->iram_base = pci_resource_start(pci, 3); - ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); - if (!ctx->iram) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram); - - /* DRAM */ - ctx->dram_end = pci_resource_end(pci, 4); - ctx->dram_base = pci_resource_start(pci, 4); - ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); - if (!ctx->dram) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram); -do_release_regions: - pci_release_regions(pci); - return 0; -} -/* -* intel_sst_probe - PCI probe function -* -* @pci: PCI device structure -* @pci_id: PCI device ID structure -* -*/ -static int intel_sst_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) -{ - int ret = 0; - struct intel_sst_drv *sst_drv_ctx; - struct sst_platform_info *sst_pdata = pci->dev.platform_data; - - dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); - - ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); - if (ret < 0) - return ret; - - sst_drv_ctx->pdata = sst_pdata; - sst_drv_ctx->irq_num = pci->irq; - - ret = sst_context_init(sst_drv_ctx); - if (ret < 0) - goto do_free_drv_ctx; - - - /* Init the device */ - ret = pcim_enable_device(pci); - if (ret) { - dev_err(sst_drv_ctx->dev, - "device can't be enabled. Returned err: %d\n", ret); - goto do_destroy_wq; - } - sst_drv_ctx->pci = pci_dev_get(pci); - - ret = sst_platform_get_resources(sst_drv_ctx); - if (ret < 0) - goto do_destroy_wq; - - sst_set_fw_state_locked(sst_drv_ctx, SST_RESET); - snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name), - "%s%04x%s", "fw_sst_", - sst_drv_ctx->dev_id, ".bin"); - dev_dbg(sst_drv_ctx->dev, - "Requesting FW %s now...\n", sst_drv_ctx->firmware_name); - ret = request_firmware_nowait(THIS_MODULE, 1, - sst_drv_ctx->firmware_name, sst_drv_ctx->dev, - GFP_KERNEL, sst_drv_ctx, sst_firmware_load_cb); - - if (ret) { - dev_err(sst_drv_ctx->dev, - "Firmware load failed with error: %d\n", ret); - goto do_release_regions; - } - - - pci_set_drvdata(pci, sst_drv_ctx); - sst_configure_runtime_pm(sst_drv_ctx); - sst_register(sst_drv_ctx->dev); - - return ret; - -do_release_regions: - pci_release_regions(pci); -do_destroy_wq: - destroy_workqueue(sst_drv_ctx->post_msg_wq); -do_free_drv_ctx: - dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); - return ret; -} - -/** -* intel_sst_remove - PCI remove function -* -* @pci: PCI device structure -* -* This function is called by OS when a device is unloaded -* This frees the interrupt etc -*/ -static void intel_sst_remove(struct pci_dev *pci) -{ - struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); - - sst_context_cleanup(sst_drv_ctx); - pci_dev_put(sst_drv_ctx->pci); - pci_release_regions(pci); - pci_set_drvdata(pci, NULL); -} +EXPORT_SYMBOL_GPL(sst_configure_runtime_pm); static int intel_sst_runtime_suspend(struct device *dev) { @@ -546,27 +385,8 @@ static int intel_sst_runtime_resume(struct device *dev) return ret; } -static const struct dev_pm_ops intel_sst_pm = { +const struct dev_pm_ops intel_sst_pm = { .runtime_suspend = intel_sst_runtime_suspend, .runtime_resume = intel_sst_runtime_resume, }; - -/* PCI Routines */ -static struct pci_device_id intel_sst_ids[] = { - { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, - { 0, } -}; - -static struct pci_driver sst_driver = { - .name = SST_DRV_NAME, - .id_table = intel_sst_ids, - .probe = intel_sst_probe, - .remove = intel_sst_remove, -#ifdef CONFIG_PM - .driver = { - .pm = &intel_sst_pm, - }, -#endif -}; - -module_pci_driver(sst_driver); +EXPORT_SYMBOL_GPL(intel_sst_pm); diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index b65b9c0..3ee555e 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -40,6 +40,7 @@ #define MRFLD_FW_FEATURE_BASE_OFFSET 0x4 #define MRFLD_FW_BSS_RESET_BIT 0 +extern const struct dev_pm_ops intel_sst_pm; enum sst_states { SST_FW_LOADING = 1, SST_FW_RUNNING, @@ -537,4 +538,9 @@ void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, int sst_register(struct device *); int sst_unregister(struct device *); +int sst_alloc_drv_context(struct intel_sst_drv **ctx, + struct device *dev, unsigned int dev_id); +int sst_context_init(struct intel_sst_drv *ctx); +void sst_context_cleanup(struct intel_sst_drv *ctx); +void sst_configure_runtime_pm(struct intel_sst_drv *ctx); #endif diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c new file mode 100644 index 0000000..3a0b3bf --- /dev/null +++ b/sound/soc/intel/sst/sst_pci.c @@ -0,0 +1,209 @@ +/* + * sst_pci.c - SST (LPE) driver init file for pci enumeration. + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" + +static int sst_platform_get_resources(struct intel_sst_drv *ctx) +{ + int ddr_base, ret = 0; + struct pci_dev *pci = ctx->pci; + + ret = pci_request_regions(pci, SST_DRV_NAME); + if (ret) + return ret; + + /* map registers */ + /* DDR base */ + if (ctx->dev_id == SST_MRFLD_PCI_ID) { + ctx->ddr_base = pci_resource_start(pci, 0); + /* check that the relocated IMR base matches with FW Binary */ + ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base); + if (!ctx->pdata->lib_info) { + dev_err(ctx->dev, "lib_info pointer NULL\n"); + ret = -EINVAL; + goto do_release_regions; + } + if (ddr_base != ctx->pdata->lib_info->mod_base) { + dev_err(ctx->dev, + "FW LSP DDR BASE does not match with IFWI\n"); + ret = -EINVAL; + goto do_release_regions; + } + ctx->ddr_end = pci_resource_end(pci, 0); + + ctx->ddr = pcim_iomap(pci, 0, + pci_resource_len(pci, 0)); + if (!ctx->ddr) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr); + } else { + ctx->ddr = NULL; + } + /* SHIM */ + ctx->shim_phy_add = pci_resource_start(pci, 1); + ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); + if (!ctx->shim) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim); + + /* Shared SRAM */ + ctx->mailbox_add = pci_resource_start(pci, 2); + ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); + if (!ctx->mailbox) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox); + + /* IRAM */ + ctx->iram_end = pci_resource_end(pci, 3); + ctx->iram_base = pci_resource_start(pci, 3); + ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); + if (!ctx->iram) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram); + + /* DRAM */ + ctx->dram_end = pci_resource_end(pci, 4); + ctx->dram_base = pci_resource_start(pci, 4); + ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); + if (!ctx->dram) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram); +do_release_regions: + pci_release_regions(pci); + return 0; +} + +/* + * intel_sst_probe - PCI probe function + * + * @pci: PCI device structure + * @pci_id: PCI device ID structure + * + */ +static int intel_sst_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + int ret = 0; + struct intel_sst_drv *sst_drv_ctx; + struct sst_platform_info *sst_pdata = pci->dev.platform_data; + + dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); + ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); + if (ret < 0) + return ret; + + sst_drv_ctx->pdata = sst_pdata; + sst_drv_ctx->irq_num = pci->irq; + snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name), + "%s%04x%s", "fw_sst_", + sst_drv_ctx->dev_id, ".bin"); + + ret = sst_context_init(sst_drv_ctx); + if (ret < 0) + return ret; + + /* Init the device */ + ret = pcim_enable_device(pci); + if (ret) { + dev_err(sst_drv_ctx->dev, + "device can't be enabled. Returned err: %d\n", ret); + goto do_free_drv_ctx; + } + sst_drv_ctx->pci = pci_dev_get(pci); + ret = sst_platform_get_resources(sst_drv_ctx); + if (ret < 0) + goto do_free_drv_ctx; + + pci_set_drvdata(pci, sst_drv_ctx); + sst_configure_runtime_pm(sst_drv_ctx); + + return ret; + +do_free_drv_ctx: + sst_context_cleanup(sst_drv_ctx); + dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); + return ret; +} + +/** + * intel_sst_remove - PCI remove function + * + * @pci: PCI device structure + * + * This function is called by OS when a device is unloaded + * This frees the interrupt etc + */ +static void intel_sst_remove(struct pci_dev *pci) +{ + struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); + + sst_context_cleanup(sst_drv_ctx); + pci_dev_put(sst_drv_ctx->pci); + pci_release_regions(pci); + pci_set_drvdata(pci, NULL); +} + +/* PCI Routines */ +static struct pci_device_id intel_sst_ids[] = { + { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, + { 0, } +}; + +static struct pci_driver sst_driver = { + .name = SST_DRV_NAME, + .id_table = intel_sst_ids, + .probe = intel_sst_probe, + .remove = intel_sst_remove, +#ifdef CONFIG_PM + .driver = { + .pm = &intel_sst_pm, + }, +#endif +}; + +module_pci_driver(sst_driver); + +MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver"); +MODULE_AUTHOR("Vinod Koul "); +MODULE_AUTHOR("Harsha Priya "); +MODULE_AUTHOR("Dharageswari R "); +MODULE_AUTHOR("KP Jeeja "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("sst"); diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c index 1c2e081..9a5df19 100644 --- a/sound/soc/intel/sst/sst_pvt.c +++ b/sound/soc/intel/sst/sst_pvt.c @@ -433,6 +433,7 @@ u32 relocate_imr_addr_mrfld(u32 base_addr) base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024)); return base_addr; } +EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld); void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, struct ipc_post *msg) -- cgit v0.10.2 From b0d94acd634a5cff7fe5fc46131a23997e8d0f60 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 4 Nov 2014 16:25:17 +0530 Subject: ASoC: Intel: mrfld - add shim save restore In ACPI platform we need to save few registers of Shim on suspend and restore them on resume, so add handlers to do this Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 8753754..b97c231 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -339,6 +339,34 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) } EXPORT_SYMBOL_GPL(sst_context_cleanup); +static inline void sst_save_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + + shim_regs->imrx = sst_shim_read64(shim, SST_IMRX), + + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + +static inline void sst_restore_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + + /* + * we only need to restore IMRX for this case, rest will be + * initialize by FW or driver when firmware is loaded + */ + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); -- cgit v0.10.2 From feec843d6c4528263724ff3f4c463ea82bf63b4a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 6 Nov 2014 15:59:22 +0100 Subject: ASoC: ssm4567: Add DAC high-pass-filter control Add a switch which can be used to enable/disable the DAC high-pass-filter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 4b5c17f..e1e33d8 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -145,6 +145,8 @@ static const struct snd_kcontrol_new ssm4567_snd_controls[] = { SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0, 0xff, 1, ssm4567_vol_tlv), SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0), + SOC_SINGLE("DAC High Pass Filter Switch", SSM4567_REG_DAC_CTRL, + 5, 1, 0), }; static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = { -- cgit v0.10.2 From ead99f89b7cd2b5cfe99601380a6f6f0a1ce7e53 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 6 Nov 2014 15:59:23 +0100 Subject: ASoC: ssm4567: Add support for setting the DAI format and TDM configuration The SSM4567 has support for a couple of different DAI formats. In TDM mode it is also possible to select the TDM slot. This patch adds support for this by implementing the set_fmt and set_tdm_slot callbacks. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index e1e33d8..2176679 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -69,6 +69,22 @@ #define SSM4567_DAC_FS_64000_96000 0x3 #define SSM4567_DAC_FS_128000_192000 0x4 +/* SAI_CTRL_1 */ +#define SSM4567_SAI_CTRL_1_BCLK BIT(6) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK (0x3 << 4) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_32 (0x0 << 4) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_48 (0x1 << 4) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_64 (0x2 << 4) +#define SSM4567_SAI_CTRL_1_FSYNC BIT(3) +#define SSM4567_SAI_CTRL_1_LJ BIT(2) +#define SSM4567_SAI_CTRL_1_TDM BIT(1) +#define SSM4567_SAI_CTRL_1_PDM BIT(0) + +/* SAI_CTRL_2 */ +#define SSM4567_SAI_CTRL_2_AUTO_SLOT BIT(3) +#define SSM4567_SAI_CTRL_2_TDM_SLOT_MASK 0x7 +#define SSM4567_SAI_CTRL_2_TDM_SLOT(x) (x) + struct ssm4567 { struct regmap *regmap; }; @@ -194,6 +210,107 @@ static int ssm4567_mute(struct snd_soc_dai *dai, int mute) SSM4567_DAC_MUTE, val); } +static int ssm4567_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai); + unsigned int blcks; + int slot; + int ret; + + if (tx_mask == 0) + return -EINVAL; + + if (rx_mask && rx_mask != tx_mask) + return -EINVAL; + + slot = __ffs(tx_mask); + if (tx_mask != BIT(slot)) + return -EINVAL; + + switch (width) { + case 32: + blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_32; + break; + case 48: + blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_48; + break; + case 64: + blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_64; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_2, + SSM4567_SAI_CTRL_2_AUTO_SLOT | SSM4567_SAI_CTRL_2_TDM_SLOT_MASK, + SSM4567_SAI_CTRL_2_TDM_SLOT(slot)); + if (ret) + return ret; + + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, + SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK, blcks); +} + +static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai); + unsigned int ctrl1 = 0; + bool invert_fclk; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_fclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1 |= SSM4567_SAI_CTRL_1_BCLK; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC; + invert_fclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl1 |= SSM4567_SAI_CTRL_1_BCLK; + invert_fclk = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1 |= SSM4567_SAI_CTRL_1_LJ; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1 |= SSM4567_SAI_CTRL_1_TDM; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1 |= SSM4567_SAI_CTRL_1_TDM | SSM4567_SAI_CTRL_1_LJ; + break; + case SND_SOC_DAIFMT_PDM: + ctrl1 |= SSM4567_SAI_CTRL_1_PDM; + break; + default: + return -EINVAL; + } + + if (invert_fclk) + ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC; + + return regmap_write(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, ctrl1); +} + static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable) { int ret = 0; @@ -248,6 +365,8 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec, static const struct snd_soc_dai_ops ssm4567_dai_ops = { .hw_params = ssm4567_hw_params, .digital_mute = ssm4567_mute, + .set_fmt = ssm4567_set_dai_fmt, + .set_tdm_slot = ssm4567_set_tdm_slot, }; static struct snd_soc_dai_driver ssm4567_dai = { -- cgit v0.10.2 From 5ad72152b695ba5027f9c6ec9a48a8e1a70f25dc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 6 Nov 2014 15:59:24 +0100 Subject: ASoC: ssm4567: Add support for disabling the boost stage This patch adds a switch to enable/disable boost stage of the output amplifier. Applications that know that they do not need the output amplifier boost stage can disable it to conserve a bit of power. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 2176679..a984485 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -165,13 +165,20 @@ static const struct snd_kcontrol_new ssm4567_snd_controls[] = { 5, 1, 0), }; +static const struct snd_kcontrol_new ssm4567_amplifier_boost_control = + SOC_DAPM_SINGLE("Switch", SSM4567_REG_POWER_CTRL, 1, 1, 1); + static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1), + SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1, + &ssm4567_amplifier_boost_control), SND_SOC_DAPM_OUTPUT("OUT"), }; static const struct snd_soc_dapm_route ssm4567_routes[] = { + { "OUT", NULL, "Amplifier Boost" }, + { "Amplifier Boost", "Switch", "DAC" }, { "OUT", NULL, "DAC" }, }; -- cgit v0.10.2 From 9b105fe447116ee3cd7fe3c09ca6a6d6a05c736b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 6 Nov 2014 19:30:43 +0530 Subject: ASoC: Intel: mrfld - remove non static definition sst_save_shim64() is defined as static in code but header is non static. Since this is not used other than file where defined remove non static definition Reported-by: kbuild test robot Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index 3ee555e..2dcbf47 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -508,8 +508,6 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst, size_t mbox_data_len, const void *mbox_data, void **data, bool large, bool fill_dsp, bool sync, bool response); -void sst_save_shim64(struct intel_sst_drv *ctx, void __iomem *shim, - struct sst_shim_regs64 *shim_regs); void sst_process_pending_msg(struct work_struct *work); int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx); void sst_init_stream(struct stream_info *stream, -- cgit v0.10.2 From 1c5d1c988302f324ac396ac13461d59d091be605 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Nov 2014 20:26:53 -0800 Subject: ASoC: rsnd: control DVC_DVUCR under rsnd_dvc_volume_update() rsnd_dvc_volume_update() is main function to control DVC feature like Digital Volume / Mute / Ramp etc. DVC_DVUCR should be controlled under this function. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index deaf0fa..3952237 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -40,6 +40,7 @@ struct rsnd_dvc { static void rsnd_dvc_volume_update(struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 dvucr = 0; u32 mute = 0; int i; @@ -47,10 +48,18 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) mute |= (!!dvc->mute.val[i]) << i; } + /* Enable Digital Volume */ + dvucr = 0x100; rsnd_mod_write(mod, DVC_VOL0R, dvc->volume.val[0]); rsnd_mod_write(mod, DVC_VOL1R, dvc->volume.val[1]); - rsnd_mod_write(mod, DVC_ZCMCR, mute); + /* Enable Mute */ + if (mute) { + dvucr |= 0x1; + rsnd_mod_write(mod, DVC_ZCMCR, mute); + } + + rsnd_mod_write(mod, DVC_DVUCR, dvucr); } static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, @@ -103,9 +112,6 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod)); - /* enable Volume / Mute */ - rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101); - /* ch0/ch1 Volume */ rsnd_dvc_volume_update(dvc_mod); -- cgit v0.10.2 From 140bab8961eb4047070b46a6dd50ec87496e0cde Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Nov 2014 20:27:18 -0800 Subject: ASoC: rsnd: move DVC_DVUER settings under rsnd_dvc_volume_update() We need to Enable/Disable DVC_DVUER register if we set DVCp_ZCMCR, DVCp_VRCTR, DVCp_VRPDR, DVCp_VRDBR, DVCp_VOL0R, DVCp_VOL1R, DVCp_VOL2R, DVCp_VOL3R, DVCp_VOL4R, DVCp_VOL5R, DVCp_VOL6R, DVCp_VOL7R and, these are controlled under rsnd_dvc_volume_update(). This patch moves DVC_DVUER settings to it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 3952237..ce1512e 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -48,6 +48,9 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) mute |= (!!dvc->mute.val[i]) << i; } + /* Disable DVC Register access */ + rsnd_mod_write(mod, DVC_DVUER, 0); + /* Enable Digital Volume */ dvucr = 0x100; rsnd_mod_write(mod, DVC_VOL0R, dvc->volume.val[0]); @@ -60,6 +63,9 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) } rsnd_mod_write(mod, DVC_DVUCR, dvucr); + + /* Enable DVC Register access */ + rsnd_mod_write(mod, DVC_DVUER, 1); } static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, @@ -117,8 +123,6 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); - rsnd_mod_write(dvc_mod, DVC_DVUER, 1); - rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io); return 0; -- cgit v0.10.2 From ec14af91a03f7d68b2a72bec20be2ab583d3f63a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Nov 2014 20:27:46 -0800 Subject: ASoC: rsnd: enable multiple DVC valume settings DVC controls some digital volume features. Some of them requests values for "each channels", but, some of them requests values for "feature". Current dvc.c is supporting Mute/Volume, and these have "each channels" settings. This patch adds rsnd_dvc_cfg_m and care about multiple settings for each channels. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index ce1512e..c729e26 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -17,6 +17,12 @@ struct rsnd_dvc_cfg { unsigned int max; + unsigned int size; + u32 *val; +}; + +struct rsnd_dvc_cfg_m { + struct rsnd_dvc_cfg cfg; u32 val[RSND_DVC_CHANNELS]; }; @@ -24,8 +30,8 @@ struct rsnd_dvc { struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; - struct rsnd_dvc_cfg volume; - struct rsnd_dvc_cfg mute; + struct rsnd_dvc_cfg_m volume; + struct rsnd_dvc_cfg_m mute; }; #define rsnd_mod_to_dvc(_mod) \ @@ -44,9 +50,8 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) u32 mute = 0; int i; - for (i = 0; i < RSND_DVC_CHANNELS; i++) { - mute |= (!!dvc->mute.val[i]) << i; - } + for (i = 0; i < dvc->mute.cfg.size; i++) + mute |= (!!dvc->mute.cfg.val[i]) << i; /* Disable DVC Register access */ rsnd_mod_write(mod, DVC_DVUER, 0); @@ -159,7 +164,7 @@ static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, { struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - uinfo->count = RSND_DVC_CHANNELS; + uinfo->count = cfg->size; uinfo->value.integer.min = 0; uinfo->value.integer.max = cfg->max; @@ -177,7 +182,7 @@ static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; int i; - for (i = 0; i < RSND_DVC_CHANNELS; i++) + for (i = 0; i < cfg->size; i++) ucontrol->value.integer.value[i] = cfg->val[i]; return 0; @@ -190,7 +195,7 @@ static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; int i, change = 0; - for (i = 0; i < RSND_DVC_CHANNELS; i++) { + for (i = 0; i < cfg->size; i++) { change |= (ucontrol->value.integer.value[i] != cfg->val[i]); cfg->val[i] = ucontrol->value.integer.value[i]; } @@ -230,6 +235,19 @@ static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod, return 0; } +static int _rsnd_dvc_pcm_new_m(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_dvc_cfg_m *private, + u32 max) +{ + private->cfg.max = max; + private->cfg.size = RSND_DVC_CHANNELS; + private->cfg.val = private->val; + return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); +} + static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd) @@ -239,20 +257,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, int ret; /* Volume */ - dvc->volume.max = 0x00800000 - 1; - ret = __rsnd_dvc_pcm_new(mod, rdai, rtd, + ret = _rsnd_dvc_pcm_new_m(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Playback Volume" : "DVC In Capture Volume", - &dvc->volume); + &dvc->volume, 0x00800000 - 1); if (ret < 0) return ret; /* Mute */ - dvc->mute.max = 1; - ret = __rsnd_dvc_pcm_new(mod, rdai, rtd, + ret = _rsnd_dvc_pcm_new_m(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Mute Switch" : "DVC In Mute Switch", - &dvc->mute); + &dvc->mute, 1); if (ret < 0) return ret; -- cgit v0.10.2 From ab2e479667507329475c8ef93d61f3dbe654c3c2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Nov 2014 20:28:10 -0800 Subject: ASoC: rsnd: enable single DVC valume settings DVC controls some digital volume features. Some of them requests values for "each channels", but, some of them requests values for "feature". And, Volume Ramp has "feature" settings. This patch adds rsnd_dvc_cfg_s and care about single settings. Compiler will report like below at this point, but, it will be removed if Volume Ramp was supported. warning: '_rsnd_dvc_pcm_new_s' defined but not used Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index c729e26..e7cfc71 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -26,6 +26,11 @@ struct rsnd_dvc_cfg_m { u32 val[RSND_DVC_CHANNELS]; }; +struct rsnd_dvc_cfg_s { + struct rsnd_dvc_cfg cfg; + u32 val; +}; + struct rsnd_dvc { struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; @@ -248,6 +253,19 @@ static int _rsnd_dvc_pcm_new_m(struct rsnd_mod *mod, return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); } +static int _rsnd_dvc_pcm_new_s(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_dvc_cfg_s *private, + u32 max) +{ + private->cfg.max = max; + private->cfg.size = 1; + private->cfg.val = &private->val; + return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); +} + static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd) -- cgit v0.10.2 From 018342976ce971944dd4d9309f75e86382079a2b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Nov 2014 20:28:50 -0800 Subject: ASoC: rsnd: enable enumerated DVC valume settings DVC controls some digital volume features. Volume Ramp is listed as "XX dB / YY steps", and this enumerated settings are easy for users. This patch adds rsnd_dvc_cfg_e and care about enumerated settings. Compiler will report like below at this point, but, it will be removed if Volume Ramp was supported. warning: '_rsnd_dvc_pcm_new_e' defined but not used Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index e7cfc71..8504f6b 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -19,6 +19,7 @@ struct rsnd_dvc_cfg { unsigned int max; unsigned int size; u32 *val; + const char * const *texts; }; struct rsnd_dvc_cfg_m { @@ -169,14 +170,23 @@ static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, { struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - uinfo->count = cfg->size; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = cfg->max; - - if (cfg->max == 1) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + if (cfg->texts) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = cfg->size; + uinfo->value.enumerated.items = cfg->max; + if (uinfo->value.enumerated.item >= cfg->max) + uinfo->value.enumerated.item = cfg->max - 1; + strlcpy(uinfo->value.enumerated.name, + cfg->texts[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + } else { + uinfo->count = cfg->size; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = cfg->max; + uinfo->type = (cfg->max == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : + SNDRV_CTL_ELEM_TYPE_INTEGER; + } return 0; } @@ -188,7 +198,10 @@ static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, int i; for (i = 0; i < cfg->size; i++) - ucontrol->value.integer.value[i] = cfg->val[i]; + if (cfg->texts) + ucontrol->value.enumerated.item[i] = cfg->val[i]; + else + ucontrol->value.integer.value[i] = cfg->val[i]; return 0; } @@ -201,8 +214,13 @@ static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, int i, change = 0; for (i = 0; i < cfg->size; i++) { - change |= (ucontrol->value.integer.value[i] != cfg->val[i]); - cfg->val[i] = ucontrol->value.integer.value[i]; + if (cfg->texts) { + change |= (ucontrol->value.enumerated.item[i] != cfg->val[i]); + cfg->val[i] = ucontrol->value.enumerated.item[i]; + } else { + change |= (ucontrol->value.integer.value[i] != cfg->val[i]); + cfg->val[i] = ucontrol->value.integer.value[i]; + } } if (change) @@ -266,6 +284,21 @@ static int _rsnd_dvc_pcm_new_s(struct rsnd_mod *mod, return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); } +static int _rsnd_dvc_pcm_new_e(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_dvc_cfg_s *private, + const char * const *texts, + u32 max) +{ + private->cfg.max = max; + private->cfg.size = 1; + private->cfg.val = &private->val; + private->cfg.texts = texts; + return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); +} + static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd) -- cgit v0.10.2 From b07597367001c2c4f36a97863530f71b84060d3d Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 7 Nov 2014 12:24:39 +0530 Subject: ASoC: Samsung: Add quirk for internal DMA Internal DMA is available only on some of Samsung platforms. So added a quirk for the same and made it optional. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index a6591c6..5e0bc77 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -27,6 +27,7 @@ struct samsung_i2s { #define QUIRK_NO_MUXPSR (1 << 2) #define QUIRK_NEED_RSTCLR (1 << 3) #define QUIRK_SUPPORTS_TDM (1 << 4) +#define QUIRK_SUPPORTS_IDMA (1 << 5) /* Quirks of the I2S controller */ u32 quirks; dma_addr_t idma_addr; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 9d51347..38b9a52 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -987,7 +987,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (i2s->quirks & QUIRK_NEED_RSTCLR) writel(CON_RSTCLR, i2s->addr + I2SCON); - if (i2s->quirks & QUIRK_SEC_DAI) + if (i2s->quirks & QUIRK_SUPPORTS_IDMA) idma_reg_addr_init(i2s->addr, i2s->sec_dai->idma_playback.dma_addr); @@ -1199,10 +1199,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) quirks = i2s_dai_data->quirks; if (of_property_read_u32(np, "samsung,idma-addr", &idma_addr)) { - if (quirks & QUIRK_SEC_DAI) { - dev_err(&pdev->dev, "idma address is not"\ + if (quirks & QUIRK_SUPPORTS_IDMA) { + dev_info(&pdev->dev, "idma address is not"\ "specified"); - return -EINVAL; } } } @@ -1309,13 +1308,14 @@ static const struct samsung_i2s_dai_data i2sv3_dai_type = { static const struct samsung_i2s_dai_data i2sv5_dai_type = { .dai_type = TYPE_PRI, - .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_IDMA, }; static const struct samsung_i2s_dai_data i2sv6_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | - QUIRK_SUPPORTS_TDM, + QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, }; static const struct samsung_i2s_dai_data samsung_dai_type_pri = { -- cgit v0.10.2 From a5a56871f804edac93a53b5e871c0e9818fb9033 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 7 Nov 2014 12:24:40 +0530 Subject: ASoC: samsung: add support for exynos7 I2S controller Exynos7 I2S controller has no internal dma, supports more no. of root clock sampling frequencies and has more no.of Rx fifos to support 7.1CH recording in TDM mode. Due to more no. of root clock frequency values some of the bit offsets got shifted up by one. Also I2S1 on previous Samsung platforms uses v3 dai type but on Exynos7 it is upgraded to v5 with slightly modified register offsets for supporting more no.of RFS values. Due to the above changes, the driver has to be modified to handle all versions of I2S controller. For this I introduced a new structure to hold modified bit offsets and masks which is passed as dai data. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 7386d44..d188296 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -6,10 +6,17 @@ Required SoC Specific Properties: - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with secondary fifo, s/w reset control and internal mux for root clk src. - - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with - secondary fifo, s/w reset control, internal mux for root clk src and - TDM support. TDM (Time division multiplexing) is to allow transfer of - multiple channel audio data on single data line. + - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for + playback, sterio channel capture, secondary fifo using internal + or external dma, s/w reset control, internal mux for root clk src + and 7.1 channel TDM support for playback. TDM (Time division multiplexing) + is to allow transfer of multiple channel audio data on single data line. + - samsung,exynos7-i2s: with all the available features of exynos5 i2s, + exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo + with only external dma and more no.of root clk sampling frequencies. + - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports + stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with + slightly modified bit offsets. - reg: physical base address of the controller and length of memory mapped region. diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 55a3869..e0e737f 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,6 +1,6 @@ config SND_SOC_SAMSUNG tristate "ASoC support for Samsung" - depends on PLAT_SAMSUNG + depends on (PLAT_SAMSUNG || ARCH_EXYNOS) depends on S3C64XX_PL080 || !ARCH_S3C64XX depends on S3C24XX_DMAC || !ARCH_S3C24XX select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index 821a502..9170c31 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -33,8 +33,9 @@ #define I2SLVL3ADDR 0x3c #define I2SSTR1 0x40 #define I2SVER 0x44 -#define I2SFIC2 0x48 +#define I2SFIC1 0x48 #define I2STDM 0x4c +#define I2SFSTA 0x50 #define CON_RSTCLR (1 << 31) #define CON_FRXOFSTATUS (1 << 26) @@ -93,8 +94,6 @@ #define MOD_BLC_24BIT (2 << 13) #define MOD_BLC_MASK (3 << 13) -#define MOD_IMS_SYSMUX (1 << 10) -#define MOD_SLAVE (1 << 11) #define MOD_TXONLY (0 << 8) #define MOD_RXONLY (1 << 8) #define MOD_TXRX (2 << 8) @@ -132,7 +131,10 @@ #define EXYNOS5420_MOD_BCLK_256FS 8 #define EXYNOS5420_MOD_BCLK_MASK 0xf -#define MOD_CDCLKCON (1 << 12) +#define EXYNOS7_MOD_RCLK_64FS 4 +#define EXYNOS7_MOD_RCLK_128FS 5 +#define EXYNOS7_MOD_RCLK_96FS 6 +#define EXYNOS7_MOD_RCLK_192FS 7 #define PSR_PSREN (1 << 15) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 38b9a52..947352d 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -36,9 +36,24 @@ enum samsung_dai_type { TYPE_SEC, }; +struct samsung_i2s_variant_regs { + unsigned int bfs_off; + unsigned int rfs_off; + unsigned int sdf_off; + unsigned int txr_off; + unsigned int rclksrc_off; + unsigned int mss_off; + unsigned int cdclkcon_off; + unsigned int lrp_off; + unsigned int bfs_mask; + unsigned int rfs_mask; + unsigned int ftx0cnt_off; +}; + struct samsung_i2s_dai_data { int dai_type; u32 quirks; + const struct samsung_i2s_variant_regs *i2s_variant_regs; }; struct i2s_dai { @@ -81,6 +96,7 @@ struct i2s_dai { u32 suspend_i2scon; u32 suspend_i2spsr; unsigned long gpios[7]; /* i2s gpio line numbers */ + const struct samsung_i2s_variant_regs *variant_regs; }; /* Lock for cross i/f checks */ @@ -95,7 +111,8 @@ static inline bool is_secondary(struct i2s_dai *i2s) /* If operating in SoC-Slave mode */ static inline bool is_slave(struct i2s_dai *i2s) { - return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false; + u32 mod = readl(i2s->addr + I2SMOD); + return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false; } /* If this interface of the controller is transmitting data */ @@ -200,14 +217,14 @@ static inline bool is_manager(struct i2s_dai *i2s) static inline unsigned get_rfs(struct i2s_dai *i2s) { u32 rfs; - - if (i2s->quirks & QUIRK_SUPPORTS_TDM) - rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT; - else - rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); - rfs &= MOD_RCLK_MASK; + rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off; + rfs &= i2s->variant_regs->rfs_mask; switch (rfs) { + case 7: return 192; + case 6: return 96; + case 5: return 128; + case 4: return 64; case 3: return 768; case 2: return 384; case 1: return 512; @@ -219,15 +236,23 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { u32 mod = readl(i2s->addr + I2SMOD); - int rfs_shift; + int rfs_shift = i2s->variant_regs->rfs_off; - if (i2s->quirks & QUIRK_SUPPORTS_TDM) - rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT; - else - rfs_shift = MOD_RCLK_SHIFT; - mod &= ~(MOD_RCLK_MASK << rfs_shift); + mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift); switch (rfs) { + case 192: + mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift); + break; + case 96: + mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift); + break; + case 128: + mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift); + break; + case 64: + mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift); + break; case 768: mod |= (MOD_RCLK_768FS << rfs_shift); break; @@ -249,14 +274,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) static inline unsigned get_bfs(struct i2s_dai *i2s) { u32 bfs; - - if (i2s->quirks & QUIRK_SUPPORTS_TDM) { - bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT; - bfs &= EXYNOS5420_MOD_BCLK_MASK; - } else { - bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; - bfs &= MOD_BCLK_MASK; - } + bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off; + bfs &= i2s->variant_regs->bfs_mask; switch (bfs) { case 8: return 256; @@ -275,16 +294,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { u32 mod = readl(i2s->addr + I2SMOD); - int bfs_shift; int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - - if (i2s->quirks & QUIRK_SUPPORTS_TDM) { - bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT; - mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift); - } else { - bfs_shift = MOD_BCLK_SHIFT; - mod &= ~(MOD_BCLK_MASK << bfs_shift); - } + int bfs_shift = i2s->variant_regs->bfs_off; /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ if (!tdm && bfs > 48) { @@ -292,6 +303,8 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) return; } + mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift); + switch (bfs) { case 48: mod |= (MOD_BCLK_48FS << bfs_shift); @@ -346,8 +359,9 @@ static inline int get_blc(struct i2s_dai *i2s) static void i2s_txctrl(struct i2s_dai *i2s, int on) { void __iomem *addr = i2s->addr; + int txr_off = i2s->variant_regs->txr_off; u32 con = readl(addr + I2SCON); - u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; + u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); if (on) { con |= CON_ACTIVE; @@ -362,9 +376,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) } if (any_rx_active(i2s)) - mod |= MOD_TXRX; + mod |= 2 << txr_off; else - mod |= MOD_TXONLY; + mod |= 0 << txr_off; } else { if (is_secondary(i2s)) { con |= CON_TXSDMA_PAUSE; @@ -382,7 +396,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) con |= CON_TXCH_PAUSE; if (any_rx_active(i2s)) - mod |= MOD_RXONLY; + mod |= 1 << txr_off; else con &= ~CON_ACTIVE; } @@ -395,23 +409,24 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) static void i2s_rxctrl(struct i2s_dai *i2s, int on) { void __iomem *addr = i2s->addr; + int txr_off = i2s->variant_regs->txr_off; u32 con = readl(addr + I2SCON); - u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; + u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); if (on) { con |= CON_RXDMA_ACTIVE | CON_ACTIVE; con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); if (any_tx_active(i2s)) - mod |= MOD_TXRX; + mod |= 2 << txr_off; else - mod |= MOD_RXONLY; + mod |= 1 << txr_off; } else { con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; con &= ~CON_RXDMA_ACTIVE; if (any_tx_active(i2s)) - mod |= MOD_TXONLY; + mod |= 0 << txr_off; else con &= ~CON_ACTIVE; } @@ -451,6 +466,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; u32 mod = readl(i2s->addr + I2SMOD); + const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; + unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; + unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; switch (clk_id) { case SAMSUNG_I2S_OPCLK: @@ -465,18 +483,18 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, if ((rfs && other && other->rfs && (other->rfs != rfs)) || (any_active(i2s) && (((dir == SND_SOC_CLOCK_IN) - && !(mod & MOD_CDCLKCON)) || + && !(mod & cdcon_mask)) || ((dir == SND_SOC_CLOCK_OUT) - && (mod & MOD_CDCLKCON))))) { + && (mod & cdcon_mask))))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } if (dir == SND_SOC_CLOCK_IN) - mod |= MOD_CDCLKCON; + mod |= 1 << i2s_regs->cdclkcon_off; else - mod &= ~MOD_CDCLKCON; + mod &= 0 << i2s_regs->cdclkcon_off; i2s->rfs = rfs; break; @@ -491,8 +509,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, if (!any_active(i2s)) { if (i2s->op_clk && !IS_ERR(i2s->op_clk)) { - if ((clk_id && !(mod & MOD_IMS_SYSMUX)) || - (!clk_id && (mod & MOD_IMS_SYSMUX))) { + if ((clk_id && !(mod & rsrc_mask)) || + (!clk_id && (mod & rsrc_mask))) { clk_disable_unprepare(i2s->op_clk); clk_put(i2s->op_clk); } else { @@ -520,8 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, other->op_clk = i2s->op_clk; other->rclk_srcrate = i2s->rclk_srcrate; } - } else if ((!clk_id && (mod & MOD_IMS_SYSMUX)) - || (clk_id && !(mod & MOD_IMS_SYSMUX))) { + } else if ((!clk_id && (mod & rsrc_mask)) + || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; @@ -533,10 +551,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } if (clk_id == 0) - mod &= ~MOD_IMS_SYSMUX; + mod &= 0 << i2s_regs->rclksrc_off; else - mod |= MOD_IMS_SYSMUX; - break; + mod |= 1 << i2s_regs->rclksrc_off; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); @@ -553,16 +570,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); - int lrp_shift, sdf_shift, sdf_mask, lrp_rlow; + int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; u32 tmp = 0; - if (i2s->quirks & QUIRK_SUPPORTS_TDM) { - lrp_shift = EXYNOS5420_MOD_LRP_SHIFT; - sdf_shift = EXYNOS5420_MOD_SDF_SHIFT; - } else { - lrp_shift = MOD_LRP_SHIFT; - sdf_shift = MOD_SDF_SHIFT; - } + lrp_shift = i2s->variant_regs->lrp_off; + sdf_shift = i2s->variant_regs->sdf_off; + mod_slave = 1 << i2s->variant_regs->mss_off; sdf_mask = MOD_SDF_MASK << sdf_shift; lrp_rlow = MOD_LR_RLOW << lrp_shift; @@ -605,7 +618,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - tmp |= MOD_SLAVE; + tmp |= mod_slave; break; case SND_SOC_DAIFMT_CBS_CFS: /* Set default source clock in Master mode */ @@ -623,13 +636,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, * channel. */ if (any_active(i2s) && - ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) { + ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } - mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE); + mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod |= tmp; writel(mod, i2s->addr + I2SMOD); @@ -751,6 +764,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; unsigned long flags; + const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; spin_lock_irqsave(&lock, flags); @@ -761,7 +775,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, other->mode |= DAI_MANAGER; } else { u32 mod = readl(i2s->addr + I2SMOD); - i2s->cdclk_out = !(mod & MOD_CDCLKCON); + i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off)); if (other) other->cdclk_out = i2s->cdclk_out; } @@ -914,13 +928,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) struct i2s_dai *i2s = to_info(dai); u32 reg = readl(i2s->addr + I2SFIC); snd_pcm_sframes_t delay; + const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) delay = FIC_RXCOUNT(reg); else if (is_secondary(i2s)) delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS)); else - delay = FIC_TXCOUNT(reg); + delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f; return delay; } @@ -1227,6 +1242,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->dma_capture.dma_size = 4; pri_dai->base = regs_base; pri_dai->quirks = quirks; + pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; if (quirks & QUIRK_PRI_6CHAN) pri_dai->i2s_dai_drv.playback.channels_max = 6; @@ -1301,21 +1317,93 @@ static int samsung_i2s_remove(struct platform_device *pdev) return 0; } +static const struct samsung_i2s_variant_regs i2sv3_regs = { + .bfs_off = 1, + .rfs_off = 3, + .sdf_off = 5, + .txr_off = 8, + .rclksrc_off = 10, + .mss_off = 11, + .cdclkcon_off = 12, + .lrp_off = 7, + .bfs_mask = 0x3, + .rfs_mask = 0x3, + .ftx0cnt_off = 8, +}; + +static const struct samsung_i2s_variant_regs i2sv6_regs = { + .bfs_off = 0, + .rfs_off = 4, + .sdf_off = 6, + .txr_off = 8, + .rclksrc_off = 10, + .mss_off = 11, + .cdclkcon_off = 12, + .lrp_off = 15, + .bfs_mask = 0xf, + .rfs_mask = 0x3, + .ftx0cnt_off = 8, +}; + +static const struct samsung_i2s_variant_regs i2sv7_regs = { + .bfs_off = 0, + .rfs_off = 4, + .sdf_off = 7, + .txr_off = 9, + .rclksrc_off = 11, + .mss_off = 12, + .cdclkcon_off = 22, + .lrp_off = 15, + .bfs_mask = 0xf, + .rfs_mask = 0x7, + .ftx0cnt_off = 0, +}; + +static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = { + .bfs_off = 0, + .rfs_off = 3, + .sdf_off = 6, + .txr_off = 8, + .rclksrc_off = 10, + .mss_off = 11, + .cdclkcon_off = 12, + .lrp_off = 15, + .bfs_mask = 0x7, + .rfs_mask = 0x7, + .ftx0cnt_off = 8, +}; + static const struct samsung_i2s_dai_data i2sv3_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_NO_MUXPSR, + .i2s_variant_regs = &i2sv3_regs, }; static const struct samsung_i2s_dai_data i2sv5_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_IDMA, + .i2s_variant_regs = &i2sv3_regs, }; static const struct samsung_i2s_dai_data i2sv6_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, + .i2s_variant_regs = &i2sv6_regs, +}; + +static const struct samsung_i2s_dai_data i2sv7_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_TDM, + .i2s_variant_regs = &i2sv7_regs, +}; + +static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, + .i2s_variant_regs = &i2sv5_i2s1_regs, }; static const struct samsung_i2s_dai_data samsung_dai_type_pri = { @@ -1349,6 +1437,12 @@ static const struct of_device_id exynos_i2s_match[] = { }, { .compatible = "samsung,exynos5420-i2s", .data = &i2sv6_dai_type, + }, { + .compatible = "samsung,exynos7-i2s", + .data = &i2sv7_dai_type, + }, { + .compatible = "samsung,exynos7-i2s1", + .data = &i2sv5_dai_type_i2s1, }, {}, }; -- cgit v0.10.2 From 19ba484d7b15c8650b30377aad6e65b34d3cf3d5 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 5 Nov 2014 13:42:53 +0800 Subject: ASoC: rt5677: Use specific r/w function for DSP mode In DSP mode, the register r/w should use the specific function to access that is invoked by address mapping of the DSP. The MX-65[1] is for switching DSP or codec mode. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 0d24dc4..4b6f7d5 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -541,49 +541,51 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg) /** * rt5677_dsp_mode_i2c_write_addr - Write value to address on DSP mode. - * @codec: SoC audio codec device. + * @rt5677: Private Data. * @addr: Address index. * @value: Address data. * * * Returns 0 for success or negative error code. */ -static int rt5677_dsp_mode_i2c_write_addr(struct snd_soc_codec *codec, +static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, unsigned int addr, unsigned int value, unsigned int opcode) { - struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_codec *codec = rt5677->codec; int ret; mutex_lock(&rt5677->dsp_cmd_lock); - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); if (ret < 0) { dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret); goto err; } - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_LSB, + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, addr & 0xffff); if (ret < 0) { dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret); goto err; } - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_DATA_MSB, + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, value >> 16); if (ret < 0) { dev_err(codec->dev, "Failed to set data msb value: %d\n", ret); goto err; } - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_DATA_LSB, + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, value & 0xffff); if (ret < 0) { dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret); goto err; } - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_OP_CODE, opcode); + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + opcode); if (ret < 0) { dev_err(codec->dev, "Failed to set op code value: %d\n", ret); goto err; @@ -597,42 +599,45 @@ err: /** * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode. - * @codec: SoC audio codec device. + * rt5677: Private Data. * @addr: Address index. * @value: Address data. * + * * Returns 0 for success or negative error code. */ static int rt5677_dsp_mode_i2c_read_addr( - struct snd_soc_codec *codec, unsigned int addr, unsigned int *value) + struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value) { - struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_codec *codec = rt5677->codec; int ret; unsigned int msb, lsb; mutex_lock(&rt5677->dsp_cmd_lock); - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_MSB, addr >> 16); + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); if (ret < 0) { dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret); goto err; } - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_LSB, + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, addr & 0xffff); if (ret < 0) { dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret); goto err; } - ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_OP_CODE , 0x0002); + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + 0x0002); if (ret < 0) { dev_err(codec->dev, "Failed to set op code value: %d\n", ret); goto err; } - regmap_read(rt5677->regmap, RT5677_DSP_I2C_DATA_MSB, &msb); - regmap_read(rt5677->regmap, RT5677_DSP_I2C_DATA_LSB, &lsb); + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); *value = (msb << 16) | lsb; err: @@ -643,17 +648,17 @@ err: /** * rt5677_dsp_mode_i2c_write - Write register on DSP mode. - * @codec: SoC audio codec device. + * rt5677: Private Data. * @reg: Register index. * @value: Register data. * * * Returns 0 for success or negative error code. */ -static int rt5677_dsp_mode_i2c_write(struct snd_soc_codec *codec, +static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677, unsigned int reg, unsigned int value) { - return rt5677_dsp_mode_i2c_write_addr(codec, 0x18020000 + reg * 2, + return rt5677_dsp_mode_i2c_write_addr(rt5677, 0x18020000 + reg * 2, value, 0x0001); } @@ -661,57 +666,33 @@ static int rt5677_dsp_mode_i2c_write(struct snd_soc_codec *codec, * rt5677_dsp_mode_i2c_read - Read register on DSP mode. * @codec: SoC audio codec device. * @reg: Register index. + * @value: Register data. * * - * Returns Register value. + * Returns 0 for success or negative error code. */ -static unsigned int rt5677_dsp_mode_i2c_read( - struct snd_soc_codec *codec, unsigned int reg) +static int rt5677_dsp_mode_i2c_read( + struct rt5677_priv *rt5677, unsigned int reg, unsigned int *value) { - unsigned int value = 0; + int ret = rt5677_dsp_mode_i2c_read_addr(rt5677, 0x18020000 + reg * 2, + value); - rt5677_dsp_mode_i2c_read_addr(codec, 0x18020000 + reg * 2, &value); + *value &= 0xffff; - return value; + return ret; } -/** - * rt5677_dsp_mode_i2c_update_bits - update register on DSP mode. - * @codec: audio codec - * @reg: register index. - * @mask: register mask - * @value: new value - * - * - * Returns 1 for change, 0 for no change, or negative error code. - */ -static int rt5677_dsp_mode_i2c_update_bits(struct snd_soc_codec *codec, - unsigned int reg, unsigned int mask, unsigned int value) +static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on) { - unsigned int old, new; - int change, ret; - - ret = rt5677_dsp_mode_i2c_read(codec, reg); - if (ret < 0) { - dev_err(codec->dev, "Failed to read reg: %d\n", ret); - goto err; - } + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); - old = ret; - new = (old & ~mask) | (value & mask); - change = old != new; - if (change) { - ret = rt5677_dsp_mode_i2c_write(codec, reg, new); - if (ret < 0) { - dev_err(codec->dev, - "Failed to write reg: %d\n", ret); - goto err; - } + if (on) { + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2); + rt5677->is_dsp_mode = true; + } else { + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x0); + rt5677->is_dsp_mode = false; } - return change; - -err: - return ret; } static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) @@ -733,9 +714,14 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) RT5677_LDO1_SEL_MASK, 0x0); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, RT5677_PWR_LDO1, RT5677_PWR_LDO1); - regmap_write(rt5677->regmap, RT5677_GLB_CLK2, 0x0080); + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC); + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, + RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK, + RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS); regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff); - regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07ff); + regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd); + rt5677_set_dsp_mode(codec, true); ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1, codec->dev); @@ -751,8 +737,7 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) release_firmware(rt5677->fw2); } - rt5677_dsp_mode_i2c_update_bits(codec, RT5677_PWR_DSP1, 0x1, - 0x0); + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0); regcache_cache_bypass(rt5677->regmap, false); regcache_cache_only(rt5677->regmap, true); @@ -762,9 +747,9 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) regcache_cache_only(rt5677->regmap, false); regcache_cache_bypass(rt5677->regmap, true); - rt5677_dsp_mode_i2c_update_bits(codec, RT5677_PWR_DSP1, 0x1, - 0x1); - rt5677_dsp_mode_i2c_write(codec, RT5677_PWR_DSP1, 0x0001); + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1); + rt5677_set_dsp_mode(codec, false); + regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001); regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); @@ -4019,6 +4004,32 @@ static int rt5677_resume(struct snd_soc_codec *codec) #define rt5677_resume NULL #endif +static int rt5677_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct rt5677_priv *rt5677 = i2c_get_clientdata(client); + + if (rt5677->is_dsp_mode) + rt5677_dsp_mode_i2c_read(rt5677, reg, val); + else + regmap_read(rt5677->regmap_physical, reg, val); + + return 0; +} + +static int rt5677_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct rt5677_priv *rt5677 = i2c_get_clientdata(client); + + if (rt5677->is_dsp_mode) + rt5677_dsp_mode_i2c_write(rt5677, reg, val); + else + regmap_write(rt5677->regmap_physical, reg, val); + + return 0; +} + #define RT5677_STEREO_RATES SNDRV_PCM_RATE_8000_96000 #define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) @@ -4144,6 +4155,17 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5677 = { .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes), }; +static const struct regmap_config rt5677_regmap_physical = { + .name = "physical", + .reg_bits = 8, + .val_bits = 16, + + .max_register = RT5677_VENDOR_ID2 + 1, + .readable_reg = rt5677_readable_register, + + .cache_type = REGCACHE_NONE, +}; + static const struct regmap_config rt5677_regmap = { .reg_bits = 8, .val_bits = 16, @@ -4153,6 +4175,8 @@ static const struct regmap_config rt5677_regmap = { .volatile_reg = rt5677_volatile_register, .readable_reg = rt5677_readable_register, + .reg_read = rt5677_read, + .reg_write = rt5677_write, .cache_type = REGCACHE_RBTREE, .reg_defaults = rt5677_reg, @@ -4309,7 +4333,16 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, msleep(10); } - rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap); + rt5677->regmap_physical = devm_regmap_init_i2c(i2c, + &rt5677_regmap_physical); + if (IS_ERR(rt5677->regmap_physical)) { + ret = PTR_ERR(rt5677->regmap_physical); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + rt5677->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5677_regmap); if (IS_ERR(rt5677->regmap)) { ret = PTR_ERR(rt5677->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 2f5b8c6..9d473b2 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1628,7 +1628,7 @@ enum { struct rt5677_priv { struct snd_soc_codec *codec; struct rt5677_platform_data pdata; - struct regmap *regmap; + struct regmap *regmap, *regmap_physical; const struct firmware *fw1, *fw2; struct mutex dsp_cmd_lock; @@ -1646,6 +1646,7 @@ struct rt5677_priv { #endif bool dsp_vad_en; struct regmap_irq_chip_data *irq_data; + bool is_dsp_mode; }; #endif /* __RT5677_H__ */ -- cgit v0.10.2 From 9e2683530d6f78b30bcf4cabb97d1b7d6b925b85 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 31 Oct 2014 15:37:55 +0800 Subject: ASoC: rt5645: Add ASRC support This patch add ASRC support for rt5645 codec. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 286438d..1dbbebc 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -441,6 +441,65 @@ static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum, RT5645_TDM_CTRL_1, 8, rt5645_tdm_adc_data_select); +static int rt5645_clk_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + unsigned int u_bit = 0, p_bit = 0; + struct soc_enum *em = + (struct soc_enum *)kcontrol->private_value; + + switch (em->reg) { + case RT5645_ASRC_2: + switch (em->shift_l) { + case 0: + u_bit = 0x8; + p_bit = RT5645_PWR_ADC_S1F; + break; + case 4: + u_bit = 0x100; + p_bit = RT5645_PWR_DAC_MF_R; + break; + case 8: + u_bit = 0x200; + p_bit = RT5645_PWR_DAC_MF_L; + break; + case 12: + u_bit = 0x400; + p_bit = RT5645_PWR_DAC_S1F; + break; + } + break; + case RT5645_ASRC_3: + switch (em->shift_l) { + case 0: + u_bit = 0x1; + p_bit = RT5645_PWR_ADC_MF_R; + break; + case 4: + u_bit = 0x2; + p_bit = RT5645_PWR_ADC_MF_L; + break; + } + break; + } + + if (u_bit || p_bit) { + switch (ucontrol->value.integer.value[0]) { + case 1 ... 4: /*enable*/ + if (snd_soc_read(codec, RT5645_PWR_DIG2) & p_bit) + snd_soc_update_bits(codec, + RT5645_ASRC_1, u_bit, u_bit); + break; + default: /*disable*/ + snd_soc_update_bits(codec, RT5645_ASRC_1, u_bit, 0); + break; + } + } + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, @@ -552,6 +611,53 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, return 0; } +static int is_using_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + + switch (source->shift) { + case 0: + reg = RT5645_ASRC_3; + shift = 0; + break; + case 1: + reg = RT5645_ASRC_3; + shift = 4; + break; + case 3: + reg = RT5645_ASRC_2; + shift = 0; + break; + case 8: + reg = RT5645_ASRC_2; + shift = 4; + break; + case 9: + reg = RT5645_ASRC_2; + shift = 8; + break; + case 10: + reg = RT5645_ASRC_2; + shift = 12; + break; + default: + return 0; + } + + val = (snd_soc_read(source->codec, reg) >> shift) & 0xf; + switch (val) { + case 1: + case 2: + case 3: + case 4: + return 1; + default: + return 0; + } + +} + /* Digital Mixer */ static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER, @@ -1244,6 +1350,30 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL, RT5645_PWR_MIC_DET_BIT, 0, NULL, 0), + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1, + 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1, + 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1, + 10, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1, + 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1, + 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1, + 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1, + 0, 0, NULL, 0), + /* Input Side */ /* micbias */ SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2, @@ -1502,6 +1632,17 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { }; static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { + { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc }, + { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc }, + { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc }, + { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc }, + { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc }, + { "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc }, + { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc }, + + { "I2S1", NULL, "I2S1 ASRC" }, + { "I2S2", NULL, "I2S2 ASRC" }, + { "IN1P", NULL, "LDO2" }, { "IN2P", NULL, "LDO2" }, @@ -1548,12 +1689,15 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" }, { "Mono DMIC L Mux", "DMIC1", "DMIC L1" }, { "Mono DMIC L Mux", "DMIC2", "DMIC L2" }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" }, { "Mono DMIC R Mux", "DMIC1", "DMIC R1" }, { "Mono DMIC R Mux", "DMIC2", "DMIC R2" }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" }, { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" }, { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, -- cgit v0.10.2 From cf1f2ebe8d6176de80ef9d9c979f998ec38fb265 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 3 Nov 2014 19:33:03 +0100 Subject: ASoC: wm9712/wm9713: Replace virtual registers with custom put/get callbacks The wm9712/wm9713 has separate mixers for the left and the right channel, but the inputs to the mixers are enabled/disabled by the same control. Currently this is implemented by the driver by registering two virtual controls for each physical control, one for the left mixer and one for the right mixer. Using virtual registers will no longer work when the driver has been converted to regmap. This patch converts the driver to use controls with custom put/get callbacks instead which implement the logic making sure that the physical control is unmuted when either the left or the right control is unmuted. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index f3aab6e..3fad37e 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -23,6 +23,11 @@ #include #include "wm9712.h" +struct wm9712_priv { + unsigned int hp_mixer[2]; + struct mutex lock; +}; + static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg); static int ac97_write(struct snd_soc_codec *codec, @@ -48,12 +53,10 @@ static const u16 wm9712_reg[] = { 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */ - 0x0000, 0x0000 /* virtual hp mixers */ }; -/* virtual HP mixers regs */ -#define HPL_MIXER 0x80 -#define HPR_MIXER 0x82 +#define HPL_MIXER 0x0 +#define HPR_MIXER 0x1 static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; @@ -157,75 +160,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv), }; +static const unsigned int wm9712_mixer_mute_regs[] = { + AC97_VIDEO, + AC97_PCM, + AC97_LINE, + AC97_PHONE, + AC97_CD, + AC97_PC_BEEP, +}; + /* We have to create a fake left and right HP mixers because * the codec only has a single control that is shared by both channels. * This makes it impossible to determine the audio path. */ -static int mixer_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) +static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - u16 l, r, beep, line, phone, mic, pcm, aux; - - l = ac97_read(w->codec, HPL_MIXER); - r = ac97_read(w->codec, HPR_MIXER); - beep = ac97_read(w->codec, AC97_PC_BEEP); - mic = ac97_read(w->codec, AC97_VIDEO); - phone = ac97_read(w->codec, AC97_PHONE); - line = ac97_read(w->codec, AC97_LINE); - pcm = ac97_read(w->codec, AC97_PCM); - aux = ac97_read(w->codec, AC97_CD); - - if (l & 0x1 || r & 0x1) - ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); + unsigned int val = ucontrol->value.enumerated.item[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, mask, shift, old; + struct snd_soc_dapm_update update; + bool change; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + mask = 1 << shift; + + mutex_lock(&wm9712->lock); + old = wm9712->hp_mixer[mixer]; + if (ucontrol->value.enumerated.item[0]) + wm9712->hp_mixer[mixer] |= mask; else - ac97_write(w->codec, AC97_VIDEO, mic | 0x8000); + wm9712->hp_mixer[mixer] &= ~mask; + + change = old != wm9712->hp_mixer[mixer]; + if (change) { + update.kcontrol = kcontrol; + update.reg = wm9712_mixer_mute_regs[shift]; + update.mask = 0x8000; + if ((wm9712->hp_mixer[0] & mask) || + (wm9712->hp_mixer[1] & mask)) + update.val = 0x0; + else + update.val = 0x8000; + + snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, + &update); + } - if (l & 0x2 || r & 0x2) - ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); - else - ac97_write(w->codec, AC97_PCM, pcm | 0x8000); + mutex_unlock(&wm9712->lock); - if (l & 0x4 || r & 0x4) - ac97_write(w->codec, AC97_LINE, line & 0x7fff); - else - ac97_write(w->codec, AC97_LINE, line | 0x8000); + return change; +} - if (l & 0x8 || r & 0x8) - ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); - else - ac97_write(w->codec, AC97_PHONE, phone | 0x8000); +static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int shift, mixer; - if (l & 0x10 || r & 0x10) - ac97_write(w->codec, AC97_CD, aux & 0x7fff); - else - ac97_write(w->codec, AC97_CD, aux | 0x8000); + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; - if (l & 0x20 || r & 0x20) - ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); - else - ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); + ucontrol->value.enumerated.item[0] = + (wm9712->hp_mixer[mixer] >> shift) & 1; return 0; } +#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \ + .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \ + (xmixer << 8) | xshift, 1, 0, 0) \ +} + /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { - SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0), - SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0), - SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0), - SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0), - SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0), - SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0), + WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5), + WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4), + WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3), + WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2), + WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1), + WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0), }; /* Right Headphone Mixers */ static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { - SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0), - SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0), - SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0), - SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0), - SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0), - SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0), + WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5), + WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4), + WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3), + WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2), + WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1), + WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0), }; /* Speaker Mixer */ @@ -299,12 +335,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, &wm9712_diff_sel_controls), SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), -SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1, - &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls), - mixer_event, SND_SOC_DAPM_POST_REG), -SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1, - &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls), - mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1, + &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1, + &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)), SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)), SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, @@ -471,8 +505,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, { u16 *cache = codec->reg_cache; - if (reg < 0x7c) - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(codec->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9712_reg))) cache[reg] = val; @@ -684,6 +717,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { static int wm9712_probe(struct platform_device *pdev) { + struct wm9712_priv *wm9712; + + wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL); + if (wm9712 == NULL) + return -ENOMEM; + + mutex_init(&wm9712->lock); + + platform_set_drvdata(pdev, wm9712); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai)); } diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index ac13fc8..998e4c7 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -31,6 +31,8 @@ struct wm9713_priv { u32 pll_in; /* PLL input frequency */ + unsigned int hp_mixer[2]; + struct mutex lock; }; static unsigned int ac97_read(struct snd_soc_codec *codec, @@ -59,12 +61,10 @@ static const u16 wm9713_reg[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x0001, 0x0000, 0x574d, 0x4c13, - 0x0000, 0x0000 }; -/* virtual HP mixers regs */ -#define HPL_MIXER 0x80 -#define HPR_MIXER 0x82 +#define HPL_MIXER 0 +#define HPR_MIXER 1 static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; @@ -233,6 +233,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, return 0; } +static const unsigned int wm9713_mixer_mute_regs[] = { + AC97_PC_BEEP, + AC97_MASTER_TONE, + AC97_PHONE, + AC97_REC_SEL, + AC97_PCM, + AC97_AUX, +}; /* We have to create a fake left and right HP mixers because * the codec only has a single control that is shared by both channels. @@ -240,73 +248,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, * register map, thus we add a new (virtual) register to help determine the * audio route within the device. */ -static int mixer_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - u16 l, r, beep, tone, phone, rec, pcm, aux; - - l = ac97_read(w->codec, HPL_MIXER); - r = ac97_read(w->codec, HPR_MIXER); - beep = ac97_read(w->codec, AC97_PC_BEEP); - tone = ac97_read(w->codec, AC97_MASTER_TONE); - phone = ac97_read(w->codec, AC97_PHONE); - rec = ac97_read(w->codec, AC97_REC_SEL); - pcm = ac97_read(w->codec, AC97_PCM); - aux = ac97_read(w->codec, AC97_AUX); - - if (event & SND_SOC_DAPM_PRE_REG) - return 0; - if ((l & 0x1) || (r & 0x1)) - ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + unsigned int val = ucontrol->value.enumerated.item[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, mask, shift, old; + struct snd_soc_dapm_update update; + bool change; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + mask = (1 << shift); + + mutex_lock(&wm9713->lock); + old = wm9713->hp_mixer[mixer]; + if (ucontrol->value.enumerated.item[0]) + wm9713->hp_mixer[mixer] |= mask; else - ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); + wm9713->hp_mixer[mixer] &= ~mask; + + change = old != wm9713->hp_mixer[mixer]; + if (change) { + update.kcontrol = kcontrol; + update.reg = wm9713_mixer_mute_regs[shift]; + update.mask = 0x8000; + if ((wm9713->hp_mixer[0] & mask) || + (wm9713->hp_mixer[1] & mask)) + update.val = 0x0; + else + update.val = 0x8000; + + snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, + &update); + } - if ((l & 0x2) || (r & 0x2)) - ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff); - else - ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000); + mutex_unlock(&wm9713->lock); - if ((l & 0x4) || (r & 0x4)) - ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); - else - ac97_write(w->codec, AC97_PHONE, phone | 0x8000); + return change; +} - if ((l & 0x8) || (r & 0x8)) - ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff); - else - ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000); +static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, shift; - if ((l & 0x10) || (r & 0x10)) - ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); - else - ac97_write(w->codec, AC97_PCM, pcm | 0x8000); + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; - if ((l & 0x20) || (r & 0x20)) - ac97_write(w->codec, AC97_AUX, aux & 0x7fff); - else - ac97_write(w->codec, AC97_AUX, aux | 0x8000); + ucontrol->value.enumerated.item[0] = + (wm9713->hp_mixer[mixer] >> shift) & 1; return 0; } +#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \ + .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \ + xshift, xmixer, 1, 0, 0) \ +} + /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { -SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0), -SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0), -SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0), -SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0), -SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0), -SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0), +WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5), +WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4), +WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3), +WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2), +WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1), +WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0), }; /* Right Headphone Mixers */ static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { -SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0), -SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0), -SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0), -SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0), -SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0), -SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0), +WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5), +WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4), +WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3), +WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2), +WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1), +WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0), }; /* headphone capture mux */ @@ -428,12 +458,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, &wm9713_mic_sel_mux_controls), SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, &wm9713_micb_sel_mux_controls), -SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, - &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls), - mixer_event, SND_SOC_DAPM_POST_REG), -SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, - &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls), - mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, + &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, + &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)), SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, @@ -666,8 +694,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { u16 *cache = codec->reg_cache; - if (reg < 0x7c) - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(codec->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9713_reg))) cache[reg] = val; @@ -1251,6 +1278,8 @@ static int wm9713_probe(struct platform_device *pdev) if (wm9713 == NULL) return -ENOMEM; + mutex_init(&wm9713->lock); + platform_set_drvdata(pdev, wm9713); return snd_soc_register_codec(&pdev->dev, -- cgit v0.10.2 From 6cc79294efefde2593eaf72effebc8b1cc71d5ac Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 8 Nov 2014 16:38:06 +0100 Subject: ASoC: Forward calls to snd_soc_cache_sync() to regcache_sync() For convenience for drivers that do not want to keep their own pointer to regmap struct around forward calls to snd_soc_cache_sync() to regcache_sync() if the driver is using regmap. This is similar to what we do for snd_soc_read()/snd_soc_write(). This patch also fixes drivers which already have been converted to regmap, but still use snd_soc_cache_sync() for trying to the sync the cache. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index a9f82b5..6dab817 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -187,6 +187,9 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) const char *name = "flat"; int ret; + if (codec->component.regmap) + return regcache_sync(codec->component.regmap); + if (!codec->cache_sync) return 0; -- cgit v0.10.2 From 427d204c86e095bb91eb8af381bd90a48376a860 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 8 Nov 2014 16:38:07 +0100 Subject: ASoC: Remove snd_soc_cache_sync() implementation This function has no more non regmap user, which means we can remove the implementation of the function and associated functions and structure fields. For convenience we keep a static inline version of the function that forwards calls to regcache_sync() unconditionally. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ba7130..fadcb35 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -409,13 +409,9 @@ int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); -int snd_soc_cache_sync(struct snd_soc_codec *codec); int snd_soc_cache_init(struct snd_soc_codec *codec); int snd_soc_cache_exit(struct snd_soc_codec *codec); -int snd_soc_cache_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value); -int snd_soc_cache_read(struct snd_soc_codec *codec, - unsigned int reg, unsigned int *value); + int snd_soc_platform_read(struct snd_soc_platform *platform, unsigned int reg); int snd_soc_platform_write(struct snd_soc_platform *platform, @@ -791,13 +787,11 @@ struct snd_soc_codec { unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int cache_init:1; /* codec cache has been initialized */ - u32 cache_sync; /* Cache needs to be synced to hardware */ /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ hw_write_t hw_write; void *reg_cache; - struct mutex cache_rw_mutex; /* component */ struct snd_soc_component component; @@ -1264,6 +1258,17 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); +/** + * snd_soc_cache_sync() - Sync the register cache with the hardware + * @codec: CODEC to sync + * + * Note: This function will call regcache_sync() + */ +static inline int snd_soc_cache_sync(struct snd_soc_codec *codec) +{ + return regcache_sync(codec->component.regmap); +} + /* component IO */ int snd_soc_component_read(struct snd_soc_component *component, unsigned int reg, unsigned int *val); diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index b04ee7e..88cf39d 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -288,31 +288,6 @@ TRACE_EVENT(snd_soc_jack_notify, TP_printk("jack=%s %x", __get_str(name), (int)__entry->val) ); -TRACE_EVENT(snd_soc_cache_sync, - - TP_PROTO(struct snd_soc_codec *codec, const char *type, - const char *status), - - TP_ARGS(codec, type, status), - - TP_STRUCT__entry( - __string( name, codec->component.name) - __string( status, status ) - __string( type, type ) - __field( int, id ) - ), - - TP_fast_assign( - __assign_str(name, codec->component.name); - __assign_str(status, status); - __assign_str(type, type); - __entry->id = codec->component.id; - ), - - TP_printk("codec=%s.%d type=%s status=%s", __get_str(name), - (int)__entry->id, __get_str(type), __get_str(status)) -); - #endif /* _TRACE_ASOC_H */ /* This part must be outside protection */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 6dab817..07f4335 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -15,56 +15,6 @@ #include #include -#include - -static bool snd_soc_set_cache_val(void *base, unsigned int idx, - unsigned int val, unsigned int word_size) -{ - switch (word_size) { - case 1: { - u8 *cache = base; - if (cache[idx] == val) - return true; - cache[idx] = val; - break; - } - case 2: { - u16 *cache = base; - if (cache[idx] == val) - return true; - cache[idx] = val; - break; - } - default: - WARN(1, "Invalid word_size %d\n", word_size); - break; - } - return false; -} - -static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, - unsigned int word_size) -{ - if (!base) - return -1; - - switch (word_size) { - case 1: { - const u8 *cache = base; - return cache[idx]; - } - case 2: { - const u16 *cache = base; - return cache[idx]; - } - default: - WARN(1, "Invalid word_size %d\n", word_size); - break; - } - /* unreachable */ - return -1; -} - int snd_soc_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv = codec->driver; @@ -75,8 +25,6 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) if (!reg_size) return 0; - mutex_init(&codec->cache_rw_mutex); - dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", codec->component.name); @@ -103,103 +51,3 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) codec->reg_cache = NULL; return 0; } - -/** - * snd_soc_cache_read: Fetch the value of a given register from the cache. - * - * @codec: CODEC to configure. - * @reg: The register index. - * @value: The value to be returned. - */ -int snd_soc_cache_read(struct snd_soc_codec *codec, - unsigned int reg, unsigned int *value) -{ - if (!value) - return -EINVAL; - - mutex_lock(&codec->cache_rw_mutex); - if (!ZERO_OR_NULL_PTR(codec->reg_cache)) - *value = snd_soc_get_cache_val(codec->reg_cache, reg, - codec->driver->reg_word_size); - mutex_unlock(&codec->cache_rw_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_cache_read); - -/** - * snd_soc_cache_write: Set the value of a given register in the cache. - * - * @codec: CODEC to configure. - * @reg: The register index. - * @value: The new register value. - */ -int snd_soc_cache_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - mutex_lock(&codec->cache_rw_mutex); - if (!ZERO_OR_NULL_PTR(codec->reg_cache)) - snd_soc_set_cache_val(codec->reg_cache, reg, value, - codec->driver->reg_word_size); - mutex_unlock(&codec->cache_rw_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_cache_write); - -static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) -{ - int i; - int ret; - const struct snd_soc_codec_driver *codec_drv; - unsigned int val; - - codec_drv = codec->driver; - for (i = 0; i < codec_drv->reg_cache_size; ++i) { - ret = snd_soc_cache_read(codec, i, &val); - if (ret) - return ret; - if (codec_drv->reg_cache_default) - if (snd_soc_get_cache_val(codec_drv->reg_cache_default, - i, codec_drv->reg_word_size) == val) - continue; - - ret = snd_soc_write(codec, i, val); - if (ret) - return ret; - dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", - i, val); - } - return 0; -} - -/** - * snd_soc_cache_sync: Sync the register cache with the hardware. - * - * @codec: CODEC to configure. - * - * Any registers that should not be synced should be marked as - * volatile. In general drivers can choose not to use the provided - * syncing functionality if they so require. - */ -int snd_soc_cache_sync(struct snd_soc_codec *codec) -{ - const char *name = "flat"; - int ret; - - if (codec->component.regmap) - return regcache_sync(codec->component.regmap); - - if (!codec->cache_sync) - return 0; - - dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", - codec->component.name); - trace_snd_soc_cache_sync(codec, name, "start"); - ret = snd_soc_flat_cache_sync(codec); - if (!ret) - codec->cache_sync = 0; - trace_snd_soc_cache_sync(codec, name, "end"); - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_cache_sync); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a2..4e0e32b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -309,9 +309,6 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root, - &codec->cache_sync); - codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->component.debugfs_root, codec, &codec_reg_fops); @@ -656,7 +653,6 @@ int snd_soc_suspend(struct device *dev) if (codec->driver->suspend) codec->driver->suspend(codec); codec->suspended = 1; - codec->cache_sync = 1; if (codec->component.regmap) regcache_mark_dirty(codec->component.regmap); /* deactivate pins to sleep state */ -- cgit v0.10.2 From fbace43e8817113475ebda00e28593baa436a131 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Sat, 8 Nov 2014 14:40:17 +0100 Subject: ASoC: tfa9879: New driver for NXP Semiconductors TFA9879 amplifier. Signed-off-by: Peter Rosin Signed-off-by: Mark Brown diff --git a/MAINTAINERS b/MAINTAINERS index a20df9b..28a3329 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6565,6 +6565,12 @@ S: Supported F: drivers/gpu/drm/i2c/tda998x_drv.c F: include/drm/i2c/tda998x.h +NXP TFA9879 DRIVER +M: Peter Rosin +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: sound/soc/codecs/tfa9879* + OMAP SUPPORT M: Tony Lindgren L: linux-omap@vger.kernel.org diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..99b34a9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -101,6 +101,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C + select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER select SND_SOC_TLV320AIC26 if SPI_MASTER @@ -577,6 +578,10 @@ config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C +config SND_SOC_TFA9879 + tristate "NXP Semiconductors TFA9879 amplifier" + depends on I2C + config SND_SOC_TLV320AIC23 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5dce451..ccfa2ab 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -101,6 +101,7 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o +snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o @@ -274,6 +275,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c new file mode 100644 index 0000000..16f1b71 --- /dev/null +++ b/sound/soc/codecs/tfa9879.c @@ -0,0 +1,328 @@ +/* + * tfa9879.c -- driver for NXP Semiconductors TFA9879 + * + * Copyright (C) 2014 Axentia Technologies AB + * Author: Peter Rosin + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "tfa9879.h" + +struct tfa9879_priv { + struct regmap *regmap; + int lsb_justified; +}; + +static int tfa9879_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec); + int fs; + int i2s_set = 0; + + switch (params_rate(params)) { + case 8000: + fs = TFA9879_I2S_FS_8000; + break; + case 11025: + fs = TFA9879_I2S_FS_11025; + break; + case 12000: + fs = TFA9879_I2S_FS_12000; + break; + case 16000: + fs = TFA9879_I2S_FS_16000; + break; + case 22050: + fs = TFA9879_I2S_FS_22050; + break; + case 24000: + fs = TFA9879_I2S_FS_24000; + break; + case 32000: + fs = TFA9879_I2S_FS_32000; + break; + case 44100: + fs = TFA9879_I2S_FS_44100; + break; + case 48000: + fs = TFA9879_I2S_FS_48000; + break; + case 64000: + fs = TFA9879_I2S_FS_64000; + break; + case 88200: + fs = TFA9879_I2S_FS_88200; + break; + case 96000: + fs = TFA9879_I2S_FS_96000; + break; + default: + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + i2s_set = TFA9879_I2S_SET_LSB_J_16; + break; + case 24: + i2s_set = TFA9879_I2S_SET_LSB_J_24; + break; + default: + return -EINVAL; + } + + if (tfa9879->lsb_justified) + snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1, + TFA9879_I2S_SET_MASK, + i2s_set << TFA9879_I2S_SET_SHIFT); + + snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1, + TFA9879_I2S_FS_MASK, + fs << TFA9879_I2S_FS_SHIFT); + return 0; +} + +static int tfa9879_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + snd_soc_update_bits(codec, TFA9879_MISC_CONTROL, + TFA9879_S_MUTE_MASK, + !!mute << TFA9879_S_MUTE_SHIFT); + + return 0; +} + +static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec); + int i2s_set; + int sck_pol; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + sck_pol = TFA9879_SCK_POL_NORMAL; + break; + case SND_SOC_DAIFMT_IB_NF: + sck_pol = TFA9879_SCK_POL_INVERSE; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + tfa9879->lsb_justified = 0; + i2s_set = TFA9879_I2S_SET_I2S_24; + break; + case SND_SOC_DAIFMT_LEFT_J: + tfa9879->lsb_justified = 0; + i2s_set = TFA9879_I2S_SET_MSB_J_24; + break; + case SND_SOC_DAIFMT_RIGHT_J: + tfa9879->lsb_justified = 1; + i2s_set = TFA9879_I2S_SET_LSB_J_24; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1, + TFA9879_SCK_POL_MASK, + sck_pol << TFA9879_SCK_POL_SHIFT); + snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1, + TFA9879_I2S_SET_MASK, + i2s_set << TFA9879_I2S_SET_SHIFT); + return 0; +} + +static struct reg_default tfa9879_regs[] = { + { TFA9879_DEVICE_CONTROL, 0x0000 }, /* 0x00 */ + { TFA9879_SERIAL_INTERFACE_1, 0x0a18 }, /* 0x01 */ + { TFA9879_PCM_IOM2_FORMAT_1, 0x0007 }, /* 0x02 */ + { TFA9879_SERIAL_INTERFACE_2, 0x0a18 }, /* 0x03 */ + { TFA9879_PCM_IOM2_FORMAT_2, 0x0007 }, /* 0x04 */ + { TFA9879_EQUALIZER_A1, 0x59dd }, /* 0x05 */ + { TFA9879_EQUALIZER_A2, 0xc63e }, /* 0x06 */ + { TFA9879_EQUALIZER_B1, 0x651a }, /* 0x07 */ + { TFA9879_EQUALIZER_B2, 0xe53e }, /* 0x08 */ + { TFA9879_EQUALIZER_C1, 0x4616 }, /* 0x09 */ + { TFA9879_EQUALIZER_C2, 0xd33e }, /* 0x0a */ + { TFA9879_EQUALIZER_D1, 0x4df3 }, /* 0x0b */ + { TFA9879_EQUALIZER_D2, 0xea3e }, /* 0x0c */ + { TFA9879_EQUALIZER_E1, 0x5ee0 }, /* 0x0d */ + { TFA9879_EQUALIZER_E2, 0xf93e }, /* 0x0e */ + { TFA9879_BYPASS_CONTROL, 0x0093 }, /* 0x0f */ + { TFA9879_DYNAMIC_RANGE_COMPR, 0x92ba }, /* 0x10 */ + { TFA9879_BASS_TREBLE, 0x12a5 }, /* 0x11 */ + { TFA9879_HIGH_PASS_FILTER, 0x0004 }, /* 0x12 */ + { TFA9879_VOLUME_CONTROL, 0x10bd }, /* 0x13 */ + { TFA9879_MISC_CONTROL, 0x0000 }, /* 0x14 */ +}; + +static bool tfa9879_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TFA9879_MISC_STATUS; +} + +static const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1); +static const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0); +static const char * const tb_freq_text[] = { + "Low", "Mid", "High" +}; +static const struct soc_enum treble_freq_enum = + SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_TRBLE_SHIFT, + ARRAY_SIZE(tb_freq_text), tb_freq_text); +static const struct soc_enum bass_freq_enum = + SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_BASS_SHIFT, + ARRAY_SIZE(tb_freq_text), tb_freq_text); + +static const struct snd_kcontrol_new tfa9879_controls[] = { + SOC_SINGLE_TLV("PCM Playback Volume", TFA9879_VOLUME_CONTROL, + TFA9879_VOL_SHIFT, 0xbd, 1, volume_tlv), + SOC_SINGLE_TLV("Treble Volume", TFA9879_BASS_TREBLE, + TFA9879_G_TRBLE_SHIFT, 18, 0, tb_gain_tlv), + SOC_SINGLE_TLV("Bass Volume", TFA9879_BASS_TREBLE, + TFA9879_G_BASS_SHIFT, 18, 0, tb_gain_tlv), + SOC_ENUM("Treble Corner Freq", treble_freq_enum), + SOC_ENUM("Bass Corner Freq", bass_freq_enum), +}; + +static const struct snd_soc_dapm_widget tfa9879_dapm_widgets[] = { +SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DAC", NULL, TFA9879_DEVICE_CONTROL, TFA9879_OPMODE_SHIFT, 0), +SND_SOC_DAPM_OUTPUT("LINEOUT"), +SND_SOC_DAPM_SUPPLY("POWER", TFA9879_DEVICE_CONTROL, TFA9879_POWERUP_SHIFT, 0, + NULL, 0), +}; + +static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = { + { "DAC", NULL, "AIFINL" }, + { "DAC", NULL, "AIFINR" }, + + { "LINEOUT", NULL, "DAC" }, + + { "DAC", NULL, "POWER" }, +}; + +static const struct snd_soc_codec_driver tfa9879_codec = { + .controls = tfa9879_controls, + .num_controls = ARRAY_SIZE(tfa9879_controls), + + .dapm_widgets = tfa9879_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets), + .dapm_routes = tfa9879_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes), +}; + +static const struct regmap_config tfa9879_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .volatile_reg = tfa9879_volatile_reg, + .max_register = TFA9879_MISC_STATUS, + .reg_defaults = tfa9879_regs, + .num_reg_defaults = ARRAY_SIZE(tfa9879_regs), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct snd_soc_dai_ops tfa9879_dai_ops = { + .hw_params = tfa9879_hw_params, + .digital_mute = tfa9879_digital_mute, + .set_fmt = tfa9879_set_fmt, +}; + +#define TFA9879_RATES SNDRV_PCM_RATE_8000_96000 + +#define TFA9879_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver tfa9879_dai = { + .name = "tfa9879-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = TFA9879_RATES, + .formats = TFA9879_FORMATS, }, + .ops = &tfa9879_dai_ops, +}; + +static int tfa9879_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tfa9879_priv *tfa9879; + int i; + + tfa9879 = devm_kzalloc(&i2c->dev, sizeof(*tfa9879), GFP_KERNEL); + if (IS_ERR(tfa9879)) + return PTR_ERR(tfa9879); + + i2c_set_clientdata(i2c, tfa9879); + + tfa9879->regmap = devm_regmap_init_i2c(i2c, &tfa9879_regmap); + if (IS_ERR(tfa9879->regmap)) + return PTR_ERR(tfa9879->regmap); + + /* Ensure the device is in reset state */ + for (i = 0; i < ARRAY_SIZE(tfa9879_regs); i++) + regmap_write(tfa9879->regmap, + tfa9879_regs[i].reg, tfa9879_regs[i].def); + + return snd_soc_register_codec(&i2c->dev, &tfa9879_codec, + &tfa9879_dai, 1); +} + +static int tfa9879_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + + return 0; +} + +static const struct i2c_device_id tfa9879_i2c_id[] = { + { "tfa9879", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id); + +static struct i2c_driver tfa9879_i2c_driver = { + .driver = { + .name = "tfa9879", + .owner = THIS_MODULE, + }, + .probe = tfa9879_i2c_probe, + .remove = tfa9879_i2c_remove, + .id_table = tfa9879_i2c_id, +}; + +module_i2c_driver(tfa9879_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TFA9879 driver"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tfa9879.h b/sound/soc/codecs/tfa9879.h new file mode 100644 index 0000000..3408c90 --- /dev/null +++ b/sound/soc/codecs/tfa9879.h @@ -0,0 +1,202 @@ +/* + * tfa9879.h -- driver for NXP Semiconductors TFA9879 + * + * Copyright (C) 2014 Axentia Technologies AB + * Author: Peter Rosin + * + * 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. + * + */ + +#ifndef _TFA9879_H +#define _TFA9879_H + +#define TFA9879_DEVICE_CONTROL 0x00 +#define TFA9879_SERIAL_INTERFACE_1 0x01 +#define TFA9879_PCM_IOM2_FORMAT_1 0x02 +#define TFA9879_SERIAL_INTERFACE_2 0x03 +#define TFA9879_PCM_IOM2_FORMAT_2 0x04 +#define TFA9879_EQUALIZER_A1 0x05 +#define TFA9879_EQUALIZER_A2 0x06 +#define TFA9879_EQUALIZER_B1 0x07 +#define TFA9879_EQUALIZER_B2 0x08 +#define TFA9879_EQUALIZER_C1 0x09 +#define TFA9879_EQUALIZER_C2 0x0a +#define TFA9879_EQUALIZER_D1 0x0b +#define TFA9879_EQUALIZER_D2 0x0c +#define TFA9879_EQUALIZER_E1 0x0d +#define TFA9879_EQUALIZER_E2 0x0e +#define TFA9879_BYPASS_CONTROL 0x0f +#define TFA9879_DYNAMIC_RANGE_COMPR 0x10 +#define TFA9879_BASS_TREBLE 0x11 +#define TFA9879_HIGH_PASS_FILTER 0x12 +#define TFA9879_VOLUME_CONTROL 0x13 +#define TFA9879_MISC_CONTROL 0x14 +#define TFA9879_MISC_STATUS 0x15 + +/* TFA9879_DEVICE_CONTROL */ +#define TFA9879_INPUT_SEL_MASK 0x0010 +#define TFA9879_INPUT_SEL_SHIFT 4 +#define TFA9879_OPMODE_MASK 0x0008 +#define TFA9879_OPMODE_SHIFT 3 +#define TFA9879_RESET_MASK 0x0002 +#define TFA9879_RESET_SHIFT 1 +#define TFA9879_POWERUP_MASK 0x0001 +#define TFA9879_POWERUP_SHIFT 0 + +/* TFA9879_SERIAL_INTERFACE */ +#define TFA9879_MONO_SEL_MASK 0x0c00 +#define TFA9879_MONO_SEL_SHIFT 10 +#define TFA9879_MONO_SEL_LEFT 0 +#define TFA9879_MONO_SEL_RIGHT 1 +#define TFA9879_MONO_SEL_BOTH 2 +#define TFA9879_I2S_FS_MASK 0x03c0 +#define TFA9879_I2S_FS_SHIFT 6 +#define TFA9879_I2S_FS_8000 0 +#define TFA9879_I2S_FS_11025 1 +#define TFA9879_I2S_FS_12000 2 +#define TFA9879_I2S_FS_16000 3 +#define TFA9879_I2S_FS_22050 4 +#define TFA9879_I2S_FS_24000 5 +#define TFA9879_I2S_FS_32000 6 +#define TFA9879_I2S_FS_44100 7 +#define TFA9879_I2S_FS_48000 8 +#define TFA9879_I2S_FS_64000 9 +#define TFA9879_I2S_FS_88200 10 +#define TFA9879_I2S_FS_96000 11 +#define TFA9879_I2S_SET_MASK 0x0038 +#define TFA9879_I2S_SET_SHIFT 3 +#define TFA9879_I2S_SET_MSB_J_24 2 +#define TFA9879_I2S_SET_I2S_24 3 +#define TFA9879_I2S_SET_LSB_J_16 4 +#define TFA9879_I2S_SET_LSB_J_18 5 +#define TFA9879_I2S_SET_LSB_J_20 6 +#define TFA9879_I2S_SET_LSB_J_24 7 +#define TFA9879_SCK_POL_MASK 0x0004 +#define TFA9879_SCK_POL_SHIFT 2 +#define TFA9879_SCK_POL_NORMAL 0 +#define TFA9879_SCK_POL_INVERSE 1 +#define TFA9879_I_MODE_MASK 0x0003 +#define TFA9879_I_MODE_SHIFT 0 +#define TFA9879_I_MODE_I2S 0 +#define TFA9879_I_MODE_PCM_IOM2_SHORT 1 +#define TFA9879_I_MODE_PCM_IOM2_LONG 2 + +/* TFA9879_PCM_IOM2_FORMAT */ +#define TFA9879_PCM_FS_MASK 0x0800 +#define TFA9879_PCM_FS_SHIFT 11 +#define TFA9879_A_LAW_MASK 0x0400 +#define TFA9879_A_LAW_SHIFT 10 +#define TFA9879_PCM_COMP_MASK 0x0200 +#define TFA9879_PCM_COMP_SHIFT 9 +#define TFA9879_PCM_DL_MASK 0x0100 +#define TFA9879_PCM_DL_SHIFT 8 +#define TFA9879_D1_SLOT_MASK 0x00f0 +#define TFA9879_D1_SLOT_SHIFT 4 +#define TFA9879_D2_SLOT_MASK 0x000f +#define TFA9879_D2_SLOT_SHIFT 0 + +/* TFA9879_EQUALIZER_X1 */ +#define TFA9879_T1_MASK 0x8000 +#define TFA9879_T1_SHIFT 15 +#define TFA9879_K1M_MASK 0x7ff0 +#define TFA9879_K1M_SHIFT 4 +#define TFA9879_K1E_MASK 0x000f +#define TFA9879_K1E_SHIFT 0 + +/* TFA9879_EQUALIZER_X2 */ +#define TFA9879_T2_MASK 0x8000 +#define TFA9879_T2_SHIFT 15 +#define TFA9879_K2M_MASK 0x7800 +#define TFA9879_K2M_SHIFT 11 +#define TFA9879_K2E_MASK 0x0700 +#define TFA9879_K2E_SHIFT 8 +#define TFA9879_K0_MASK 0x00fe +#define TFA9879_K0_SHIFT 1 +#define TFA9879_S_MASK 0x0001 +#define TFA9879_S_SHIFT 0 + +/* TFA9879_BYPASS_CONTROL */ +#define TFA9879_L_OCP_MASK 0x00c0 +#define TFA9879_L_OCP_SHIFT 6 +#define TFA9879_L_OTP_MASK 0x0030 +#define TFA9879_L_OTP_SHIFT 4 +#define TFA9879_CLIPCTRL_MASK 0x0008 +#define TFA9879_CLIPCTRL_SHIFT 3 +#define TFA9879_HPF_BP_MASK 0x0004 +#define TFA9879_HPF_BP_SHIFT 2 +#define TFA9879_DRC_BP_MASK 0x0002 +#define TFA9879_DRC_BP_SHIFT 1 +#define TFA9879_EQ_BP_MASK 0x0001 +#define TFA9879_EQ_BP_SHIFT 0 + +/* TFA9879_DYNAMIC_RANGE_COMPR */ +#define TFA9879_AT_LVL_MASK 0xf000 +#define TFA9879_AT_LVL_SHIFT 12 +#define TFA9879_AT_RATE_MASK 0x0f00 +#define TFA9879_AT_RATE_SHIFT 8 +#define TFA9879_RL_LVL_MASK 0x00f0 +#define TFA9879_RL_LVL_SHIFT 4 +#define TFA9879_RL_RATE_MASK 0x000f +#define TFA9879_RL_RATE_SHIFT 0 + +/* TFA9879_BASS_TREBLE */ +#define TFA9879_G_TRBLE_MASK 0x3e00 +#define TFA9879_G_TRBLE_SHIFT 9 +#define TFA9879_F_TRBLE_MASK 0x0180 +#define TFA9879_F_TRBLE_SHIFT 7 +#define TFA9879_G_BASS_MASK 0x007c +#define TFA9879_G_BASS_SHIFT 2 +#define TFA9879_F_BASS_MASK 0x0003 +#define TFA9879_F_BASS_SHIFT 0 + +/* TFA9879_HIGH_PASS_FILTER */ +#define TFA9879_HP_CTRL_MASK 0x00ff +#define TFA9879_HP_CTRL_SHIFT 0 + +/* TFA9879_VOLUME_CONTROL */ +#define TFA9879_ZR_CRSS_MASK 0x1000 +#define TFA9879_ZR_CRSS_SHIFT 12 +#define TFA9879_VOL_MASK 0x00ff +#define TFA9879_VOL_SHIFT 0 + +/* TFA9879_MISC_CONTROL */ +#define TFA9879_DE_PHAS_MASK 0x0c00 +#define TFA9879_DE_PHAS_SHIFT 10 +#define TFA9879_H_MUTE_MASK 0x0200 +#define TFA9879_H_MUTE_SHIFT 9 +#define TFA9879_S_MUTE_MASK 0x0100 +#define TFA9879_S_MUTE_SHIFT 8 +#define TFA9879_P_LIM_MASK 0x00ff +#define TFA9879_P_LIM_SHIFT 0 + +/* TFA9879_MISC_STATUS */ +#define TFA9879_PS_MASK 0x4000 +#define TFA9879_PS_SHIFT 14 +#define TFA9879_PORA_MASK 0x2000 +#define TFA9879_PORA_SHIFT 13 +#define TFA9879_AMP_MASK 0x0600 +#define TFA9879_AMP_SHIFT 9 +#define TFA9879_IBP_2_MASK 0x0100 +#define TFA9879_IBP_2_SHIFT 8 +#define TFA9879_OFP_2_MASK 0x0080 +#define TFA9879_OFP_2_SHIFT 7 +#define TFA9879_UFP_2_MASK 0x0040 +#define TFA9879_UFP_2_SHIFT 6 +#define TFA9879_IBP_1_MASK 0x0020 +#define TFA9879_IBP_1_SHIFT 5 +#define TFA9879_OFP_1_MASK 0x0010 +#define TFA9879_OFP_1_SHIFT 4 +#define TFA9879_UFP_1_MASK 0x0008 +#define TFA9879_UFP_1_SHIFT 3 +#define TFA9879_OCPOKA_MASK 0x0004 +#define TFA9879_OCPOKA_SHIFT 2 +#define TFA9879_OCPOKB_MASK 0x0002 +#define TFA9879_OCPOKB_SHIFT 1 +#define TFA9879_OTPOK_MASK 0x0001 +#define TFA9879_OTPOK_SHIFT 0 + +#endif -- cgit v0.10.2 From 368494093354ac613a80c2e1d77602aa12473cf0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Nov 2014 12:27:33 +0200 Subject: ASoC: tlv320aic3x: Add TDM support TDM support is achieved using DSP transfer mode and setting a programmable offset which specifies where data begins with respect to the frame sync. It requires 256-clock mode if CODEC is master (not currently supported in the driver). No additional dependency if CODEC is slave. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index f7c2a57..8770e28 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -78,6 +78,8 @@ struct aic3x_priv { struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES]; struct aic3x_setup_data *setup; unsigned int sysclk; + unsigned int dai_fmt; + unsigned int tdm_delay; struct list_head list; int master; int gpio_reset; @@ -1009,6 +1011,25 @@ found: return 0; } +static int aic3x_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + int delay = 0; + + /* TDM slot selection only valid in DSP_A/_B mode */ + if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A) + delay += (aic3x->tdm_delay + 1); + else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B) + delay += aic3x->tdm_delay; + + /* Configure data delay */ + snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, aic3x->tdm_delay); + + return 0; +} + static int aic3x_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; @@ -1048,7 +1069,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_codec *codec = codec_dai->codec; struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); u8 iface_areg, iface_breg; - int delay = 0; iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f; iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f; @@ -1076,7 +1096,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): break; case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): - delay = 1; case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): iface_breg |= (0x01 << 6); break; @@ -1090,10 +1109,45 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } + aic3x->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + /* set iface */ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg); snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg); - snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay); + + return 0; +} + +static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + unsigned int lsb; + + if (tx_mask != rx_mask) { + dev_err(codec->dev, "tx and rx masks must be symmetric\n"); + return -EINVAL; + } + + if (unlikely(!tx_mask)) { + dev_err(codec->dev, "tx and rx masks need to be non 0\n"); + return -EINVAL; + } + + /* TDM based on DSP mode requires slots to be adjacent */ + lsb = __ffs(tx_mask); + if ((lsb + 1) != __fls(tx_mask)) { + dev_err(codec->dev, "Invalid mask, slots must be adjacent\n"); + return -EINVAL; + } + + aic3x->tdm_delay = lsb * slot_width; + + /* DOUT in high-impedance on inactive bit clocks */ + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, + DOUT_TRISTATE, DOUT_TRISTATE); return 0; } @@ -1212,9 +1266,11 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, static const struct snd_soc_dai_ops aic3x_dai_ops = { .hw_params = aic3x_hw_params, + .prepare = aic3x_prepare, .digital_mute = aic3x_mute, .set_sysclk = aic3x_set_dai_sysclk, .set_fmt = aic3x_set_dai_fmt, + .set_tdm_slot = aic3x_set_dai_tdm_slot, }; static struct snd_soc_dai_driver aic3x_dai = { diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index e521ac3..89fa692 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -169,6 +169,7 @@ /* Audio serial data interface control register A bits */ #define BIT_CLK_MASTER 0x80 #define WORD_CLK_MASTER 0x40 +#define DOUT_TRISTATE 0x20 /* Codec Datapath setup register 7 */ #define FSREF_44100 (1 << 7) -- cgit v0.10.2 From ff7c532c3ae570981a46663d5810dda913d468d9 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sun, 9 Nov 2014 20:41:51 +0100 Subject: ASoC: omap: enable sound support on n900 on devicetree-based boot With device tree, it is possible (and encouraged) to build N900 kernels without CONFIG_MACH_NOKIA_RX51. Update config file to enable the driver build in this case. This makes sound work on my n900 under 3.18-rc1. Signed-off-by: Pavel Machek Signed-off-by: Mark Brown diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index d44463a..2738b19 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -25,15 +25,15 @@ config SND_OMAP_SOC_N810 Say Y if you want to add support for SoC audio on Nokia N810. config SND_OMAP_SOC_RX51 - tristate "SoC Audio support for Nokia RX-51" - depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST) && I2C + tristate "SoC Audio support for Nokia N900 (RX-51)" + depends on SND_OMAP_SOC && ARM && I2C select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X select SND_SOC_TPA6130A2 depends on GPIOLIB help - Say Y if you want to add support for SoC audio on Nokia RX-51 - hardware. This is also known as Nokia N900 product. + Say Y if you want to add support for SoC audio on Nokia N900 + cellphone. config SND_OMAP_SOC_AMS_DELTA tristate "SoC Audio support for Amstrad E3 (Delta) videophone" -- cgit v0.10.2 From 7771ef3286711a121b763c3620c4619f51b2acfd Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Sun, 9 Nov 2014 18:15:14 +0530 Subject: ASoC: davinvi-mcasp: Balance pm_runtime_enable() on probe failure If probe fails then we need to call pm_runtime_disable() to balance out the previous pm_runtime_enable() call. Signed-off-by: Anil Kumar Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 002351f..57f606e 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1225,6 +1225,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (IS_ERR_VALUE(ret)) { dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); + pm_runtime_disable(&pdev->dev); return ret; } -- cgit v0.10.2 From 30cc4faf703955cd5cd07da489bd817ae43e3fec Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 9 Nov 2014 20:00:30 -0800 Subject: ASoC: rsnd: tidyup debug message format and timing Current Renesas R-Car sound driver debug message is using random format (ex "ssi0: xxx" / "SSI0 xxx" / "ssi[0]: xxx") and confusable timing ("xxx probe failed" and "xxx probed" are shown in same time) This patch fixes these Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 1922ec5..5205618 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -409,7 +409,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - dev_dbg(dev, "%s [%d] %s\n", \ + dev_dbg(dev, "%s[%d] %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ (mod)->ops->func(mod, rdai); \ }) diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 8504f6b..956b84e 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -85,7 +85,8 @@ static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod)); + dev_dbg(dev, "%s[%d] (Gen2) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 61dee68..a0fed66 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -78,7 +78,7 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; - dev_dbg(dev, "r %s(%d) - %4d : %08x\n", + dev_dbg(dev, "r %s[%d] - %4d : %08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); @@ -96,7 +96,7 @@ void rsnd_write(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - dev_dbg(dev, "w %s(%d) - %4d : %08x\n", + dev_dbg(dev, "w %s[%d] - %4d : %08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data); regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); @@ -111,7 +111,7 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - dev_dbg(dev, "b %s(%d) - %4d : %08x/%08x\n", + dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask); regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 9183e01..4679501 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -438,7 +438,8 @@ static int rsnd_src_probe_gen1(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod)); + dev_dbg(dev, "%s[%d] (Gen1) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; } @@ -578,9 +579,11 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, rsnd_info_is_playback(priv, src), src->info->dma_id); if (ret < 0) - dev_err(dev, "SRC DMA failed\n"); - - dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod)); + dev_err(dev, "%s[%d] (Gen2) failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + else + dev_dbg(dev, "%s[%d] (Gen2) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 34e8400..cae08b7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -159,7 +159,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(j); - dev_dbg(dev, "ssi%d outputs %u Hz\n", + dev_dbg(dev, "%s[%d] outputs %u Hz\n", + rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod), rate); return 0; @@ -206,7 +207,8 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, ssi->usrcnt++; - dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod)); + dev_dbg(dev, "%s[%d] hw started\n", + rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); } static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, @@ -249,7 +251,8 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, clk_disable_unprepare(ssi->clk); } - dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod)); + dev_dbg(dev, "%s[%d] hw stopped\n", + rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); } /* @@ -385,9 +388,11 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, IRQF_SHARED, dev_name(dev), ssi); if (ret) - dev_err(dev, "SSI request interrupt failed\n"); - - dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod)); + dev_err(dev, "%s[%d] (PIO) request interrupt failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + else + dev_dbg(dev, "%s[%d] (PIO) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -448,9 +453,11 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, dma_id); if (ret < 0) - dev_err(dev, "SSI DMA failed\n"); - - dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod)); + dev_err(dev, "%s[%d] (DMA) is failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + else + dev_dbg(dev, "%s[%d] (DMA) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } -- cgit v0.10.2 From d3a768233243b5892a9c74b85896b9e8c017b259 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 9 Nov 2014 20:00:58 -0800 Subject: ASoC: rsnd: fallback to PIO mode if DMA mode was failed Current Renesas R-Car sound driver probe will be failed if it try to use DMA mode and it couldn't use for some reasons. But PIO mode works even though in such case. This patch try to fallback to PIO mode if DMA mode probing was failed. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 5205618..110b99d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -349,7 +349,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, dma_name); if (!dma->chan) { dev_err(dev, "can't get dma channel\n"); - return -EIO; + goto rsnd_dma_channel_err; } ret = dmaengine_slave_config(dma->chan, &cfg); @@ -363,8 +363,15 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, rsnd_dma_init_err: rsnd_dma_quit(priv, dma); +rsnd_dma_channel_err: - return ret; + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_dma_remove() + * rsnd_rdai_continuance_probe() + */ + return -EAGAIN; } void rsnd_dma_quit(struct rsnd_priv *priv, @@ -456,6 +463,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, return 0; } +static void rsnd_dai_disconnect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + mod->io = NULL; + io->mod[mod->type] = NULL; +} + int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) { int id = rdai - priv->rdai; @@ -686,6 +700,20 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { ret; \ }) +#define rsnd_path_break(priv, io, type) \ +{ \ + struct rsnd_mod *mod; \ + int id = -1; \ + \ + if (rsnd_is_enable_path(io, type)) { \ + id = rsnd_info_id(priv, io, type); \ + if (id >= 0) { \ + mod = rsnd_##type##_mod_get(priv, id); \ + rsnd_dai_disconnect(mod, io); \ + } \ + } \ +} + static int rsnd_path_init(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -977,6 +1005,44 @@ static const struct snd_soc_component_driver rsnd_soc_component = { .name = "rsnd", }; +static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + int is_play) +{ + struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture; + int ret; + + ret = rsnd_dai_call(probe, io, rdai); + if (ret == -EAGAIN) { + /* + * Fallback to PIO mode + */ + + /* + * call "remove" for SSI/SRC/DVC + * SSI will be switch to PIO mode if it was DMA mode + * see + * rsnd_dma_init() + * rsnd_ssi_dma_remove() + */ + rsnd_dai_call(remove, io, rdai); + + /* + * remove SRC/DVC from DAI, + */ + rsnd_path_break(priv, io, src); + rsnd_path_break(priv, io, dvc); + + /* + * retry to "probe". + * DAI has SSI which is PIO mode only now. + */ + ret = rsnd_dai_call(probe, io, rdai); + } + + return ret; +} + /* * rsnd probe */ @@ -1038,11 +1104,11 @@ static int rsnd_probe(struct platform_device *pdev) } for_each_rsnd_dai(rdai, priv, i) { - ret = rsnd_dai_call(probe, &rdai->playback, rdai); + ret = rsnd_rdai_continuance_probe(priv, rdai, 1); if (ret) goto exit_snd_probe; - ret = rsnd_dai_call(probe, &rdai->capture, rdai); + ret = rsnd_rdai_continuance_probe(priv, rdai, 0); if (ret) goto exit_snd_probe; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index cae08b7..346d3dc 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -465,8 +465,23 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); + /* + * fallback to PIO + * + * SSI .probe might be called again. + * see + * rsnd_rdai_continuance_probe() + */ + mod->ops = &rsnd_ssi_pio_ops; + + dev_info(dev, "%s[%d] fallback to PIO mode\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + return 0; } -- cgit v0.10.2 From d75249f54577d489d1642a246d3702416daa68f9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Nov 2014 12:32:18 +0200 Subject: ASoC: davinci-mcasp: Symmetric sample bits for IIS mode In IIS mode the tx and rx configuration is symmetric, the BCLK and FSYNC is shared. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 57f606e..80c54ed 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -961,6 +961,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { }, .ops = &davinci_mcasp_dai_ops, + .symmetric_samplebits = 1, }, { .name = "davinci-mcasp.1", -- cgit v0.10.2 From d742b925244ce91f16d380befdca473e4536359b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Nov 2014 12:32:19 +0200 Subject: ASoC: davinci-mcasp: Fix rx format when more bclk is used on the bus When the bus is configured to have more BCLK then the data type demands we need to use the rotation to move the data to correct place. Reported-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 80c54ed..ea3ad74 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -490,8 +490,17 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, * both left and right channels), so it has to be divided by number of * tdm-slots (for I2S - divided by 2). */ - if (mcasp->bclk_lrclk_ratio) - word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; + if (mcasp->bclk_lrclk_ratio) { + u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; + + /* + * When we have more bclk then it is needed for the data, we + * need to use the rotation to move the received samples to have + * correct alignment. + */ + rx_rotate = (slot_length - word_length) / 4; + word_length = slot_length; + } /* mapping of the XSSZ bit-field as described in the datasheet */ fmt = (word_length >> 1) - 1; -- cgit v0.10.2 From 1a5923da4e2e6997f03545a2b88f2cd2dea1a5c2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Nov 2014 12:32:15 +0200 Subject: ASoC: davinci-mcasp: Validate tdm_slots parameter at probe time Instead of validating the tdm_slots parameter every time at hw_params we can do it once during probe. If the parameter is not valid (<2 or >32) print an error and fix it up. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 6b1bfd9..10c2647 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -632,13 +632,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream) u32 mask = 0; u32 busel = 0; - if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) { - dev_err(mcasp->dev, "tdm slot %d not supported\n", - mcasp->tdm_slots); - return -EINVAL; - } - - active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots; + active_slots = mcasp->tdm_slots; for (i = 0; i < active_slots; i++) mask |= (1 << i); @@ -650,12 +644,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream) mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF)); + FSXMOD(active_slots), FSXMOD(0x1FF)); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF)); + FSRMOD(active_slots), FSRMOD(0x1FF)); return 0; } @@ -1237,7 +1231,21 @@ static int davinci_mcasp_probe(struct platform_device *pdev) } mcasp->op_mode = pdata->op_mode; - mcasp->tdm_slots = pdata->tdm_slots; + /* sanity check for tdm slots parameter */ + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { + if (pdata->tdm_slots < 2) { + dev_err(&pdev->dev, "invalid tdm slots: %d\n", + pdata->tdm_slots); + mcasp->tdm_slots = 2; + } else if (pdata->tdm_slots > 32) { + dev_err(&pdev->dev, "invalid tdm slots: %d\n", + pdata->tdm_slots); + mcasp->tdm_slots = 32; + } else { + mcasp->tdm_slots = pdata->tdm_slots; + } + } + mcasp->num_serializer = pdata->num_serializer; #ifdef CONFIG_PM_SLEEP mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev, -- cgit v0.10.2 From 11277833ce8018ddd18925d2f85037bf02dcba63 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Nov 2014 12:32:16 +0200 Subject: ASoC: davinci-mcasp: Place constraint on number of channels In IIS (I2S, TDM, etc) mode the maximum number of allowed channels for either direction can be: number of serializers for the direction * tdm_slots. This constraint applicable for the first stream, while consequent stream should not have more channels then the first stream. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 10c2647..10ae75e 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -90,6 +90,9 @@ struct davinci_mcasp { bool dat_port; + /* Used for comstraint setting on the second stream */ + u32 channels; + #ifdef CONFIG_PM_SLEEP struct davinci_mcasp_context context; #endif @@ -811,6 +814,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, davinci_config_channel_size(mcasp, word_length); + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) + mcasp->channels = channels; + return 0; } @@ -839,7 +845,61 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, return ret; } +static int davinci_mcasp_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + u32 max_channels = 0; + int i, dir; + + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + return 0; + + /* + * Limit the maximum allowed channels for the first stream: + * number of serializers for the direction * tdm slots per serializer + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = TX_MODE; + else + dir = RX_MODE; + + for (i = 0; i < mcasp->num_serializer; i++) { + if (mcasp->serial_dir[i] == dir) + max_channels++; + } + max_channels *= mcasp->tdm_slots; + /* + * If the already active stream has less channels than the calculated + * limnit based on the seirializers * tdm_slots, we need to use that as + * a constraint for the second stream. + * Otherwise (first stream or less allowed channels) we use the + * calculated constraint. + */ + if (mcasp->channels && mcasp->channels < max_channels) + max_channels = mcasp->channels; + + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 2, max_channels); + return 0; +} + +static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + return; + + if (!cpu_dai->active) + mcasp->channels = 0; +} + static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .startup = davinci_mcasp_startup, + .shutdown = davinci_mcasp_shutdown, .trigger = davinci_mcasp_trigger, .hw_params = davinci_mcasp_hw_params, .set_fmt = davinci_mcasp_set_dai_fmt, -- cgit v0.10.2 From 18a4f55756ac945bd89d1fe63dafe36462ecba86 Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Mon, 10 Nov 2014 12:32:17 +0200 Subject: ASoC: davinci-mcasp: Active slots depend on active serializers Active slots count depends on the number of channels in the stream and the number of active serializers. Each serializer will handle at most the number of channels specified via 'tdm-slots' parameter in DT. There are two possible scenarios: - Single serializer: channel count fits in the max slots supported by McASP serializers, active slots is same as channel count - Multiple serializers: channel count is bigger than max slots supported by a serializer. Channel count determines how many serializers are needed at their max slot count configuration Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 10ae75e..a9822c7 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -629,13 +629,29 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, return 0; } -static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream) +static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, + int channels) { int i, active_slots; + int total_slots; + int active_serializers; u32 mask = 0; u32 busel = 0; - active_slots = mcasp->tdm_slots; + total_slots = mcasp->tdm_slots; + + /* + * If more than one serializer is needed, then use them with + * their specified tdm_slots count. Otherwise, one serializer + * can cope with the transaction using as many slots as channels + * in the stream, requires channels symmetry + */ + active_serializers = (channels + total_slots - 1) / total_slots; + if (active_serializers == 1) + active_slots = channels; + else + active_slots = total_slots; + for (i = 0; i < active_slots; i++) mask |= (1 << i); @@ -647,12 +663,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream) mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(active_slots), FSXMOD(0x1FF)); + FSXMOD(total_slots), FSXMOD(0x1FF)); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(active_slots), FSRMOD(0x1FF)); + FSRMOD(total_slots), FSRMOD(0x1FF)); return 0; } @@ -766,7 +782,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) ret = mcasp_dit_hw_param(mcasp, params_rate(params)); else - ret = mcasp_i2s_hw_param(mcasp, substream->stream); + ret = mcasp_i2s_hw_param(mcasp, substream->stream, + channels); if (ret) return ret; -- cgit v0.10.2 From 3539cacff2031f6d47881c5f3a4932b0ad5ec224 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 9 Nov 2014 19:52:06 -0800 Subject: ASoC: rsnd: Add Volume Ramp support This patch adds Volume Ramp to Renesas sound driver. amixer set "DVC Out" 100% amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" amixer set "DVC Out Ramp" on aplay xxx.wav & amixer set "DVC Out" 80% // Volume Down amixer set "DVC Out" 100% // Volume Up Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 956b84e..e2c8473 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -38,6 +38,9 @@ struct rsnd_dvc { struct clk *clk; struct rsnd_dvc_cfg_m volume; struct rsnd_dvc_cfg_m mute; + struct rsnd_dvc_cfg_s ren; /* Ramp Enable */ + struct rsnd_dvc_cfg_s rup; /* Ramp Rate Up */ + struct rsnd_dvc_cfg_s rdown; /* Ramp Rate Down */ }; #define rsnd_mod_to_dvc(_mod) \ @@ -49,9 +52,37 @@ struct rsnd_dvc { ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ i++) +static const char const *dvc_ramp_rate[] = { + "128 dB/1 step", /* 00000 */ + "64 dB/1 step", /* 00001 */ + "32 dB/1 step", /* 00010 */ + "16 dB/1 step", /* 00011 */ + "8 dB/1 step", /* 00100 */ + "4 dB/1 step", /* 00101 */ + "2 dB/1 step", /* 00110 */ + "1 dB/1 step", /* 00111 */ + "0.5 dB/1 step", /* 01000 */ + "0.25 dB/1 step", /* 01001 */ + "0.125 dB/1 step", /* 01010 */ + "0.125 dB/2 steps", /* 01011 */ + "0.125 dB/4 steps", /* 01100 */ + "0.125 dB/8 steps", /* 01101 */ + "0.125 dB/16 steps", /* 01110 */ + "0.125 dB/32 steps", /* 01111 */ + "0.125 dB/64 steps", /* 10000 */ + "0.125 dB/128 steps", /* 10001 */ + "0.125 dB/256 steps", /* 10010 */ + "0.125 dB/512 steps", /* 10011 */ + "0.125 dB/1024 steps", /* 10100 */ + "0.125 dB/2048 steps", /* 10101 */ + "0.125 dB/4096 steps", /* 10110 */ + "0.125 dB/8192 steps", /* 10111 */ +}; + static void rsnd_dvc_volume_update(struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 val[RSND_DVC_CHANNELS]; u32 dvucr = 0; u32 mute = 0; int i; @@ -62,10 +93,35 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) /* Disable DVC Register access */ rsnd_mod_write(mod, DVC_DVUER, 0); + /* Enable Ramp */ + if (dvc->ren.val) { + dvucr |= 0x10; + + /* Digital Volume Max */ + for (i = 0; i < RSND_DVC_CHANNELS; i++) + val[i] = dvc->volume.cfg.max; + + rsnd_mod_write(mod, DVC_VRCTR, 0xff); + rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 | + dvc->rdown.val); + /* + * FIXME !! + * use scale-downed Digital Volume + * as Volume Ramp + * 7F FFFF -> 3FF + */ + rsnd_mod_write(mod, DVC_VRDBR, + 0x3ff - (dvc->volume.val[0] >> 13)); + + } else { + for (i = 0; i < RSND_DVC_CHANNELS; i++) + val[i] = dvc->volume.val[i]; + } + /* Enable Digital Volume */ - dvucr = 0x100; - rsnd_mod_write(mod, DVC_VOL0R, dvc->volume.val[0]); - rsnd_mod_write(mod, DVC_VOL1R, dvc->volume.val[1]); + dvucr |= 0x100; + rsnd_mod_write(mod, DVC_VOL0R, val[0]); + rsnd_mod_write(mod, DVC_VOL1R, val[1]); /* Enable Mute */ if (mute) { @@ -324,6 +380,31 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; + /* Ramp */ + ret = _rsnd_dvc_pcm_new_s(mod, rdai, rtd, + rsnd_dai_is_play(rdai, io) ? + "DVC Out Ramp Switch" : "DVC In Ramp Switch", + &dvc->ren, 1); + if (ret < 0) + return ret; + + ret = _rsnd_dvc_pcm_new_e(mod, rdai, rtd, + rsnd_dai_is_play(rdai, io) ? + "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", + &dvc->rup, + dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate)); + if (ret < 0) + return ret; + + ret = _rsnd_dvc_pcm_new_e(mod, rdai, rtd, + rsnd_dai_is_play(rdai, io) ? + "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", + &dvc->rdown, + dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate)); + + if (ret < 0) + return ret; + return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index a0fed66..87a6f2d 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -324,6 +324,9 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100), RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100), RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100), + RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100), + RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100), + RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100), RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100), RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100), RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index d119adf..ed44ca8 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -91,6 +91,9 @@ enum rsnd_reg { RSND_REG_SHARE20, RSND_REG_SHARE21, RSND_REG_SHARE22, + RSND_REG_SHARE23, + RSND_REG_SHARE24, + RSND_REG_SHARE25, RSND_REG_MAX, }; @@ -129,6 +132,9 @@ enum rsnd_reg { #define RSND_REG_CMD_CTRL RSND_REG_SHARE20 #define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21 #define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22 +#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 +#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 +#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 struct rsnd_of_data; struct rsnd_priv; -- cgit v0.10.2 From 0099c762855eeee8d3eacc11fcc1e0819e77b2ed Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 9 Nov 2014 12:38:56 +0100 Subject: ASoC: simple-card: Remove useless casts There is no need to cast the cpu_of_node or codec_of_node of the dai_links when calling of_put_node. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index cd49d50..3e3fec4 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -457,16 +457,13 @@ static int asoc_simple_card_unref(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); struct snd_soc_dai_link *dai_link; - struct device_node *np; int num_links; for (num_links = 0, dai_link = card->dai_link; num_links < card->num_links; num_links++, dai_link++) { - np = (struct device_node *) dai_link->cpu_of_node; - of_node_put(np); - np = (struct device_node *) dai_link->codec_of_node; - of_node_put(np); + of_node_put(dai_link->cpu_of_node); + of_node_put(dai_link->codec_of_node); } return 0; } diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c index 3c8f604..d7640e7 100644 --- a/sound/soc/samsung/odroidx2_max98090.c +++ b/sound/soc/samsung/odroidx2_max98090.c @@ -153,8 +153,8 @@ static int odroidx2_audio_remove(struct platform_device *pdev) snd_soc_unregister_card(card); - of_node_put((struct device_node *)odroidx2_dai[0].cpu_of_node); - of_node_put((struct device_node *)odroidx2_dai[0].codec_of_node); + of_node_put(odroidx2_dai[0].cpu_of_node); + of_node_put(odroidx2_dai[0].codec_of_node); return 0; } diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index b3b66aa..ea9ba284 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -64,11 +64,9 @@ static void mop500_of_node_put(void) for (i = 0; i < 2; i++) { if (mop500_dai_links[i].cpu_of_node) - of_node_put((struct device_node *) - mop500_dai_links[i].cpu_of_node); + of_node_put(mop500_dai_links[i].cpu_of_node); if (mop500_dai_links[i].codec_of_node) - of_node_put((struct device_node *) - mop500_dai_links[i].codec_of_node); + of_node_put(mop500_dai_links[i].codec_of_node); } } -- cgit v0.10.2 From 52ef6284a840bdef50b6ed505bdda014dd769cab Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:00:57 +0100 Subject: ASoC: ab8500-codec: Move control lock to the driver level The ab8500 driver uses a driver specific lock to protect concurrent access to some of the control put/get handlers and uses the snd_soc_codec mutex for some others. This patch updates the driver to consistently use the driver specific lock for all controls. This will allow us to eventually remove the snd_soc_codec mutex. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index fd43827..7dfbc99 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -126,13 +126,13 @@ struct ab8500_codec_drvdata_dbg { /* Private data for AB8500 device-driver */ struct ab8500_codec_drvdata { struct regmap *regmap; + struct mutex ctrl_lock; /* Sidetone */ long *sid_fir_values; enum sid_state sid_status; /* ANC */ - struct mutex anc_lock; long *anc_fir_values; long *anc_iir_values; enum anc_state anc_status; @@ -1129,9 +1129,9 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); ucontrol->value.integer.value[0] = drvdata->sid_status; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1154,7 +1154,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, return -EIO; } - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF); if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { @@ -1185,7 +1185,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, drvdata->sid_status = SID_FIR_CONFIGURED; out: - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); dev_dbg(codec->dev, "%s: Exit\n", __func__); @@ -1198,9 +1198,9 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); ucontrol->value.integer.value[0] = drvdata->anc_status; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1217,7 +1217,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, dev_dbg(dev, "%s: Enter.\n", __func__); - mutex_lock(&drvdata->anc_lock); + mutex_lock(&drvdata->ctrl_lock); req = ucontrol->value.integer.value[0]; if (req >= ARRAY_SIZE(enum_anc_state)) { @@ -1244,9 +1244,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, } snd_soc_dapm_sync(&codec->dapm); - mutex_lock(&codec->mutex); anc_configure(codec, apply_fir, apply_iir); - mutex_unlock(&codec->mutex); if (apply_fir) { if (drvdata->anc_status == ANC_IIR_CONFIGURED) @@ -1265,7 +1263,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, snd_soc_dapm_sync(&codec->dapm); cleanup: - mutex_unlock(&drvdata->anc_lock); + mutex_unlock(&drvdata->ctrl_lock); if (status < 0) dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n", @@ -1294,14 +1292,15 @@ static int filter_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec); struct filter_control *fc = (struct filter_control *)kcontrol->private_value; unsigned int i; - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); for (i = 0; i < fc->count; i++) ucontrol->value.integer.value[i] = fc->value[i]; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1310,14 +1309,15 @@ static int filter_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec); struct filter_control *fc = (struct filter_control *)kcontrol->private_value; unsigned int i; - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); for (i = 0; i < fc->count; i++) fc->value[i] = ucontrol->value.integer.value[i]; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -2545,7 +2545,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); - mutex_init(&drvdata->anc_lock); + mutex_init(&drvdata->ctrl_lock); return status; } -- cgit v0.10.2 From 210a5fae55c05174b8a5b571b6698626b3ae35d3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:00:58 +0100 Subject: ASoC: max98095: Move mutex to the driver level The max98095 uses the snd_soc_codec mutex to protect against concurrent access in some of its control put handlers. Move this mutex to the driver level so we can eventually remove the snd_soc_codec mutex. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 0ee6797..01f3cc9 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ struct max98095_priv { unsigned int mic2pre; struct snd_soc_jack *headphone_jack; struct snd_soc_jack *mic_jack; + struct mutex lock; }; static const struct reg_default max98095_reg_def[] = { @@ -1803,7 +1805,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL); snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&codec->mutex); + mutex_lock(&max98095->lock); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); m98095_eq_band(codec, channel, 0, coef_set->band1); m98095_eq_band(codec, channel, 1, coef_set->band2); @@ -1811,7 +1813,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, m98095_eq_band(codec, channel, 3, coef_set->band4); m98095_eq_band(codec, channel, 4, coef_set->band5); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&codec->mutex); + mutex_unlock(&max98095->lock); /* Restore the original on/off state */ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave); @@ -1957,12 +1959,12 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL); snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&codec->mutex); + mutex_lock(&max98095->lock); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); m98095_biquad_band(codec, channel, 0, coef_set->band1); m98095_biquad_band(codec, channel, 1, coef_set->band2); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&codec->mutex); + mutex_unlock(&max98095->lock); /* Restore the original on/off state */ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave); @@ -2395,6 +2397,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c, if (max98095 == NULL) return -ENOMEM; + mutex_init(&max98095->lock); + max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap); if (IS_ERR(max98095->regmap)) { ret = PTR_ERR(max98095->regmap); -- cgit v0.10.2 From d74bcaaeb66826192c9e361cbfe8fd1ffaccf74e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:00:59 +0100 Subject: ASoC: wm5102: Move ultrasonic response settings lock to the driver level The wm5102 driver currently uses the snd_soc_codec mutex to protect its ultrasonic response settings from concurrent access. This patch moves this lock to the driver level. This will allow us to eventually remove the snd_soc_codec mutex. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index f34723f..910e3aa 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -141,6 +141,7 @@ struct arizona { uint16_t dac_comp_coeff; uint8_t dac_comp_enabled; + struct mutex dac_comp_lock; }; int arizona_clk32k_enable(struct arizona *arizona); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 0c05e7a..730636c 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1164,13 +1164,13 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, { 0x80, 0x0 }, }; - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); dac_comp[1].def = arizona->dac_comp_coeff; if (rate >= 176400) dac_comp[2].def = arizona->dac_comp_enabled; - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); regmap_multi_reg_write(arizona->regmap, dac_comp, diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index f602349..1f75534 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -619,10 +619,10 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, struct arizona *arizona = dev_get_drvdata(codec->dev->parent); uint16_t data; - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); data = cpu_to_be16(arizona->dac_comp_coeff); memcpy(ucontrol->value.bytes.data, &data, sizeof(data)); - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -633,11 +633,11 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data, sizeof(arizona->dac_comp_coeff)); arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff); - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -648,9 +648,9 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -661,9 +661,9 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } -- cgit v0.10.2 From a51ff30f45473a80f78b2572666473887e010d91 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:01:00 +0100 Subject: ASoC: wm8731: Move the deemph lock to the driver level The wm8731 uses the snd_soc_codec mutex to protect its deemph settings from concurrent access. This patch moves this lock to the driver level. This will allow us to eventually remove the snd_soc_codec mutex. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index eebb328..5dae9a6 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,8 @@ struct wm8731_priv { int sysclk_type; int playback_fs; bool deemph; + + struct mutex lock; }; @@ -138,7 +141,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, if (deemph > 1) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock(&wm8731->lock); if (wm8731->deemph != deemph) { wm8731->deemph = deemph; @@ -146,7 +149,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, ret = 1; } - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8731->lock); return ret; } @@ -685,6 +688,8 @@ static int wm8731_spi_probe(struct spi_device *spi) if (wm8731 == NULL) return -ENOMEM; + mutex_init(&wm8731->lock); + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); -- cgit v0.10.2 From 78660af7ba30e9d2cc9614465c8b65b3c85f49a9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:01:01 +0100 Subject: ASoC: wm8903: Move the deemph lock to the driver level The wm8903 uses the snd_soc_codec mutex to protect its deemph settings from concurrent access. This patch moves this lock to the driver level. This will allow us to eventually remove the snd_soc_codec mutex. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index c038b3e..ffbe6df 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,7 @@ struct wm8903_priv { int sysclk; int irq; + struct mutex lock; int fs; int deemph; @@ -457,7 +459,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, if (deemph > 1) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock(&wm8903->lock); if (wm8903->deemph != deemph) { wm8903->deemph = deemph; @@ -465,7 +467,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, ret = 1; } - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8903->lock); return ret; } @@ -2023,6 +2025,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c, GFP_KERNEL); if (wm8903 == NULL) return -ENOMEM; + + mutex_init(&wm8903->lock); wm8903->dev = &i2c->dev; wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap); -- cgit v0.10.2 From fabfad2f8b23529722c6ef5b3537c457e63d2c82 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:01:02 +0100 Subject: ASoC: wm8958: Move DSP firmware lock to driver level The wm8958 driver uses the snd_soc_codec mutex to protect the various firmware pointers from concurrent assignment. This patch moves this lock to the driver level. This will allow us to eventually remove the snd_soc_codec mutex. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index 0dada7f..3cbc82b 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -867,9 +867,9 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) { - mutex_lock(&codec->mutex); + mutex_lock(&wm8994->fw_lock); wm8994->enh_eq = fw; - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8994->fw_lock); } } @@ -879,9 +879,9 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) { - mutex_lock(&codec->mutex); + mutex_lock(&wm8994->fw_lock); wm8994->mbc_vss = fw; - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8994->fw_lock); } } @@ -891,9 +891,9 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) { - mutex_lock(&codec->mutex); + mutex_lock(&wm8994->fw_lock); wm8994->mbc = fw; - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8994->fw_lock); } } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1fcb9f3..dbca6e0 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4457,6 +4457,8 @@ static int wm8994_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm8994); + mutex_init(&wm8994->fw_lock); + wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent); pm_runtime_enable(&pdev->dev); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 6536f8d..dd73387 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "wm_hubs.h" @@ -156,6 +157,7 @@ struct wm8994_priv { unsigned int aif1clk_disable:1; unsigned int aif2clk_disable:1; + struct mutex fw_lock; int dsp_active; const struct firmware *cur_fw; const struct firmware *mbc; -- cgit v0.10.2 From 3e4199ef0105fb718b24cbcc837ad527fd60c880 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:01:03 +0100 Subject: ASoC: wm8962: Move DSP enable lock to the driver level The wm8962 uses the snd_soc_codec mutex to protect the wm8962_dsp2_ena_put() function from concurrent execution. This patch moves that lock to the driver level. This will allow us to eventually remove the snd_soc_codec mutex. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 9077411..61ca4a7 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,7 @@ struct wm8962_priv { int fll_fref; int fll_fout; + struct mutex dsp2_ena_lock; u16 dsp2_ena; struct delayed_work mic_work; @@ -1570,7 +1572,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) & WM8962_DSP2_ENA; - mutex_lock(&codec->mutex); + mutex_lock(&wm8962->dsp2_ena_lock); if (ucontrol->value.integer.value[0]) wm8962->dsp2_ena |= 1 << shift; @@ -1590,7 +1592,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, } out: - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8962->dsp2_ena_lock); return ret; } @@ -3557,6 +3559,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, if (wm8962 == NULL) return -ENOMEM; + mutex_init(&wm8962->dsp2_ena_lock); + i2c_set_clientdata(i2c, wm8962); INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work); -- cgit v0.10.2 From bd6b87c104bae49816808fde5f55a262093e85ed Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Nov 2014 17:01:04 +0100 Subject: ASoC: Remove CODEC mutex The CODEC mutex is now unused and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ba7130..5c91b06 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -780,7 +780,6 @@ struct snd_soc_codec { struct device *dev; const struct snd_soc_codec_driver *driver; - struct mutex mutex; struct list_head list; struct list_head card_list; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a2..cc7bb7a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4362,7 +4362,6 @@ int snd_soc_register_codec(struct device *dev, codec->dev = dev; codec->driver = codec_drv; codec->component.val_bytes = codec_drv->reg_word_size; - mutex_init(&codec->mutex); #ifdef CONFIG_DEBUG_FS codec->component.init_debugfs = soc_init_codec_debugfs; -- cgit v0.10.2 From 8c2727f97b4825adaad43bf98632abc9940345a4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 10 Nov 2014 17:49:30 -0200 Subject: ASoC: mxs: mxs-saif: Register the irq with the device name Instead of registering the irq name with the driver name, it's better to pass the device name so that we have a more explicit indication as to what saif instance the irq is related: $ cat /proc/interrupts CPU0 ... 214: 4 - 59 80042000.saif 215: 0 - 58 80046000.saif Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 231d7e7..83b2fea 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -773,7 +773,7 @@ static int mxs_saif_probe(struct platform_device *pdev) saif->dev = &pdev->dev; ret = devm_request_irq(&pdev->dev, saif->irq, mxs_saif_irq, 0, - "mxs-saif", saif); + dev_name(&pdev->dev), saif); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); return ret; -- cgit v0.10.2 From b31eb901c4e5eeef4c83c43dfbc7fe0d4348cb21 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 6 Nov 2014 17:49:45 -0300 Subject: [media] smiapp: Only some selection targets are settable Setting a non-settable selection target caused BUG() to be called. The check for valid selections only takes the selection target into account, but does not tell whether it may be set, or only get. Fix the issue by simply returning an error to the user. Signed-off-by: Sakari Ailus Cc: stable@vger.kernel.org # for v3.10 and up Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 932ed9b..b10aaed 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2190,7 +2190,7 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, ret = smiapp_set_compose(subdev, fh, sel); break; default: - BUG(); + ret = -EINVAL; } mutex_unlock(&sensor->mutex); -- cgit v0.10.2 From 0cf1863219b474e82af9cb1f6715a0bbfa3fdf1a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 11 Nov 2014 17:59:50 +0800 Subject: ASoC: rt5670: add rt5672 codec support rt5672 is very similar to rt5670. Therefore we use one codec driver to support both codecs. The difference between rt5670 and rt5672 is there is some difference in their dapm routing table. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index ba9d9b4..066b583 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -1595,29 +1595,40 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = { /* PDM */ SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2, RT5670_PWR_PDM1_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2, - RT5670_PWR_PDM2_BIT, 0, NULL, 0), SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL, RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux), SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL, RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux), - SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL, - RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux), - SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL, - RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux), /* Output Lines */ SND_SOC_DAPM_OUTPUT("HPOL"), SND_SOC_DAPM_OUTPUT("HPOR"), SND_SOC_DAPM_OUTPUT("LOUTL"), SND_SOC_DAPM_OUTPUT("LOUTR"), +}; + +static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2, + RT5670_PWR_PDM2_BIT, 0, NULL, 0), + SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux), + SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux), SND_SOC_DAPM_OUTPUT("PDM1L"), SND_SOC_DAPM_OUTPUT("PDM1R"), SND_SOC_DAPM_OUTPUT("PDM2L"), SND_SOC_DAPM_OUTPUT("PDM2R"), }; +static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = { + SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPOLP"), + SND_SOC_DAPM_OUTPUT("SPOLN"), + SND_SOC_DAPM_OUTPUT("SPORP"), + SND_SOC_DAPM_OUTPUT("SPORN"), +}; + static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc }, { "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc }, @@ -1970,12 +1981,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" }, { "PDM1 R Mux", NULL, "PDM1 Power" }, - { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, - { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" }, - { "PDM2 L Mux", NULL, "PDM2 Power" }, - { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, - { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" }, - { "PDM2 R Mux", NULL, "PDM2 Power" }, { "HP Amp", NULL, "HPO MIX" }, { "HP Amp", NULL, "Mic Det Power" }, @@ -1993,13 +1998,30 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "LOUTR", NULL, "LOUT R Playback" }, { "LOUTL", NULL, "Improve HP Amp Drv" }, { "LOUTR", NULL, "Improve HP Amp Drv" }, +}; +static const struct snd_soc_dapm_route rt5670_specific_dapm_routes[] = { + { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM2 L Mux", NULL, "PDM2 Power" }, + { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM2 R Mux", NULL, "PDM2 Power" }, { "PDM1L", NULL, "PDM1 L Mux" }, { "PDM1R", NULL, "PDM1 R Mux" }, { "PDM2L", NULL, "PDM2 L Mux" }, { "PDM2R", NULL, "PDM2 R Mux" }, }; +static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = { + { "SPO Amp", NULL, "PDM1 L Mux" }, + { "SPO Amp", NULL, "PDM1 R Mux" }, + { "SPOLP", NULL, "SPO Amp" }, + { "SPOLN", NULL, "SPO Amp" }, + { "SPORP", NULL, "SPO Amp" }, + { "SPORN", NULL, "SPO Amp" }, +}; + static int rt5670_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -2331,6 +2353,29 @@ static int rt5670_probe(struct snd_soc_codec *codec) { struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) { + case RT5670_ID_5670: + case RT5670_ID_5671: + snd_soc_dapm_new_controls(&codec->dapm, + rt5670_specific_dapm_widgets, + ARRAY_SIZE(rt5670_specific_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, + rt5670_specific_dapm_routes, + ARRAY_SIZE(rt5670_specific_dapm_routes)); + break; + case RT5670_ID_5672: + snd_soc_dapm_new_controls(&codec->dapm, + rt5672_specific_dapm_widgets, + ARRAY_SIZE(rt5672_specific_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, + rt5672_specific_dapm_routes, + ARRAY_SIZE(rt5672_specific_dapm_routes)); + break; + default: + dev_err(codec->dev, + "The driver is for RT5670 RT5671 or RT5672 only\n"); + return -ENODEV; + } rt5670->codec = codec; return 0; @@ -2452,6 +2497,8 @@ static const struct regmap_config rt5670_regmap = { static const struct i2c_device_id rt5670_i2c_id[] = { { "rt5670", 0 }, + { "rt5671", 0 }, + { "rt5672", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index a0b5c85..d11b9c2 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -228,6 +228,12 @@ #define RT5670_R_VOL_MASK (0x3f) #define RT5670_R_VOL_SFT 0 +/* SW Reset & Device ID (0x00) */ +#define RT5670_ID_MASK (0x3 << 1) +#define RT5670_ID_5670 (0x0 << 1) +#define RT5670_ID_5672 (0x1 << 1) +#define RT5670_ID_5671 (0x2 << 1) + /* Combo Jack Control 1 (0x0a) */ #define RT5670_CBJ_BST1_MASK (0xf << 12) #define RT5670_CBJ_BST1_SFT (12) -- cgit v0.10.2 From 5563502cb68d9520e13fe2350922ca88c4531c63 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 11 Nov 2014 11:31:28 +0800 Subject: ASoC: rt5645: remove unused rt5645_clk_sel_put Remove rt5645_clk_sel_put function since it is never used. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 1dbbebc..665f8b6 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -441,65 +441,6 @@ static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum, RT5645_TDM_CTRL_1, 8, rt5645_tdm_adc_data_select); -static int rt5645_clk_sel_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int u_bit = 0, p_bit = 0; - struct soc_enum *em = - (struct soc_enum *)kcontrol->private_value; - - switch (em->reg) { - case RT5645_ASRC_2: - switch (em->shift_l) { - case 0: - u_bit = 0x8; - p_bit = RT5645_PWR_ADC_S1F; - break; - case 4: - u_bit = 0x100; - p_bit = RT5645_PWR_DAC_MF_R; - break; - case 8: - u_bit = 0x200; - p_bit = RT5645_PWR_DAC_MF_L; - break; - case 12: - u_bit = 0x400; - p_bit = RT5645_PWR_DAC_S1F; - break; - } - break; - case RT5645_ASRC_3: - switch (em->shift_l) { - case 0: - u_bit = 0x1; - p_bit = RT5645_PWR_ADC_MF_R; - break; - case 4: - u_bit = 0x2; - p_bit = RT5645_PWR_ADC_MF_L; - break; - } - break; - } - - if (u_bit || p_bit) { - switch (ucontrol->value.integer.value[0]) { - case 1 ... 4: /*enable*/ - if (snd_soc_read(codec, RT5645_PWR_DIG2) & p_bit) - snd_soc_update_bits(codec, - RT5645_ASRC_1, u_bit, u_bit); - break; - default: /*disable*/ - snd_soc_update_bits(codec, RT5645_ASRC_1, u_bit, 0); - break; - } - } - - return snd_soc_put_enum_double(kcontrol, ucontrol); -} - static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, -- cgit v0.10.2 From a60e654be733a69879148cb4c56d0f58b749e3c4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 11 Nov 2014 10:59:00 +0200 Subject: ASoC: tlv320aic3x: Convert SOC_ENUM_SINGLE/DOUBLE arrays to individual It is easier to find the relevant enums in the code. Use the SOC_ENUM_*_DECL macro for the individual items. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 8770e28..9901400 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -216,61 +216,68 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, return 0; } -static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" }; -static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" }; -static const char *aic3x_left_hpcom_mux[] = - { "differential of HPLOUT", "constant VCM", "single-ended" }; -static const char *aic3x_right_hpcom_mux[] = - { "differential of HPROUT", "constant VCM", "single-ended", - "differential of HPLCOM", "external feedback" }; -static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" }; -static const char *aic3x_adc_hpf[] = - { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" }; - -#define LDAC_ENUM 0 -#define RDAC_ENUM 1 -#define LHPCOM_ENUM 2 -#define RHPCOM_ENUM 3 -#define LINE1L_2_L_ENUM 4 -#define LINE1L_2_R_ENUM 5 -#define LINE1R_2_L_ENUM 6 -#define LINE1R_2_R_ENUM 7 -#define LINE2L_ENUM 8 -#define LINE2R_ENUM 9 -#define ADC_HPF_ENUM 10 - -static const struct soc_enum aic3x_enum[] = { - SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux), - SOC_ENUM_SINGLE(DAC_LINE_MUX, 4, 3, aic3x_right_dac_mux), - SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux), - SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux), - SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), - SOC_ENUM_SINGLE(LINE1L_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), - SOC_ENUM_SINGLE(LINE1R_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), - SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), - SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), - SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), - SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf), -}; - -static const char *aic3x_agc_level[] = - { "-5.5dB", "-8dB", "-10dB", "-12dB", "-14dB", "-17dB", "-20dB", "-24dB" }; -static const struct soc_enum aic3x_agc_level_enum[] = { - SOC_ENUM_SINGLE(LAGC_CTRL_A, 4, 8, aic3x_agc_level), - SOC_ENUM_SINGLE(RAGC_CTRL_A, 4, 8, aic3x_agc_level), -}; - -static const char *aic3x_agc_attack[] = { "8ms", "11ms", "16ms", "20ms" }; -static const struct soc_enum aic3x_agc_attack_enum[] = { - SOC_ENUM_SINGLE(LAGC_CTRL_A, 2, 4, aic3x_agc_attack), - SOC_ENUM_SINGLE(RAGC_CTRL_A, 2, 4, aic3x_agc_attack), -}; - -static const char *aic3x_agc_decay[] = { "100ms", "200ms", "400ms", "500ms" }; -static const struct soc_enum aic3x_agc_decay_enum[] = { - SOC_ENUM_SINGLE(LAGC_CTRL_A, 0, 4, aic3x_agc_decay), - SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay), -}; +static const char * const aic3x_left_dac_mux[] = { + "DAC_L1", "DAC_L3", "DAC_L2" }; +static SOC_ENUM_SINGLE_DECL(aic3x_left_dac_enum, DAC_LINE_MUX, 6, + aic3x_left_dac_mux); + +static const char * const aic3x_right_dac_mux[] = { + "DAC_R1", "DAC_R3", "DAC_R2" }; +static SOC_ENUM_SINGLE_DECL(aic3x_right_dac_enum, DAC_LINE_MUX, 4, + aic3x_right_dac_mux); + +static const char * const aic3x_left_hpcom_mux[] = { + "differential of HPLOUT", "constant VCM", "single-ended" }; +static SOC_ENUM_SINGLE_DECL(aic3x_left_hpcom_enum, HPLCOM_CFG, 4, + aic3x_left_hpcom_mux); + +static const char * const aic3x_right_hpcom_mux[] = { + "differential of HPROUT", "constant VCM", "single-ended", + "differential of HPLCOM", "external feedback" }; +static SOC_ENUM_SINGLE_DECL(aic3x_right_hpcom_enum, HPRCOM_CFG, 3, + aic3x_right_hpcom_mux); + +static const char * const aic3x_linein_mode_mux[] = { + "single-ended", "differential" }; +static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_l_enum, LINE1L_2_LADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_r_enum, LINE1L_2_RADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_l_enum, LINE1R_2_LADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_r_enum, LINE1R_2_RADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line2l_2_ldac_enum, LINE2L_2_LADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line2r_2_rdac_enum, LINE2R_2_RADC_CTRL, 7, + aic3x_linein_mode_mux); + +static const char * const aic3x_adc_hpf[] = { + "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" }; +static SOC_ENUM_DOUBLE_DECL(aic3x_adc_hpf_enum, AIC3X_CODEC_DFILT_CTRL, 6, 4, + aic3x_adc_hpf); + +static const char * const aic3x_agc_level[] = { + "-5.5dB", "-8dB", "-10dB", "-12dB", + "-14dB", "-17dB", "-20dB", "-24dB" }; +static SOC_ENUM_SINGLE_DECL(aic3x_lagc_level_enum, LAGC_CTRL_A, 4, + aic3x_agc_level); +static SOC_ENUM_SINGLE_DECL(aic3x_ragc_level_enum, RAGC_CTRL_A, 4, + aic3x_agc_level); + +static const char * const aic3x_agc_attack[] = { + "8ms", "11ms", "16ms", "20ms" }; +static SOC_ENUM_SINGLE_DECL(aic3x_lagc_attack_enum, LAGC_CTRL_A, 2, + aic3x_agc_attack); +static SOC_ENUM_SINGLE_DECL(aic3x_ragc_attack_enum, RAGC_CTRL_A, 2, + aic3x_agc_attack); + +static const char * const aic3x_agc_decay[] = { + "100ms", "200ms", "400ms", "500ms" }; +static SOC_ENUM_SINGLE_DECL(aic3x_lagc_decay_enum, LAGC_CTRL_A, 0, + aic3x_agc_decay); +static SOC_ENUM_SINGLE_DECL(aic3x_ragc_decay_enum, RAGC_CTRL_A, 0, + aic3x_agc_decay); /* * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps @@ -385,12 +392,12 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { * adjust PGA to max value when ADC is on and will never go back. */ SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0), - SOC_ENUM("Left AGC Target level", aic3x_agc_level_enum[0]), - SOC_ENUM("Right AGC Target level", aic3x_agc_level_enum[1]), - SOC_ENUM("Left AGC Attack time", aic3x_agc_attack_enum[0]), - SOC_ENUM("Right AGC Attack time", aic3x_agc_attack_enum[1]), - SOC_ENUM("Left AGC Decay time", aic3x_agc_decay_enum[0]), - SOC_ENUM("Right AGC Decay time", aic3x_agc_decay_enum[1]), + SOC_ENUM("Left AGC Target level", aic3x_lagc_level_enum), + SOC_ENUM("Right AGC Target level", aic3x_ragc_level_enum), + SOC_ENUM("Left AGC Attack time", aic3x_lagc_attack_enum), + SOC_ENUM("Right AGC Attack time", aic3x_ragc_attack_enum), + SOC_ENUM("Left AGC Decay time", aic3x_lagc_decay_enum), + SOC_ENUM("Right AGC Decay time", aic3x_ragc_decay_enum), /* De-emphasis */ SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0), @@ -400,7 +407,7 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { 0, 119, 0, adc_tlv), SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1), - SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]), + SOC_ENUM("ADC HPF Cut-off", aic3x_adc_hpf_enum), }; static const struct snd_kcontrol_new aic3x_mono_controls[] = { @@ -427,19 +434,19 @@ static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl = /* Left DAC Mux */ static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_left_dac_enum); /* Right DAC Mux */ static const struct snd_kcontrol_new aic3x_right_dac_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[RDAC_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_right_dac_enum); /* Left HPCOM Mux */ static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LHPCOM_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_left_hpcom_enum); /* Right HPCOM Mux */ static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_right_hpcom_enum); /* Left Line Mixer */ static const struct snd_kcontrol_new aic3x_left_line_mixer_controls[] = { @@ -531,23 +538,23 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = { /* Left Line1 Mux */ static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_L_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_line1l_2_l_enum); static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_R_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_line1l_2_r_enum); /* Right Line1 Mux */ static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_R_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_line1r_2_r_enum); static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_L_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_line1r_2_l_enum); /* Left Line2 Mux */ static const struct snd_kcontrol_new aic3x_left_line2_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE2L_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_line2l_2_ldac_enum); /* Right Line2 Mux */ static const struct snd_kcontrol_new aic3x_right_line2_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]); +SOC_DAPM_ENUM("Route", aic3x_line2r_2_rdac_enum); static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { /* Left DAC to Left Outputs */ -- cgit v0.10.2 From 68d6626925c3529790a2055d41578415fa98495e Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Tue, 11 Nov 2014 10:59:01 +0200 Subject: ASoC: tlv320aic3x: Add output driver pop reduction controls Output driver has two parameters that can be configured to reduce pop noise: power-on delay and ramp-up step time. Two new kcontrols have been added to set these parameters. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 9901400..f0a8281 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -279,6 +279,16 @@ static SOC_ENUM_SINGLE_DECL(aic3x_lagc_decay_enum, LAGC_CTRL_A, 0, static SOC_ENUM_SINGLE_DECL(aic3x_ragc_decay_enum, RAGC_CTRL_A, 0, aic3x_agc_decay); +static const char * const aic3x_poweron_time[] = { + "0us", "10us", "100us", "1ms", "10ms", "50ms", + "100ms", "200ms", "400ms", "800ms", "2s", "4s" }; +static SOC_ENUM_SINGLE_DECL(aic3x_poweron_time_enum, HPOUT_POP_REDUCTION, 4, + aic3x_poweron_time); + +static const char * const aic3x_rampup_step[] = { "0ms", "1ms", "2ms", "4ms" }; +static SOC_ENUM_SINGLE_DECL(aic3x_rampup_step_enum, HPOUT_POP_REDUCTION, 2, + aic3x_rampup_step); + /* * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps */ @@ -408,6 +418,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1), SOC_ENUM("ADC HPF Cut-off", aic3x_adc_hpf_enum), + + /* Pop reduction */ + SOC_ENUM("Output Driver Power-On time", aic3x_poweron_time_enum), + SOC_ENUM("Output Driver Ramp-up step", aic3x_rampup_step_enum), }; static const struct snd_kcontrol_new aic3x_mono_controls[] = { -- cgit v0.10.2 From 91159ecaf4401f7b4b0d48f59c877a0779209af5 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 11 Nov 2014 15:31:19 +0800 Subject: ASoC: rt5677: Add TDM channel mux in DAC side of IF1 and IF2 It is the slot selection in DAC side of IF1 and IF2. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 4b6f7d5..5d317c68 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -1906,6 +1906,126 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5677_if2_adc_tdm_swap_mux = SOC_DAPM_ENUM("IF2 ADC TDM Swap Source", rt5677_if2_adc_tdm_swap_enum); +/* TDM IF1/2 DAC Data Selection */ /* MX-3E[14:12][10:8][6:4][2:0] + MX-3F[14:12][10:8][6:4][2:0] + MX-43[14:12][10:8][6:4][2:0] + MX-44[14:12][10:8][6:4][2:0] */ +static const char * const rt5677_if12_dac_tdm_sel_src[] = { + "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac0_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC0_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC0 TDM Source", rt5677_if1_dac0_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac1_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC1_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC1 TDM Source", rt5677_if1_dac1_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac2_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC2_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC2 TDM Source", rt5677_if1_dac2_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac3_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC3_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC3 TDM Source", rt5677_if1_dac3_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac4_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC4_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac4_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC4 TDM Source", rt5677_if1_dac4_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac5_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC5_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac5_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC5 TDM Source", rt5677_if1_dac5_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac6_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC6_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac6_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC6 TDM Source", rt5677_if1_dac6_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac7_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC7_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac7_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC7 TDM Source", rt5677_if1_dac7_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac0_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC0_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC0 TDM Source", rt5677_if2_dac0_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac1_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC1_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC1 TDM Source", rt5677_if2_dac1_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac2_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC2_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC2 TDM Source", rt5677_if2_dac2_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac3_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC3_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC3 TDM Source", rt5677_if2_dac3_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac4_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC4_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac4_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC4 TDM Source", rt5677_if2_dac4_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac5_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC5_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac5_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC5 TDM Source", rt5677_if2_dac5_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac6_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC6_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac6_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC6 TDM Source", rt5677_if2_dac6_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac7_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC7_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC7 TDM Source", rt5677_if2_dac7_tdm_sel_enum); + static int rt5677_bst1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -2389,6 +2509,40 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_MUX("SLB ADC4 Mux", SND_SOC_NOPM, 0, 0, &rt5677_slb_adc4_mux), + SND_SOC_DAPM_MUX("IF1 DAC0 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac3_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac4_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC5 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac5_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC6 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac6_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC7 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac7_tdm_sel_mux), + + SND_SOC_DAPM_MUX("IF2 DAC0 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac3_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac4_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC5 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac5_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC6 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac6_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC7 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac7_tdm_sel_mux), + /* Audio Interface */ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), @@ -3036,14 +3190,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "IF1 DAC6", NULL, "I2S1" }, { "IF1 DAC7", NULL, "I2S1" }, - { "IF1 DAC01", NULL, "IF1 DAC0" }, - { "IF1 DAC01", NULL, "IF1 DAC1" }, - { "IF1 DAC23", NULL, "IF1 DAC2" }, - { "IF1 DAC23", NULL, "IF1 DAC3" }, - { "IF1 DAC45", NULL, "IF1 DAC4" }, - { "IF1 DAC45", NULL, "IF1 DAC5" }, - { "IF1 DAC67", NULL, "IF1 DAC6" }, - { "IF1 DAC67", NULL, "IF1 DAC7" }, + { "IF1 DAC0 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC0 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC0 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC0 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC0 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC0 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC0 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC0 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC1 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC1 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC1 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC1 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC1 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC1 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC1 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC1 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC2 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC2 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC2 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC2 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC2 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC2 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC2 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC2 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC3 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC3 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC3 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC3 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC3 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC3 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC3 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC3 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC4 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC4 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC4 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC4 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC4 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC4 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC4 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC4 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC5 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC5 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC5 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC5 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC5 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC5 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC5 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC5 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC6 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC6 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC6 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC6 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC6 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC6 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC6 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC6 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC7 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC7 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC7 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC7 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC7 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC7 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC7 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC7 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC01", NULL, "IF1 DAC0 Mux" }, + { "IF1 DAC01", NULL, "IF1 DAC1 Mux" }, + { "IF1 DAC23", NULL, "IF1 DAC2 Mux" }, + { "IF1 DAC23", NULL, "IF1 DAC3 Mux" }, + { "IF1 DAC45", NULL, "IF1 DAC4 Mux" }, + { "IF1 DAC45", NULL, "IF1 DAC5 Mux" }, + { "IF1 DAC67", NULL, "IF1 DAC6 Mux" }, + { "IF1 DAC67", NULL, "IF1 DAC7 Mux" }, { "IF2 DAC0", NULL, "AIF2RX" }, { "IF2 DAC1", NULL, "AIF2RX" }, @@ -3062,14 +3288,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "IF2 DAC6", NULL, "I2S2" }, { "IF2 DAC7", NULL, "I2S2" }, - { "IF2 DAC01", NULL, "IF2 DAC0" }, - { "IF2 DAC01", NULL, "IF2 DAC1" }, - { "IF2 DAC23", NULL, "IF2 DAC2" }, - { "IF2 DAC23", NULL, "IF2 DAC3" }, - { "IF2 DAC45", NULL, "IF2 DAC4" }, - { "IF2 DAC45", NULL, "IF2 DAC5" }, - { "IF2 DAC67", NULL, "IF2 DAC6" }, - { "IF2 DAC67", NULL, "IF2 DAC7" }, + { "IF2 DAC0 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC0 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC0 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC0 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC0 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC0 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC0 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC0 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC1 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC1 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC1 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC1 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC1 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC1 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC1 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC1 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC2 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC2 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC2 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC2 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC2 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC2 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC2 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC2 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC3 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC3 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC3 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC3 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC3 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC3 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC3 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC3 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC4 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC4 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC4 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC4 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC4 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC4 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC4 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC4 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC5 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC5 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC5 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC5 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC5 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC5 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC5 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC5 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC6 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC6 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC6 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC6 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC6 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC6 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC6 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC6 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC7 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC7 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC7 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC7 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC7 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC7 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC7 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC7 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC01", NULL, "IF2 DAC0 Mux" }, + { "IF2 DAC01", NULL, "IF2 DAC1 Mux" }, + { "IF2 DAC23", NULL, "IF2 DAC2 Mux" }, + { "IF2 DAC23", NULL, "IF2 DAC3 Mux" }, + { "IF2 DAC45", NULL, "IF2 DAC4 Mux" }, + { "IF2 DAC45", NULL, "IF2 DAC5 Mux" }, + { "IF2 DAC67", NULL, "IF2 DAC6 Mux" }, + { "IF2 DAC67", NULL, "IF2 DAC7 Mux" }, { "IF3 DAC", NULL, "AIF3RX" }, { "IF3 DAC", NULL, "I2S3" }, diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 9d473b2..2979d5a 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -799,7 +799,7 @@ #define RT5677_PDM2_I2C_EXE (0x1 << 1) #define RT5677_PDM2_I2C_BUSY (0x1 << 0) -/* MX3B TDM1 control 1 (0x3b) */ +/* TDM1 control 1 (0x3b) */ #define RT5677_IF1_ADC_MODE_MASK (0x1 << 12) #define RT5677_IF1_ADC_MODE_SFT 12 #define RT5677_IF1_ADC_MODE_I2S (0x0 << 12) @@ -813,7 +813,7 @@ #define RT5677_IF1_ADC4_SWAP_MASK (0x3 << 0) #define RT5677_IF1_ADC4_SWAP_SFT 0 -/* MX3C TDM1 control 2 (0x3c) */ +/* TDM1 control 2 (0x3c) */ #define RT5677_IF1_ADC4_MASK (0x3 << 10) #define RT5677_IF1_ADC4_SFT 10 #define RT5677_IF1_ADC3_MASK (0x3 << 8) @@ -825,7 +825,27 @@ #define RT5677_IF1_ADC_CTRL_MASK (0x7 << 0) #define RT5677_IF1_ADC_CTRL_SFT 0 -/* MX40 TDM2 control 1 (0x40) */ +/* TDM1 control 4 (0x3e) */ +#define RT5677_IF1_DAC0_MASK (0x7 << 12) +#define RT5677_IF1_DAC0_SFT 12 +#define RT5677_IF1_DAC1_MASK (0x7 << 8) +#define RT5677_IF1_DAC1_SFT 8 +#define RT5677_IF1_DAC2_MASK (0x7 << 4) +#define RT5677_IF1_DAC2_SFT 4 +#define RT5677_IF1_DAC3_MASK (0x7 << 0) +#define RT5677_IF1_DAC3_SFT 0 + +/* TDM1 control 5 (0x3f) */ +#define RT5677_IF1_DAC4_MASK (0x7 << 12) +#define RT5677_IF1_DAC4_SFT 12 +#define RT5677_IF1_DAC5_MASK (0x7 << 8) +#define RT5677_IF1_DAC5_SFT 8 +#define RT5677_IF1_DAC6_MASK (0x7 << 4) +#define RT5677_IF1_DAC6_SFT 4 +#define RT5677_IF1_DAC7_MASK (0x7 << 0) +#define RT5677_IF1_DAC7_SFT 0 + +/* TDM2 control 1 (0x40) */ #define RT5677_IF2_ADC_MODE_MASK (0x1 << 12) #define RT5677_IF2_ADC_MODE_SFT 12 #define RT5677_IF2_ADC_MODE_I2S (0x0 << 12) @@ -839,7 +859,7 @@ #define RT5677_IF2_ADC4_SWAP_MASK (0x3 << 0) #define RT5677_IF2_ADC4_SWAP_SFT 0 -/* MX41 TDM2 control 2 (0x41) */ +/* TDM2 control 2 (0x41) */ #define RT5677_IF2_ADC4_MASK (0x3 << 10) #define RT5677_IF2_ADC4_SFT 10 #define RT5677_IF2_ADC3_MASK (0x3 << 8) @@ -851,6 +871,26 @@ #define RT5677_IF2_ADC_CTRL_MASK (0x7 << 0) #define RT5677_IF2_ADC_CTRL_SFT 0 +/* TDM2 control 4 (0x43) */ +#define RT5677_IF2_DAC0_MASK (0x7 << 12) +#define RT5677_IF2_DAC0_SFT 12 +#define RT5677_IF2_DAC1_MASK (0x7 << 8) +#define RT5677_IF2_DAC1_SFT 8 +#define RT5677_IF2_DAC2_MASK (0x7 << 4) +#define RT5677_IF2_DAC2_SFT 4 +#define RT5677_IF2_DAC3_MASK (0x7 << 0) +#define RT5677_IF2_DAC3_SFT 0 + +/* TDM2 control 5 (0x44) */ +#define RT5677_IF2_DAC4_MASK (0x7 << 12) +#define RT5677_IF2_DAC4_SFT 12 +#define RT5677_IF2_DAC5_MASK (0x7 << 8) +#define RT5677_IF2_DAC5_SFT 8 +#define RT5677_IF2_DAC6_MASK (0x7 << 4) +#define RT5677_IF2_DAC6_SFT 4 +#define RT5677_IF2_DAC7_MASK (0x7 << 0) +#define RT5677_IF2_DAC7_SFT 0 + /* Digital Microphone Control 1 (0x50) */ #define RT5677_DMIC_1_EN_MASK (0x1 << 15) #define RT5677_DMIC_1_EN_SFT 15 -- cgit v0.10.2 From a7a3324a602cd7ebabfb7f5990006ec4f3d6449f Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Wed, 12 Nov 2014 16:38:05 +0200 Subject: ASoC: davinci-mcasp: Add overrun/underrun event handling An underrun (playback) event occurs when the serializer transfer data from the XRBUF buffer to the XRSR shift register, but the XRBUF hasn't been filled. Similarly, the overrun (capture) event occurs when data from the XRSR shift register is transferred to the XRBUF but it hasn't been read yet. These events are handled as XRUN events that cause the pcm to stop. The stream has to be explicitly restarted by the userspace which ensures that after stopping/starting McASP the data transfer is aligned with DMA. The other possibility was to internally stop and start McASP without DMA even knowing about it. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index 60ca079..46bc982 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -32,7 +32,7 @@ Optional properties: - rx-num-evt : FIFO levels. - sram-size-playback : size of sram to be allocated during playback - sram-size-capture : size of sram to be allocated during capture -- interrupts : Interrupt numbers for McASP, currently not used by the driver +- interrupts : Interrupt numbers for McASP - interrupt-names : Known interrupt names are "tx" and "rx" - pinctrl-0: Should specify pin control group used for this controller. - pinctrl-names: Should contain only one value - "default", for more details diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index a9822c7..e460f97 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -70,6 +70,7 @@ struct davinci_mcasp { void __iomem *base; u32 fifo_base; struct device *dev; + struct snd_pcm_substream *substreams[2]; /* McASP specific data */ int tdm_slots; @@ -80,6 +81,7 @@ struct davinci_mcasp { u8 bclk_div; u16 bclk_lrclk_ratio; int streams; + u32 irq_request[2]; int sysclk_freq; bool bclk_master; @@ -185,6 +187,10 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) 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); + + /* enable receive IRQs */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); } static void mcasp_start_tx(struct davinci_mcasp *mcasp) @@ -214,6 +220,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); /* Release Frame Sync generator */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); + + /* enable transmit IRQs */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); } static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) @@ -228,6 +238,10 @@ static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) static void mcasp_stop_rx(struct davinci_mcasp *mcasp) { + /* disable IRQ sources */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); + /* * In synchronous mode stop the TX clocks if no other stream is * running @@ -249,6 +263,10 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) { u32 val = 0; + /* disable IRQ sources */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); + /* * In synchronous mode keep TX clocks running if the capture stream is * still running. @@ -276,6 +294,76 @@ static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) mcasp_stop_rx(mcasp); } +static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + struct snd_pcm_substream *substream; + u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]; + u32 handled_mask = 0; + u32 stat; + + stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG); + if (stat & XUNDRN & irq_mask) { + dev_warn(mcasp->dev, "Transmit buffer underflow\n"); + handled_mask |= XUNDRN; + + substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream) { + snd_pcm_stream_lock_irq(substream); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(substream); + } + } + + if (!handled_mask) + dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n", + stat); + + if (stat & XRERR) + handled_mask |= XRERR; + + /* Ack the handled event only */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask); + + return IRQ_RETVAL(handled_mask); +} + +static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + struct snd_pcm_substream *substream; + u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]; + u32 handled_mask = 0; + u32 stat; + + stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG); + if (stat & ROVRN & irq_mask) { + dev_warn(mcasp->dev, "Receive buffer overflow\n"); + handled_mask |= ROVRN; + + substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream) { + snd_pcm_stream_lock_irq(substream); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(substream); + } + } + + if (!handled_mask) + dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n", + stat); + + if (stat & XRERR) + handled_mask |= XRERR; + + /* Ack the handled event only */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask); + + return IRQ_RETVAL(handled_mask); +} + static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -869,6 +957,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, u32 max_channels = 0; int i, dir; + mcasp->substreams[substream->stream] = substream; + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) return 0; @@ -907,6 +997,8 @@ static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + mcasp->substreams[substream->stream] = NULL; + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) return; @@ -1256,6 +1348,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev) struct resource *mem, *ioarea, *res, *dat; struct davinci_mcasp_pdata *pdata; struct davinci_mcasp *mcasp; + char *irq_name; + int irq; int ret; if (!pdev->dev.platform_data && !pdev->dev.of_node) { @@ -1336,6 +1430,36 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->dev = &pdev->dev; + irq = platform_get_irq_byname(pdev, "rx"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n", + dev_name(&pdev->dev)); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_rx_irq_handler, + IRQF_ONESHOT, irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "RX IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; + } + + irq = platform_get_irq_byname(pdev, "tx"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n", + dev_name(&pdev->dev)); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_tx_irq_handler, + IRQF_ONESHOT, irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "TX IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; + } + dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); if (dat) mcasp->dat_port = true; diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 9737108..79dc511 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -256,6 +256,7 @@ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits */ +#define XRERR BIT(8) /* Transmit/Receive error */ #define XRDATA BIT(5) /* Transmit/Receive data ready */ /* @@ -285,6 +286,16 @@ #define TXDATADMADIS BIT(0) /* + * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits + */ +#define ROVRN BIT(0) + +/* + * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits + */ +#define XUNDRN BIT(0) + +/* * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits */ #define FIFO_ENABLE BIT(16) -- cgit v0.10.2 From ef326f4bb2675e9309ba318b19442d9823e58ee2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 12 Nov 2014 14:55:26 +0000 Subject: ASoC: arizona: Add support for 768kHz DMIC operation The new IPs supports a new lower frequency 768kHz DMIC operation add support for this into the OSR control. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 0c05e7a..786464f 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -648,7 +648,7 @@ SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum, EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum); static const char * const arizona_in_dmic_osr_text[] = { - "1.536MHz", "3.072MHz", "6.144MHz", + "1.536MHz", "3.072MHz", "6.144MHz", "768kHz", }; const struct soc_enum arizona_in_dmic_osr[] = { -- cgit v0.10.2 From e9c7f34a7eba13e1a53212246c607d13574f9eff Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 12 Nov 2014 16:12:46 +0000 Subject: ASoC: arizona: Add DSP_B and LEFT_J mode support Signed-off-by: Richard Fitzgerald Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 786464f..19887bf 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -61,6 +61,11 @@ #define ARIZONA_FLL_MIN_OUTDIV 2 #define ARIZONA_FLL_MAX_OUTDIV 7 +#define ARIZONA_FMT_DSP_MODE_A 0 +#define ARIZONA_FMT_DSP_MODE_B 1 +#define ARIZONA_FMT_I2S_MODE 2 +#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3 + #define arizona_fll_err(_fll, fmt, ...) \ dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_warn(_fll, fmt, ...) \ @@ -946,10 +951,26 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: - mode = 0; + mode = ARIZONA_FMT_DSP_MODE_A; + break; + case SND_SOC_DAIFMT_DSP_B: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) + != SND_SOC_DAIFMT_CBM_CFM) { + arizona_aif_err(dai, "DSP_B not valid in slave mode\n"); + return -EINVAL; + } + mode = ARIZONA_FMT_DSP_MODE_B; break; case SND_SOC_DAIFMT_I2S: - mode = 2; + mode = ARIZONA_FMT_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) + != SND_SOC_DAIFMT_CBM_CFM) { + arizona_aif_err(dai, "LEFT_J not valid in slave mode\n"); + return -EINVAL; + } + mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE; break; default: arizona_aif_err(dai, "Unsupported DAI format %d\n", @@ -1298,7 +1319,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, /* Force multiple of 2 channels for I2S mode */ val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT); - if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) { + val &= ARIZONA_AIF1_FMT_MASK; + if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) { arizona_aif_dbg(dai, "Forcing stereo mode\n"); bclk_target /= channels; bclk_target *= channels + 1; -- cgit v0.10.2 From caaeb6a96f35af14f615838b924f54c9d0f33ab3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 10 Nov 2014 20:00:40 +0100 Subject: ASoC: sh: fsi: Document SoC-specific bindings The documentation only mentioned the generic fallback compatible property. Add the missing SoC-specific compatible properties, some of which are already in use. Also fix a small typo, while we're at it. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt index c5be003..0d0ab51 100644 --- a/Documentation/devicetree/bindings/sound/renesas,fsi.txt +++ b/Documentation/devicetree/bindings/sound/renesas,fsi.txt @@ -1,11 +1,16 @@ Renesas FSI Required properties: -- compatible : "renesas,sh_fsi2" or "renesas,sh_fsi" +- compatible : "renesas,fsi2-", + "renesas,sh_fsi2" or "renesas,sh_fsi" as + fallback. + Examples with soctypes are: + - "renesas,fsi2-r8a7740" (R-Mobile A1) + - "renesas,fsi2-sh73a0" (SH-Mobile AG5) - reg : Should contain the register physical address and length - interrupts : Should contain FSI interrupt -- fsia,spdif-connection : FSI is connected by S/PDFI +- fsia,spdif-connection : FSI is connected by S/PDIF - fsia,stream-mode-support : FSI supports 16bit stream mode. - fsia,use-internal-clock : FSI uses internal clock when master mode. -- cgit v0.10.2 From 56ba98acc398883324c0e70dc8aee1dc53eb2331 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 10 Nov 2014 20:00:42 +0100 Subject: ASoC: rsnd: Document SoC-specific bindings The documentation only mentioned the generic fallback compatible property. Add the missing SoC-specific compatible properties, which are already in use. Also drop a bogus 0x unit-address prefix while we're at it. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index aa697ab..2dd690b 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -1,8 +1,12 @@ Renesas R-Car sound Required properties: -- compatible : "renesas,rcar_sound-gen1" if generation1 +- compatible : "renesas,rcar_sound-", fallbacks + "renesas,rcar_sound-gen1" if generation1, and "renesas,rcar_sound-gen2" if generation2 + Examples with soctypes are: + - "renesas,rcar_sound-r8a7790" (R-Car H2) + - "renesas,rcar_sound-r8a7791" (R-Car M2-W) - reg : Should contain the register physical address. required register is SRU/ADG/SSI if generation1 @@ -35,9 +39,9 @@ DAI subnode properties: Example: -rcar_sound: rcar_sound@0xffd90000 { +rcar_sound: rcar_sound@ec500000 { #sound-dai-cells = <1>; - compatible = "renesas,rcar_sound-gen2"; + compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2"; reg = <0 0xec500000 0 0x1000>, /* SCU */ <0 0xec5a0000 0 0x100>, /* ADG */ <0 0xec540000 0 0x1000>, /* SSIU */ -- cgit v0.10.2 From 850577db99dbc4fdebe62d30d380de1878f77d2a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 13 Nov 2014 09:55:22 +0800 Subject: ASoC: rt5645: add register setting for TDM We need to set extra register to avoid a recording issue in TDM mode. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 665f8b6..57afa12 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2112,8 +2112,11 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, struct snd_soc_codec *codec = dai->codec; unsigned int val = 0; - if (rx_mask || tx_mask) + if (rx_mask || tx_mask) { val |= (1 << 14); + snd_soc_update_bits(codec, RT5645_BASS_BACK, + RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB); + } switch (slots) { case 4: diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 82f681b..196daf0 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -1855,6 +1855,7 @@ #define RT5645_M_BB_HPF_R_SFT 6 #define RT5645_G_BB_BST_MASK (0x3f) #define RT5645_G_BB_BST_SFT 0 +#define RT5645_G_BB_BST_25DB 0x14 /* MP3 Plus Control 1 (0xd0) */ #define RT5645_M_MP3_L_MASK (0x1 << 15) -- cgit v0.10.2 From 9e6280cd44c1cceaaac921567ee8c5731b7cc72b Mon Sep 17 00:00:00 2001 From: Krishna Mohan Dani Date: Thu, 13 Nov 2014 17:44:21 +0530 Subject: ASoC: rt5631: Add device tree binding documentation Document the device tree binding for the ALC5631 codec and update vendor specific prefix for the Realtek. Signed-off-by: Krishna Mohan Dani Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/rt5631.txt b/Documentation/devicetree/bindings/sound/rt5631.txt new file mode 100644 index 0000000..92b986c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rt5631.txt @@ -0,0 +1,48 @@ +ALC5631/RT5631 audio CODEC + +This device supports I2C only. + +Required properties: + + - compatible : "realtek,alc5631" or "realtek,rt5631" + + - reg : the I2C address of the device. + +Pins on the device (for linking into audio routes): + + * SPK_OUT_R_P + * SPK_OUT_R_N + * SPK_OUT_L_P + * SPK_OUT_L_N + * HP_OUT_L + * HP_OUT_R + * AUX_OUT2_LP + * AUX_OUT2_RN + * AUX_OUT1_LP + * AUX_OUT1_RN + * AUX_IN_L_JD + * AUX_IN_R_JD + * MONO_IN_P + * MONO_IN_N + * MIC1_P + * MIC1_N + * MIC2_P + * MIC2_N + * MONO_OUT_P + * MONO_OUT_N + * MICBIAS1 + * MICBIAS2 + +Example: + +alc5631: alc5631@1a { + compatible = "realtek,alc5631"; + reg = <0x1a>; +}; + +or + +rt5631: rt5631@1a { + compatible = "realtek,rt5631"; + reg = <0x1a>; +}; -- cgit v0.10.2 From 86707f7fece1d3a34aeb1e9c7f2178fd5ff4e788 Mon Sep 17 00:00:00 2001 From: Krishna Mohan Dani Date: Thu, 13 Nov 2014 17:44:23 +0530 Subject: ASoC: rt5631: Adding the description of the codec Signed-off-by: Krishna Mohan Dani Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..7e43e97 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -487,7 +487,8 @@ config SND_SOC_RT286 depends on I2C config SND_SOC_RT5631 - tristate + tristate "Realtek ALC5631/RT5631 CODEC" + depends on I2C config SND_SOC_RT5640 tristate -- cgit v0.10.2 From 189c88ced169d5197c806828e275c6f063b1d499 Mon Sep 17 00:00:00 2001 From: Krishna Mohan Dani Date: Thu, 13 Nov 2014 17:44:24 +0530 Subject: ASoC: rt5631: Adding Device Tree compatibility to Realtek's ALC5631/RT5631 codec driver Signed-off-by: Krishna Mohan Dani Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 1ba27db..3b7d5e4 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1686,10 +1686,18 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = { static const struct i2c_device_id rt5631_i2c_id[] = { { "rt5631", 0 }, + { "alc5631", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); +static struct of_device_id rt5631_i2c_dt_ids[] = { + { .compatible = "realtek,rt5631"}, + { .compatible = "realtek,alc5631"}, + { } +}; +MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids); + static const struct regmap_config rt5631_regmap_config = { .reg_bits = 8, .val_bits = 16, @@ -1734,6 +1742,7 @@ static struct i2c_driver rt5631_i2c_driver = { .driver = { .name = "rt5631", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), }, .probe = rt5631_i2c_probe, .remove = rt5631_i2c_remove, -- cgit v0.10.2 From 0605815e7ec21e048febcebb691d7f0cc3bdc36c Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 14 Nov 2014 15:51:34 +0800 Subject: ASoC: rt5670 : Add ACPI match ID for Intel CHT/BSW platforms This patch adds the ACPI match ID for rt5670/5672 codec. So on Intel CherryTrail/Braswell platforms, the codec can be enumerated from ACPI and depends on ACPI to get platform-specific info and power saving. Signed-off-by: Mengdong Lin Reviewed-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 066b583..b0aabd4 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -2503,6 +2504,14 @@ static const struct i2c_device_id rt5670_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); +#ifdef CONFIG_ACPI +static struct acpi_device_id rt5670_acpi_match[] = { + { "10EC5670", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); +#endif + static int rt5670_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2691,6 +2700,7 @@ static struct i2c_driver rt5670_i2c_driver = { .driver = { .name = "rt5670", .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(rt5670_acpi_match), }, .probe = rt5670_i2c_probe, .remove = rt5670_i2c_remove, -- cgit v0.10.2 From 471f208af987a3741757c169c4e2ad984359000b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 14 Nov 2014 14:25:37 +0800 Subject: ASoC: rt5645: two jacks for hp and mic Some OS need headphone and microphone to be separated. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 57afa12..ef88b50 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2201,8 +2201,7 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int rt5645_jack_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *jack) +static int rt5645_jack_detect(struct snd_soc_codec *codec) { struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); int gpio_state, jack_type = 0; @@ -2245,19 +2244,19 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, snd_soc_dapm_sync(&codec->dapm); } - snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET); - + snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE); return 0; } int rt5645_set_jack_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *jack) + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack) { struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - rt5645->jack = jack; - - rt5645_jack_detect(codec, rt5645->jack); + rt5645->hp_jack = hp_jack; + rt5645->mic_jack = mic_jack; + rt5645_jack_detect(codec); return 0; } @@ -2268,7 +2267,7 @@ static void rt5645_jack_detect_work(struct work_struct *work) struct rt5645_priv *rt5645 = container_of(work, struct rt5645_priv, jack_detect_work.work); - rt5645_jack_detect(rt5645->codec, rt5645->jack); + rt5645_jack_detect(rt5645->codec); } static irqreturn_t rt5645_irq(int irq, void *data) diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 196daf0..c72220a 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2173,7 +2173,8 @@ struct rt5645_priv { struct rt5645_platform_data pdata; struct regmap *regmap; struct i2c_client *i2c; - struct snd_soc_jack *jack; + struct snd_soc_jack *hp_jack; + struct snd_soc_jack *mic_jack; struct delayed_work jack_detect_work; int sysclk; @@ -2188,6 +2189,6 @@ struct rt5645_priv { }; int rt5645_set_jack_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *jack); + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack); #endif /* __RT5645_H__ */ -- cgit v0.10.2 From 2880fc877971d6c14b0c76ac09744e3ff5b126d5 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Thu, 13 Nov 2014 11:18:29 -0800 Subject: ASoC: add TI ts3a227e headset chip driver The TS3A227E is an autonomous audio accessory detection and configuration switch that detects 3-pole or 4-pole audio accessories and configures internal switches to route the signals accordingly. This chip also has built-in support for the new button standard described in the Android "Wired audio headset specification" v1.0. These buttons will be reported on the jack as buttons 0-3 mapped to KEY_MEDIA, KEY_VOLUMEUP, KEY_VOLUMEDOWN, and KEY_VOICE_COMMAND. This will be added as an aux_dev and have the jack passed in from the machine driver. Signed-off-by: Dylan Reid Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/ts3a227e.txt b/Documentation/devicetree/bindings/sound/ts3a227e.txt new file mode 100644 index 0000000..e8bf23e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ts3a227e.txt @@ -0,0 +1,26 @@ +Texas Instruments TS3A227E +Autonomous Audio Accessory Detection and Configuration Switch + +The TS3A227E detect headsets of 3-ring and 4-ring standards and +switches automatically to route the microphone correctly. It also +handles key press detection in accordance with the Android audio +headset specification v1.0. + +Required properties: + + - compatible: Should contain "ti,ts3a227e". + - reg: The i2c address. Should contain <0x3b>. + - interrupt-parent: The parent interrupt controller + - interrupts: Interrupt number for /INT pin from the 227e + + +Examples: + + i2c { + ts3a227e@3b { + compatible = "ti,ts3a227e"; + reg = <0x3b>; + interrupt-parent = <&gpio>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + }; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..243ec86 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -109,6 +109,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C + select SND_SOC_TS3A227E if I2C select SND_SOC_TWL4030 if TWL4030_CORE select SND_SOC_TWL6040 if TWL6040_CORE select SND_SOC_UDA134X @@ -607,6 +608,10 @@ config SND_SOC_TLV320AIC3X config SND_SOC_TLV320DAC33 tristate +config SND_SOC_TS3A227E + tristate "TI Headset/Mic detect and keypress chip" + depends on I2C + config SND_SOC_TWL4030 select MFD_TWL4030_AUDIO tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5dce451..a1eb7ef 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -109,6 +109,7 @@ snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320dac33-objs := tlv320dac33.o +snd-soc-ts3a227e-objs := ts3a227e.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda134x-objs := uda134x.o @@ -282,6 +283,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o +obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c new file mode 100644 index 0000000..1d12057 --- /dev/null +++ b/sound/soc/codecs/ts3a227e.c @@ -0,0 +1,314 @@ +/* + * TS3A227E Autonomous Audio Accessory Detection and Configuration Switch + * + * Copyright (C) 2014 Google, 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct ts3a227e { + struct regmap *regmap; + struct snd_soc_jack *jack; + bool plugged; + bool mic_present; + unsigned int buttons_held; +}; + +/* Button values to be reported on the jack */ +static const int ts3a227e_buttons[] = { + SND_JACK_BTN_0, + SND_JACK_BTN_1, + SND_JACK_BTN_2, + SND_JACK_BTN_3, +}; + +#define TS3A227E_NUM_BUTTONS 4 +#define TS3A227E_JACK_MASK (SND_JACK_HEADPHONE | \ + SND_JACK_MICROPHONE | \ + SND_JACK_BTN_0 | \ + SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | \ + SND_JACK_BTN_3) + +/* TS3A227E registers */ +#define TS3A227E_REG_DEVICE_ID 0x00 +#define TS3A227E_REG_INTERRUPT 0x01 +#define TS3A227E_REG_KP_INTERRUPT 0x02 +#define TS3A227E_REG_INTERRUPT_DISABLE 0x03 +#define TS3A227E_REG_SETTING_1 0x04 +#define TS3A227E_REG_SETTING_2 0x05 +#define TS3A227E_REG_SETTING_3 0x06 +#define TS3A227E_REG_SWITCH_CONTROL_1 0x07 +#define TS3A227E_REG_SWITCH_CONTROL_2 0x08 +#define TS3A227E_REG_SWITCH_STATUS_1 0x09 +#define TS3A227E_REG_SWITCH_STATUS_2 0x0a +#define TS3A227E_REG_ACCESSORY_STATUS 0x0b +#define TS3A227E_REG_ADC_OUTPUT 0x0c +#define TS3A227E_REG_KP_THRESHOLD_1 0x0d +#define TS3A227E_REG_KP_THRESHOLD_2 0x0e +#define TS3A227E_REG_KP_THRESHOLD_3 0x0f + +/* TS3A227E_REG_INTERRUPT 0x01 */ +#define INS_REM_EVENT 0x01 +#define DETECTION_COMPLETE_EVENT 0x02 + +/* TS3A227E_REG_KP_INTERRUPT 0x02 */ +#define PRESS_MASK(idx) (0x01 << (2 * (idx))) +#define RELEASE_MASK(idx) (0x02 << (2 * (idx))) + +/* TS3A227E_REG_INTERRUPT_DISABLE 0x03 */ +#define INS_REM_INT_DISABLE 0x01 +#define DETECTION_COMPLETE_INT_DISABLE 0x02 +#define ADC_COMPLETE_INT_DISABLE 0x04 +#define INTB_DISABLE 0x08 + +/* TS3A227E_REG_SETTING_2 0x05 */ +#define KP_ENABLE 0x04 + +/* TS3A227E_REG_ACCESSORY_STATUS 0x0b */ +#define TYPE_3_POLE 0x01 +#define TYPE_4_POLE_OMTP 0x02 +#define TYPE_4_POLE_STANDARD 0x04 +#define JACK_INSERTED 0x08 +#define EITHER_MIC_MASK (TYPE_4_POLE_OMTP | TYPE_4_POLE_STANDARD) + +static const struct reg_default ts3a227e_reg_defaults[] = { + { TS3A227E_REG_DEVICE_ID, 0x10 }, + { TS3A227E_REG_INTERRUPT, 0x00 }, + { TS3A227E_REG_KP_INTERRUPT, 0x00 }, + { TS3A227E_REG_INTERRUPT_DISABLE, 0x08 }, + { TS3A227E_REG_SETTING_1, 0x23 }, + { TS3A227E_REG_SETTING_2, 0x00 }, + { TS3A227E_REG_SETTING_3, 0x0e }, + { TS3A227E_REG_SWITCH_CONTROL_1, 0x00 }, + { TS3A227E_REG_SWITCH_CONTROL_2, 0x00 }, + { TS3A227E_REG_SWITCH_STATUS_1, 0x0c }, + { TS3A227E_REG_SWITCH_STATUS_2, 0x00 }, + { TS3A227E_REG_ACCESSORY_STATUS, 0x00 }, + { TS3A227E_REG_ADC_OUTPUT, 0x00 }, + { TS3A227E_REG_KP_THRESHOLD_1, 0x20 }, + { TS3A227E_REG_KP_THRESHOLD_2, 0x40 }, + { TS3A227E_REG_KP_THRESHOLD_3, 0x68 }, +}; + +static bool ts3a227e_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TS3A227E_REG_DEVICE_ID ... TS3A227E_REG_KP_THRESHOLD_3: + return true; + default: + return false; + } +} + +static bool ts3a227e_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TS3A227E_REG_INTERRUPT_DISABLE ... TS3A227E_REG_SWITCH_CONTROL_2: + case TS3A227E_REG_KP_THRESHOLD_1 ... TS3A227E_REG_KP_THRESHOLD_3: + return true; + default: + return false; + } +} + +static bool ts3a227e_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TS3A227E_REG_INTERRUPT ... TS3A227E_REG_INTERRUPT_DISABLE: + case TS3A227E_REG_SETTING_2: + case TS3A227E_REG_SWITCH_STATUS_1 ... TS3A227E_REG_ADC_OUTPUT: + return true; + default: + return false; + } +} + +static void ts3a227e_jack_report(struct ts3a227e *ts3a227e) +{ + unsigned int i; + int report = 0; + + if (!ts3a227e->jack) + return; + + if (ts3a227e->plugged) + report = SND_JACK_HEADPHONE; + if (ts3a227e->mic_present) + report |= SND_JACK_MICROPHONE; + for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) { + if (ts3a227e->buttons_held & (1 << i)) + report |= ts3a227e_buttons[i]; + } + snd_soc_jack_report(ts3a227e->jack, report, TS3A227E_JACK_MASK); +} + +static void ts3a227e_new_jack_state(struct ts3a227e *ts3a227e, unsigned acc_reg) +{ + bool plugged, mic_present; + + plugged = !!(acc_reg & JACK_INSERTED); + mic_present = plugged && !!(acc_reg & EITHER_MIC_MASK); + + ts3a227e->plugged = plugged; + + if (mic_present != ts3a227e->mic_present) { + ts3a227e->mic_present = mic_present; + ts3a227e->buttons_held = 0; + if (mic_present) { + /* Enable key press detection. */ + regmap_update_bits(ts3a227e->regmap, + TS3A227E_REG_SETTING_2, + KP_ENABLE, KP_ENABLE); + } + } +} + +static irqreturn_t ts3a227e_interrupt(int irq, void *data) +{ + struct ts3a227e *ts3a227e = (struct ts3a227e *)data; + struct regmap *regmap = ts3a227e->regmap; + unsigned int int_reg, kp_int_reg, acc_reg, i; + + /* Check for plug/unplug. */ + regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg); + if (int_reg & (DETECTION_COMPLETE_EVENT | INS_REM_EVENT)) { + regmap_read(regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg); + ts3a227e_new_jack_state(ts3a227e, acc_reg); + } + + /* Report any key events. */ + regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg); + for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) { + if (kp_int_reg & PRESS_MASK(i)) + ts3a227e->buttons_held |= (1 << i); + if (kp_int_reg & RELEASE_MASK(i)) + ts3a227e->buttons_held &= ~(1 << i); + } + + ts3a227e_jack_report(ts3a227e); + + return IRQ_HANDLED; +} + +/** + * ts3a227e_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events 0-3 will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int ts3a227e_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component); + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ts3a227e->jack = jack; + ts3a227e_jack_report(ts3a227e); + + return 0; +} +EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect); + +static struct snd_soc_component_driver ts3a227e_soc_driver; + +static const struct regmap_config ts3a227e_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .max_register = TS3A227E_REG_KP_THRESHOLD_3, + .readable_reg = ts3a227e_readable_reg, + .writeable_reg = ts3a227e_writeable_reg, + .volatile_reg = ts3a227e_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ts3a227e_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults), +}; + +static int ts3a227e_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ts3a227e *ts3a227e; + struct device *dev = &i2c->dev; + int ret; + + ts3a227e = devm_kzalloc(&i2c->dev, sizeof(*ts3a227e), GFP_KERNEL); + if (ts3a227e == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, ts3a227e); + + ts3a227e->regmap = devm_regmap_init_i2c(i2c, &ts3a227e_regmap_config); + if (IS_ERR(ts3a227e->regmap)) + return PTR_ERR(ts3a227e->regmap); + + ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "TS3A227E", ts3a227e); + if (ret) { + dev_err(dev, "Cannot request irq %d (%d)\n", i2c->irq, ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, &ts3a227e_soc_driver, + NULL, 0); + if (ret) + return ret; + + /* Enable interrupts except for ADC complete. */ + regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_INTERRUPT_DISABLE, + INTB_DISABLE | ADC_COMPLETE_INT_DISABLE, + ADC_COMPLETE_INT_DISABLE); + + return 0; +} + +static const struct i2c_device_id ts3a227e_i2c_ids[] = { + { "ts3a227e", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids); + +static const struct of_device_id ts3a227e_of_match[] = { + { .compatible = "ti,ts3a227e", }, + { } +}; +MODULE_DEVICE_TABLE(of, ts3a227e_of_match); + +static struct i2c_driver ts3a227e_driver = { + .driver = { + .name = "ts3a227e", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ts3a227e_of_match), + }, + .probe = ts3a227e_i2c_probe, + .id_table = ts3a227e_i2c_ids, +}; +module_i2c_driver(ts3a227e_driver); + +MODULE_DESCRIPTION("ASoC ts3a227e driver"); +MODULE_AUTHOR("Dylan Reid "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ts3a227e.h b/sound/soc/codecs/ts3a227e.h new file mode 100644 index 0000000..e2acf9c --- /dev/null +++ b/sound/soc/codecs/ts3a227e.h @@ -0,0 +1,17 @@ +/* + * TS3A227E Autonous Audio Accessory Detection and Configureation Switch + * + * Copyright (C) 2014 Google, 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 _TS3A227E_H +#define _TS3A227E_H + +int ts3a227e_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack); + +#endif -- cgit v0.10.2 From 336cfbb05edf7b122ea927dad6c746608723eb25 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 11 Nov 2014 16:36:28 +0530 Subject: ASoC: Intel: mrfld- add ACPI module Add the last ACPI module support which also uses core module like the PCI part Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index c963a5d..a992e85 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -20,6 +20,11 @@ config SND_SST_IPC_PCI tristate select SND_SST_IPC +config SND_SST_IPC_ACPI + tristate + select SND_SST_IPC + depends on ACPI + config SND_SOC_INTEL_SST tristate "ASoC support for Intel(R) Smart Sound Technology" select SND_SOC_INTEL_SST_ACPI if ACPI diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile index b8aa1d3..fd21726 100644 --- a/sound/soc/intel/sst/Makefile +++ b/sound/soc/intel/sst/Makefile @@ -1,7 +1,7 @@ snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o snd-intel-sst-pci-objs += sst_pci.o - +snd-intel-sst-acpi-objs += sst_acpi.o obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o - +obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index b97c231..b2b5604 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -181,6 +182,7 @@ int sst_driver_ops(struct intel_sst_drv *sst) switch (sst->dev_id) { case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: sst->tstamp = SST_TIME_STAMP_MRFLD; sst->ops = &mrfld_ops; return 0; @@ -323,7 +325,7 @@ EXPORT_SYMBOL_GPL(sst_context_init); void sst_context_cleanup(struct intel_sst_drv *ctx) { pm_runtime_get_noresume(ctx->dev); - pm_runtime_forbid(ctx->dev); + pm_runtime_disable(ctx->dev); sst_unregister(ctx->dev); sst_set_fw_state_locked(ctx, SST_SHUTDOWN); flush_scheduled_work(); @@ -371,8 +373,19 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); pm_runtime_use_autosuspend(ctx->dev); - pm_runtime_allow(ctx->dev); - pm_runtime_put_noidle(ctx->dev); + /* + * For acpi devices, the actual physical device state is + * initially active. So change the state to active before + * enabling the pm + */ + if (acpi_disabled) { + pm_runtime_set_active(ctx->dev); + pm_runtime_enable(ctx->dev); + } else { + pm_runtime_allow(ctx->dev); + pm_runtime_put_noidle(ctx->dev); + } + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); } EXPORT_SYMBOL_GPL(sst_configure_runtime_pm); @@ -395,6 +408,9 @@ static int intel_sst_runtime_suspend(struct device *dev) synchronize_irq(ctx->irq_num); flush_workqueue(ctx->post_msg_wq); + /* save the shim registers because PMC doesn't save state */ + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); + return ret; } diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index 2dcbf47..683dc71 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -29,6 +29,7 @@ /* driver names */ #define SST_DRV_NAME "intel_sst_driver" #define SST_MRFLD_PCI_ID 0x119A +#define SST_BYT_ACPI_ID 0x80860F28 #define SST_SUSPEND_DELAY 2000 #define FW_CONTEXT_MEM (64*1024) diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c new file mode 100644 index 0000000..2b1c5d9 --- /dev/null +++ b/sound/soc/intel/sst/sst_acpi.c @@ -0,0 +1,362 @@ +/* + * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration. + * + * Copyright (c) 2013, Intel Corporation. + * + * Authors: Ramesh Babu K V + * Authors: Omair Mohammed Abdullah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "../sst-dsp.h" +#include "sst.h" + +struct sst_machines { + char codec_id[32]; + char board[32]; + char machine[32]; + void (*machine_quirk)(void); + char firmware[32]; + struct sst_platform_info *pdata; + +}; + +/* LPE viewpoint addresses */ +#define SST_BYT_IRAM_PHY_START 0xff2c0000 +#define SST_BYT_IRAM_PHY_END 0xff2d4000 +#define SST_BYT_DRAM_PHY_START 0xff300000 +#define SST_BYT_DRAM_PHY_END 0xff320000 +#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */ +#define SST_BYT_IMR_VIRT_END 0xc01fffff +#define SST_BYT_SHIM_PHY_ADDR 0xff340000 +#define SST_BYT_MBOX_PHY_ADDR 0xff344000 +#define SST_BYT_DMA0_PHY_ADDR 0xff298000 +#define SST_BYT_DMA1_PHY_ADDR 0xff29c000 +#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000 +#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000 + +#define BYT_FW_MOD_TABLE_OFFSET 0x80000 +#define BYT_FW_MOD_TABLE_SIZE 0x100 +#define BYT_FW_MOD_OFFSET (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE) + +static const struct sst_info byt_fwparse_info = { + .use_elf = false, + .max_streams = 25, + .iram_start = SST_BYT_IRAM_PHY_START, + .iram_end = SST_BYT_IRAM_PHY_END, + .iram_use = true, + .dram_start = SST_BYT_DRAM_PHY_START, + .dram_end = SST_BYT_DRAM_PHY_END, + .dram_use = true, + .imr_start = SST_BYT_IMR_VIRT_START, + .imr_end = SST_BYT_IMR_VIRT_END, + .imr_use = true, + .mailbox_start = SST_BYT_MBOX_PHY_ADDR, + .num_probes = 0, + .lpe_viewpt_rqd = true, +}; + +static const struct sst_ipc_info byt_ipc_info = { + .ipc_offset = 0, + .mbox_recv_off = 0x400, +}; + +static const struct sst_lib_dnld_info byt_lib_dnld_info = { + .mod_base = SST_BYT_IMR_VIRT_START, + .mod_end = SST_BYT_IMR_VIRT_END, + .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET, + .mod_table_size = BYT_FW_MOD_TABLE_SIZE, + .mod_ddr_dnld = false, +}; + +static const struct sst_res_info byt_rvp_res_info = { + .shim_offset = 0x140000, + .shim_size = 0x000100, + .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR, + .ssp0_offset = 0xa0000, + .ssp0_size = 0x1000, + .dma0_offset = 0x98000, + .dma0_size = 0x4000, + .dma1_offset = 0x9c000, + .dma1_size = 0x4000, + .iram_offset = 0x0c0000, + .iram_size = 0x14000, + .dram_offset = 0x100000, + .dram_size = 0x28000, + .mbox_offset = 0x144000, + .mbox_size = 0x1000, + .acpi_lpe_res_index = 0, + .acpi_ddr_index = 2, + .acpi_ipc_irq_index = 5, +}; + +struct sst_platform_info byt_rvp_platform_data = { + .probe_data = &byt_fwparse_info, + .ipc_info = &byt_ipc_info, + .lib_info = &byt_lib_dnld_info, + .res_info = &byt_rvp_res_info, + .platform = "sst-mfld-platform", +}; + +static int sst_platform_get_resources(struct intel_sst_drv *ctx) +{ + struct resource *rsrc; + struct platform_device *pdev = to_platform_device(ctx->dev); + + /* All ACPI resource request here */ + /* Get Shim addr */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, + ctx->pdata->res_info->acpi_lpe_res_index); + if (!rsrc) { + dev_err(ctx->dev, "Invalid SHIM base from IFWI"); + return -EIO; + } + dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start, + (unsigned int)resource_size(rsrc)); + + ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset; + ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1; + dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base); + ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, + ctx->pdata->res_info->iram_size); + if (!ctx->iram) { + dev_err(ctx->dev, "unable to map IRAM"); + return -EIO; + } + + ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset; + ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1; + dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base); + ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, + ctx->pdata->res_info->dram_size); + if (!ctx->dram) { + dev_err(ctx->dev, "unable to map DRAM"); + return -EIO; + } + + ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset; + dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add); + ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, + ctx->pdata->res_info->shim_size); + if (!ctx->shim) { + dev_err(ctx->dev, "unable to map SHIM"); + return -EIO; + } + + /* reassign physical address to LPE viewpoint address */ + ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr; + + /* Get mailbox addr */ + ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset; + dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add); + ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, + ctx->pdata->res_info->mbox_size); + if (!ctx->mailbox) { + dev_err(ctx->dev, "unable to map mailbox"); + return -EIO; + } + + /* reassign physical address to LPE viewpoint address */ + ctx->mailbox_add = ctx->info.mailbox_start; + + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, + ctx->pdata->res_info->acpi_ddr_index); + if (!rsrc) { + dev_err(ctx->dev, "Invalid DDR base from IFWI"); + return -EIO; + } + ctx->ddr_base = rsrc->start; + ctx->ddr_end = rsrc->end; + dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base); + ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, + resource_size(rsrc)); + if (!ctx->ddr) { + dev_err(ctx->dev, "unable to map DDR"); + return -EIO; + } + + /* Find the IRQ */ + ctx->irq_num = platform_get_irq(pdev, + ctx->pdata->res_info->acpi_ipc_irq_index); + return 0; +} + +static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + +static struct sst_machines *sst_acpi_find_machine( + struct sst_machines *machines) +{ + struct sst_machines *mach; + bool found = false; + + for (mach = machines; mach->codec_id; mach++) + if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id, + sst_acpi_mach_match, + &found, NULL)) && found) + return mach; + + return NULL; +} + +int sst_acpi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret = 0; + struct intel_sst_drv *ctx; + const struct acpi_device_id *id; + struct sst_machines *mach; + struct platform_device *mdev; + struct platform_device *plat_dev; + unsigned int dev_id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + dev_dbg(dev, "for %s", id->id); + + mach = (struct sst_machines *)id->driver_data; + mach = sst_acpi_find_machine(mach); + if (mach == NULL) { + dev_err(dev, "No matching machine driver found\n"); + return -ENODEV; + } + + ret = kstrtouint(id->id, 16, &dev_id); + if (ret < 0) { + dev_err(dev, "Unique device id conversion error: %d\n", ret); + return ret; + } + + dev_dbg(dev, "ACPI device id: %x\n", dev_id); + + plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0); + if (plat_dev == NULL) { + dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform); + return -ENODEV; + } + + /* Create platform device for sst machine driver */ + mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0); + if (mdev == NULL) { + dev_err(dev, "Failed to create machine device: %s\n", mach->machine); + return -ENODEV; + } + + ret = sst_alloc_drv_context(&ctx, dev, dev_id); + if (ret < 0) + return ret; + + /* Fill sst platform data */ + ctx->pdata = mach->pdata; + strcpy(ctx->firmware_name, mach->firmware); + + ret = sst_platform_get_resources(ctx); + if (ret) + return ret; + + ret = sst_context_init(ctx); + if (ret < 0) + return ret; + + /* need to save shim registers in BYT */ + ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), + GFP_KERNEL); + if (!ctx->shim_regs64) { + return -ENOMEM; + goto do_sst_cleanup; + } + + sst_configure_runtime_pm(ctx); + platform_set_drvdata(pdev, ctx); + return ret; + +do_sst_cleanup: + sst_context_cleanup(ctx); + platform_set_drvdata(pdev, NULL); + dev_err(ctx->dev, "failed with %d\n", ret); + return ret; +} + +/** +* intel_sst_remove - remove function +* +* @pdev: platform device structure +* +* This function is called by OS when a device is unloaded +* This frees the interrupt etc +*/ +int sst_acpi_remove(struct platform_device *pdev) +{ + struct intel_sst_drv *ctx; + + ctx = platform_get_drvdata(pdev); + sst_context_cleanup(ctx); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct sst_machines sst_acpi_bytcr[] = { + {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin", + &byt_rvp_platform_data }, + {}, +}; + +static const struct acpi_device_id sst_acpi_ids[] = { + { "80860F28", (unsigned long)&sst_acpi_bytcr}, + { }, +}; + +static struct platform_driver sst_acpi_driver = { + .driver = { + .name = "intel_sst_acpi", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(sst_acpi_ids), + .pm = &intel_sst_pm, + }, + .probe = sst_acpi_probe, + .remove = sst_acpi_remove, +}; + +module_platform_driver(sst_acpi_driver); + +MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver"); +MODULE_AUTHOR("Ramesh Babu K V"); +MODULE_AUTHOR("Omair Mohammed Abdullah"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("sst"); diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c index 9a5df19..4b77208 100644 --- a/sound/soc/intel/sst/sst_pvt.c +++ b/sound/soc/intel/sst/sst_pvt.c @@ -117,6 +117,7 @@ unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) switch (sst->dev_id) { case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: val = sst_shim_read64(sst->shim, addr); break; } @@ -128,6 +129,7 @@ void write_shim_data(struct intel_sst_drv *sst, int addr, { switch (sst->dev_id) { case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: sst_shim_write64(sst->shim, addr, (u64) data); break; } -- cgit v0.10.2 From 044b724ada4448174f3f7510b791df0bdcb834ee Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 12 Nov 2014 19:54:30 +0800 Subject: ASoC: rt5670: make bias level more reasonable This patah separate bias level off to standby and off. The standby level will provide the necessary power for JD and push button functions. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index b0aabd4..5e54ac9 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2310,6 +2310,8 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, static int rt5670_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { @@ -2331,16 +2333,27 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec, } break; case SND_SOC_BIAS_STANDBY: - snd_soc_write(codec, RT5670_PWR_DIG1, 0x0000); - snd_soc_write(codec, RT5670_PWR_DIG2, 0x0001); - snd_soc_write(codec, RT5670_PWR_VOL, 0x0000); - snd_soc_write(codec, RT5670_PWR_MIXER, 0x0001); - snd_soc_write(codec, RT5670_PWR_ANLG1, 0x2800); - snd_soc_write(codec, RT5670_PWR_ANLG2, 0x0004); - snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0); + snd_soc_update_bits(codec, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_VREF2 | + RT5670_PWR_FV1 | RT5670_PWR_FV2, 0); snd_soc_update_bits(codec, RT5670_PWR_ANLG1, RT5670_LDO_SEL_MASK, 0x1); break; + case SND_SOC_BIAS_OFF: + if (rt5670->pdata.jd_mode) + snd_soc_update_bits(codec, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2 | + RT5670_PWR_FV1 | RT5670_PWR_FV2, + RT5670_PWR_MB | RT5670_PWR_BG); + else + snd_soc_update_bits(codec, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2 | + RT5670_PWR_FV1 | RT5670_PWR_FV2, 0); + + snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0); + break; default: break; -- cgit v0.10.2 From cdcd7f7287532131d2075dd45f15aaf39dcfe983 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 14 Nov 2014 15:40:45 +0000 Subject: ASoC: wm_adsp: Use vmalloc to allocate firmware download buffer Use vmalloc to allocate the buffer for firmware/coefficient download and rely on the SPI core to split this up into DMA-able chunks. This should give better performance and means we no longer need to manually split the download into page size chunks to avoid allocating overly large continuous memory regions. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index f412a99..0a08ef5e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -169,11 +170,12 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, if (buf == NULL) return NULL; - buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); + buf->buf = vmalloc(len); if (!buf->buf) { - kfree(buf); + vfree(buf); return NULL; } + memcpy(buf->buf, src, len); if (list) list_add_tail(&buf->list, list); @@ -188,7 +190,7 @@ static void wm_adsp_buf_free(struct list_head *list) struct wm_adsp_buf, list); list_del(&buf->list); - kfree(buf->buf); + vfree(buf->buf); kfree(buf); } } @@ -684,38 +686,24 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - size_t to_write = PAGE_SIZE; - size_t remain = le32_to_cpu(region->len); - const u8 *data = region->data; - - while (remain > 0) { - if (remain < PAGE_SIZE) - to_write = remain; - - buf = wm_adsp_buf_alloc(data, - to_write, - &buf_list); - if (!buf) { - adsp_err(dsp, "Out of memory\n"); - ret = -ENOMEM; - goto out_fw; - } - - ret = regmap_raw_write_async(regmap, reg, - buf->buf, - to_write); - if (ret != 0) { - adsp_err(dsp, - "%s.%d: Failed to write %zd bytes at %d in %s: %d\n", - file, regions, - to_write, offset, - region_name, ret); - goto out_fw; - } + buf = wm_adsp_buf_alloc(region->data, + le32_to_cpu(region->len), + &buf_list); + if (!buf) { + adsp_err(dsp, "Out of memory\n"); + ret = -ENOMEM; + goto out_fw; + } - data += to_write; - reg += to_write / 2; - remain -= to_write; + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(region->len)); + if (ret != 0) { + adsp_err(dsp, + "%s.%d: Failed to write %d bytes at %d in %s: %d\n", + file, regions, + le32_to_cpu(region->len), offset, + region_name, ret); + goto out_fw; } } -- cgit v0.10.2 From 6fdaac1c1ab4fee1619145487c5aaf1bd44acc7b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 17 Nov 2014 09:37:34 +0100 Subject: ASoC: adav80x: Replace w->codec with snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index ce3cdca..b67480f 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = { static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { - struct snd_soc_codec *codec = source->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); const char *clk; @@ -236,7 +236,7 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source, static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { - struct snd_soc_codec *codec = source->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL; -- cgit v0.10.2 From de172051af78883a4a2e7897e7af58ba49353b99 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 17 Nov 2014 09:37:35 +0100 Subject: ASoC: adau1373: Replace w->codec with snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 7c784ad..783dcb5 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -551,7 +551,7 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = { static int adau1373_pll_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); unsigned int pll_id = w->name[3] - '1'; unsigned int val; @@ -823,7 +823,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = { static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { - struct snd_soc_codec *codec = source->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); unsigned int dai; const char *clk; @@ -844,7 +844,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source, static int adau1373_check_src(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { - struct snd_soc_codec *codec = source->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); unsigned int dai; -- cgit v0.10.2 From d69db7f7cd57fdfc6ac64c4c8679eb7b80c84fc7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 17 Nov 2014 09:37:36 +0100 Subject: ASoC: adau17x1: Replace w->codec with snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 5518ebd..3dddb28 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -255,7 +255,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control = static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct adau *adau = snd_soc_codec_get_drvdata(w->codec); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct adau *adau = snd_soc_codec_get_drvdata(codec); /* After any power changes have been made the dejitter circuit * has to be reinitialized. */ diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index e9fc00f..aa6a37c 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -174,7 +174,7 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = { static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct adau *adau = snd_soc_codec_get_drvdata(codec); /* After any power changes have been made the dejitter circuit diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 3e16c1c..427ad77 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -61,7 +61,8 @@ static const struct snd_kcontrol_new adau17x1_controls[] = { static int adau17x1_pll_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct adau *adau = snd_soc_codec_get_drvdata(w->codec); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; if (SND_SOC_DAPM_EVENT_ON(event)) { -- cgit v0.10.2 From eb826a35d2578106cf6fbfb2a83eedd1c0c2c415 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 17 Nov 2014 14:36:40 +0800 Subject: ASoC: Intel: add missing ACPI device table The ACPI device table will generate the driver module alias for Intel audio devices enumerated from ACPI. Signed-off-by: Mengdong Lin Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index 2b1c5d9..b261821 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -342,6 +342,8 @@ static const struct acpi_device_id sst_acpi_ids[] = { { }, }; +MODULE_DEVICE_TABLE(acpi, sst_acpi_ids); + static struct platform_driver sst_acpi_driver = { .driver = { .name = "intel_sst_acpi", -- cgit v0.10.2 From 6d3efa40790ad1286cfa032df6d3c9a2748a695e Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Sat, 15 Nov 2014 22:51:46 +0300 Subject: ASoC: pxa: prepare/unprepare clocks in pxa-ssp Change clk_enable/disable() calls to clk_prepare_enable() and clk_disable_unrepapre(). Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index a8e0974..cbba063 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -97,7 +97,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, int ret = 0; if (!cpu_dai->active) { - clk_enable(ssp->clk); + clk_prepare_enable(ssp->clk); pxa_ssp_disable(ssp); } @@ -121,7 +121,7 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream, if (!cpu_dai->active) { pxa_ssp_disable(ssp); - clk_disable(ssp->clk); + clk_disable_unprepare(ssp->clk); } kfree(snd_soc_dai_get_dma_data(cpu_dai, substream)); @@ -136,7 +136,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai) struct ssp_device *ssp = priv->ssp; if (!cpu_dai->active) - clk_enable(ssp->clk); + clk_prepare_enable(ssp->clk); priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0); priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1); @@ -144,7 +144,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai) priv->psp = __raw_readl(ssp->mmio_base + SSPSP); pxa_ssp_disable(ssp); - clk_disable(ssp->clk); + clk_disable_unprepare(ssp->clk); return 0; } @@ -154,7 +154,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) struct ssp_device *ssp = priv->ssp; uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE; - clk_enable(ssp->clk); + clk_prepare_enable(ssp->clk); __raw_writel(sssr, ssp->mmio_base + SSSR); __raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0); @@ -165,7 +165,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) if (cpu_dai->active) pxa_ssp_enable(ssp); else - clk_disable(ssp->clk); + clk_disable_unprepare(ssp->clk); return 0; } @@ -256,11 +256,11 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, /* The SSP clock must be disabled when changing SSP clock mode * on PXA2xx. On PXA3xx it must be enabled when doing so. */ if (ssp->type != PXA3xx_SSP) - clk_disable(ssp->clk); + clk_disable_unprepare(ssp->clk); val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0; pxa_ssp_write_reg(ssp, SSCR0, val); if (ssp->type != PXA3xx_SSP) - clk_enable(ssp->clk); + clk_prepare_enable(ssp->clk); return 0; } -- cgit v0.10.2 From 187024b36c635bd454c1b1587b58c9439d3a46ad Mon Sep 17 00:00:00 2001 From: Krishna Mohan Dani Date: Mon, 17 Nov 2014 19:26:29 +0530 Subject: ASoC: rt5631: Fixing compilation warning when DT is disabled Fixes the following compilation warning: Warning: 'rt5631_i2c_dt_ids' defined but not used - when DT is not used. Signed-off-by: Claude Youn Signed-off-by: Krishna Mohan Dani Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 3b7d5e4..9425545 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1691,12 +1691,14 @@ static const struct i2c_device_id rt5631_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); +#ifdef CONFIG_OF static struct of_device_id rt5631_i2c_dt_ids[] = { { .compatible = "realtek,rt5631"}, { .compatible = "realtek,alc5631"}, { } }; MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids); +#endif static const struct regmap_config rt5631_regmap_config = { .reg_bits = 8, -- cgit v0.10.2 From 86ae04b174152147052adec7b95dba0c9cd7dff0 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 17 Nov 2014 10:18:11 +0800 Subject: ASoC: rt5677: Modify the default value of the MX-8E[4] for ASRC function Modify the default value of the MX-8E[4] to 1 for ASRC function. It could prevent the pop noise with ASRC function. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 5d317c68..9ae2e84 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -55,7 +55,8 @@ static const struct regmap_range_cfg rt5677_ranges[] = { }; static const struct reg_default init_list[] = { - {RT5677_PR_BASE + 0x3d, 0x364d}, + {RT5677_ASRC_12, 0x0018}, + {RT5677_PR_BASE + 0x3d, 0x364d}, {RT5677_PR_BASE + 0x17, 0x4fc0}, {RT5677_PR_BASE + 0x13, 0x0312}, {RT5677_PR_BASE + 0x1e, 0x0000}, @@ -173,7 +174,7 @@ static const struct reg_default rt5677_reg[] = { {RT5677_ASRC_9 , 0x0000}, {RT5677_ASRC_10 , 0x0000}, {RT5677_ASRC_11 , 0x0000}, - {RT5677_ASRC_12 , 0x0008}, + {RT5677_ASRC_12 , 0x0018}, {RT5677_ASRC_13 , 0x0000}, {RT5677_ASRC_14 , 0x0000}, {RT5677_ASRC_15 , 0x0000}, -- cgit v0.10.2 From 7e35ac81598c9a98dee4faa77b988c4ea919d1cd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 17 Nov 2014 00:16:19 -0200 Subject: ASoC: fsl_ssi: Remove comment about SSI running only in slave mode Current driver can also run in I2S master mode, so remove the old comment. Signed-off-by: Fabio Estevam Acked-by: Timur Tabi Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index e695517..bc19849 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -67,8 +67,6 @@ /** * FSLSSI_I2S_FORMATS: audio formats supported by the SSI * - * This driver currently only supports the SSI running in I2S slave mode. - * * The SSI has a limitation in that the samples must be in the same byte * order as the host CPU. This is because when multiple bytes are written * to the STX register, the bytes and bits must be written in the same -- cgit v0.10.2 From bb66f2dc197d9cf1daaa82609302204d71c70389 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Nov 2014 14:05:27 +0100 Subject: ASoC: omap-mcbsp: Deletion of an unnecessary check before the function call "kfree" The kfree() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 86c7538..68a1252 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -621,8 +621,7 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp) mcbsp->reg_cache = NULL; spin_unlock(&mcbsp->lock); - if (reg_cache) - kfree(reg_cache); + kfree(reg_cache); } /* -- cgit v0.10.2 From bb29a93b38610d2adc6ead40b75e1a1991617550 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 12 Nov 2014 00:52:23 +0900 Subject: ASoC: jack: Fix warning while make htmldocs caused by soc-jack.c This patch fix following errors while "make htmldocs" on linux-next-20141110. Warning(.//sound/soc/soc-jack.c:126): No description found for parameter 'zones' Warning(.//sound/soc/soc-jack.c:126): Excess function parameter 'zone' description in 'snd_soc_jack_add_zones' Signed-off-by: Masanari Iida Signed-off-by: Mark Brown diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ab47fea..ef1d42d 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_report); * * @jack: ASoC jack * @count: Number of zones - * @zone: Array of zones + * @zones: Array of zones * * After this function has been called the zones specified in the * array will be associated with the jack. -- cgit v0.10.2 From 35480e3536cdab1ee1976675e798f16d707f5356 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:43 +0100 Subject: ASoC: mpc5200_psc_ac97: Remove unused on-stack snd_ac97 device The mpc5200_psc_ac97 driver puts a snd_ac97 device on the stack in the driver probe function, initializes the private data member of the device and the never uses the device again. It should be safe to remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 24eafa2..640801a 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -282,7 +282,6 @@ static const struct snd_soc_component_driver psc_ac97_component = { static int psc_ac97_of_probe(struct platform_device *op) { int rc; - struct snd_ac97 ac97; struct mpc52xx_psc __iomem *regs; rc = mpc5200_audio_dma_create(op); @@ -304,7 +303,6 @@ static int psc_ac97_of_probe(struct platform_device *op) psc_dma = dev_get_drvdata(&op->dev); regs = psc_dma->psc_regs; - ac97.private_data = psc_dma; psc_dma->imr = 0; out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); -- cgit v0.10.2 From 65c72efd1ea370f0311a5d89754996fff9fc0747 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:44 +0100 Subject: ASoC: mpc5200_dma: Don't overwrite ac97 device private_data The mpc5200_dma overwrites the private_data field of the CODEC's AC'97 device with the DMA drivers private data, but never actually reads it again. Given that the private_data field is supposed to be owned by the AC'97 driver, overwriting it may cause undefined behavior. This patch removes the code that overwrites the field from the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index f2b5d75..0b82e20 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd) goto capture_alloc_err; } - if (rtd->codec->ac97) - rtd->codec->ac97->private_data = psc_dma; - return 0; capture_alloc_err: -- cgit v0.10.2 From 70f3af3ca15affaef3d026a5aa6e44c4627ea6c7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:45 +0100 Subject: ASoC: Properly handle AC'97 device lifetime management The memory that a struct device is contained in must not be freed except from within the device's release callback. The ASoC code currently does not adhere to this rule for the AC'97 device. This patch fixes it by moving the freeing of the AC'97 to the release callback and splitting up the registration and unregistration of the device into separate steps for getting/putting the reference to the device and adding/removing it to the device hierarchy. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a2..7084c6f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -504,13 +504,10 @@ EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) { if (codec->ac97->dev.bus) - device_unregister(&codec->ac97->dev); + device_del(&codec->ac97->dev); return 0; } -/* stop no dev release warning */ -static void soc_ac97_device_release(struct device *dev){} - /* register ac97 codec to bus */ static int soc_ac97_dev_register(struct snd_soc_codec *codec) { @@ -518,12 +515,11 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) codec->ac97->dev.bus = &ac97_bus_type; codec->ac97->dev.parent = codec->component.card->dev; - codec->ac97->dev.release = soc_ac97_device_release; dev_set_name(&codec->ac97->dev, "%d-%d:%s", codec->component.card->snd_card->number, 0, codec->component.name); - err = device_register(&codec->ac97->dev); + err = device_add(&codec->ac97->dev); if (err < 0) { dev_err(codec->dev, "ASoC: Can't register ac97 bus\n"); codec->ac97->dev.bus = NULL; @@ -1948,6 +1944,11 @@ static struct platform_driver soc_driver = { .remove = soc_remove, }; +static void soc_ac97_device_release(struct device *dev) +{ + kfree(to_ac97_t(dev)); +} + /** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec @@ -1972,12 +1973,14 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, codec->ac97->bus->ops = ops; codec->ac97->num = num; + codec->ac97->dev.release = soc_ac97_device_release; /* * Mark the AC97 device to be created by us. This way we ensure that the * device will be registered with the device subsystem later on. */ codec->ac97_created = 1; + device_initialize(&codec->ac97->dev); return 0; } @@ -2152,7 +2155,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) soc_unregister_ac97_codec(codec); #endif kfree(codec->ac97->bus); - kfree(codec->ac97); + codec->ac97->bus = NULL; + put_device(&codec->ac97->dev); codec->ac97 = NULL; codec->ac97_created = 0; } -- cgit v0.10.2 From 336b8423e285174ebecf02a743d69913b83bbc48 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:46 +0100 Subject: ASoC: Move AC'97 support to its own file Currently the AC'97 support is splattered all throughout soc-core.c. Some parts are #ifdef'd some parts are not. This patch moves the AC'97 support to its own file, this should make the code a bit more clearer and also makes it possible to easily not compile it into the kernel when not needed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ba7130..adef34f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -366,8 +366,6 @@ struct snd_soc_jack_gpio; typedef int (*hw_write_t)(void *,const char* ,int); -extern struct snd_ac97_bus_ops *soc_ac97_ops; - enum snd_soc_pcm_subclass { SND_SOC_PCM_CLASS_PCM = 0, SND_SOC_PCM_CLASS_BE = 1, @@ -500,6 +498,7 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec, int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value); +#ifdef CONFIG_SND_SOC_AC97_BUS int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, struct snd_ac97_bus_ops *ops, int num); void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); @@ -508,6 +507,31 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, struct platform_device *pdev); +extern struct snd_ac97_bus_ops *soc_ac97_ops; + +int snd_soc_ac97_register_dai_links(struct snd_soc_card *card); +void snd_soc_ac97_add_pdata(struct snd_soc_pcm_runtime *rtd); +#else + +static inline int snd_soc_ac97_register_dai_links(struct snd_soc_card *card) +{ + return 0; +} + +static inline void snd_soc_ac97_add_pdata(struct snd_soc_pcm_runtime *rtd) {} + +static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev) +{ + return 0; +} + +static inline int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) +{ + return 0; +} +#endif + /* *Controls */ diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 534714a..0fded1b 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -5,6 +5,10 @@ ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o endif +ifneq ($(CONFIG_SND_SOC_AC97_BUS),) +snd-soc-core-objs += soc-ac97.o +endif + obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c new file mode 100644 index 0000000..da7b031 --- /dev/null +++ b/sound/soc/soc-ac97.c @@ -0,0 +1,382 @@ +/* + * soc-ac97.c -- ALSA SoC Audio Layer AC97 support + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * Copyright (C) 2010 Slimlogic Ltd. + * Copyright (C) 2010 Texas Instruments Inc. + * + * Author: Liam Girdwood + * with code, comments and ideas from :- + * Richard Purdie + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_ac97_reset_cfg { + struct pinctrl *pctl; + struct pinctrl_state *pstate_reset; + struct pinctrl_state *pstate_warm_reset; + struct pinctrl_state *pstate_run; + int gpio_sdata; + int gpio_sync; + int gpio_reset; +}; + +/* unregister ac97 codec */ +static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) +{ + if (codec->ac97->dev.bus) + device_del(&codec->ac97->dev); + return 0; +} + +/* register ac97 codec to bus */ +static int soc_ac97_dev_register(struct snd_soc_codec *codec) +{ + int err; + + codec->ac97->dev.bus = &ac97_bus_type; + codec->ac97->dev.parent = codec->component.card->dev; + + dev_set_name(&codec->ac97->dev, "%d-%d:%s", + codec->component.card->snd_card->number, 0, + codec->component.name); + err = device_add(&codec->ac97->dev); + if (err < 0) { + dev_err(codec->dev, "ASoC: Can't register ac97 bus\n"); + codec->ac97->dev.bus = NULL; + return err; + } + return 0; +} + +static int soc_register_ac97_codec(struct snd_soc_codec *codec, + struct snd_soc_dai *codec_dai) +{ + int ret; + + /* Only instantiate AC97 if not already done by the adaptor + * for the generic AC97 subsystem. + */ + if (codec_dai->driver->ac97_control && !codec->ac97_registered) { + /* + * It is possible that the AC97 device is already registered to + * the device subsystem. This happens when the device is created + * via snd_ac97_mixer(). Currently only SoC codec that does so + * is the generic AC97 glue but others migh emerge. + * + * In those cases we don't try to register the device again. + */ + if (!codec->ac97_created) + return 0; + + ret = soc_ac97_dev_register(codec); + if (ret < 0) { + dev_err(codec->dev, + "ASoC: AC97 device register failed: %d\n", ret); + return ret; + } + + codec->ac97_registered = 1; + } + return 0; +} + +static void soc_unregister_ac97_codec(struct snd_soc_codec *codec) +{ + if (codec->ac97_registered) { + soc_ac97_dev_unregister(codec); + codec->ac97_registered = 0; + } +} + +static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) +{ + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + ret = soc_register_ac97_codec(codec_dai->codec, codec_dai); + if (ret) { + while (--i >= 0) + soc_unregister_ac97_codec(codec_dai->codec); + return ret; + } + } + + return 0; +} + +static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) +{ + int i; + + for (i = 0; i < rtd->num_codecs; i++) + soc_unregister_ac97_codec(rtd->codec_dais[i]->codec); +} + +static void soc_ac97_device_release(struct device *dev) +{ + kfree(to_ac97_t(dev)); +} + +/** + * snd_soc_new_ac97_codec - initailise AC97 device + * @codec: audio codec + * @ops: AC97 bus operations + * @num: AC97 codec number + * + * Initialises AC97 codec resources for use by ad-hoc devices only. + */ +int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, + struct snd_ac97_bus_ops *ops, int num) +{ + codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); + if (codec->ac97 == NULL) + return -ENOMEM; + + codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL); + if (codec->ac97->bus == NULL) { + kfree(codec->ac97); + codec->ac97 = NULL; + return -ENOMEM; + } + + codec->ac97->bus->ops = ops; + codec->ac97->num = num; + codec->ac97->dev.release = soc_ac97_device_release; + + /* + * Mark the AC97 device to be created by us. This way we ensure that the + * device will be registered with the device subsystem later on. + */ + codec->ac97_created = 1; + device_initialize(&codec->ac97->dev); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); + +/** + * snd_soc_free_ac97_codec - free AC97 codec device + * @codec: audio codec + * + * Frees AC97 codec device resources. + */ +void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) +{ + soc_unregister_ac97_codec(codec); + kfree(codec->ac97->bus); + codec->ac97->bus = NULL; + put_device(&codec->ac97->dev); + codec->ac97 = NULL; + codec->ac97_created = 0; +} +EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); + +static struct snd_ac97_reset_cfg snd_ac97_rst_cfg; + +static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static void snd_soc_ac97_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static int snd_soc_ac97_parse_pinctl(struct device *dev, + struct snd_ac97_reset_cfg *cfg) +{ + struct pinctrl *p; + struct pinctrl_state *state; + int gpio; + int ret; + + p = devm_pinctrl_get(dev); + if (IS_ERR(p)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_ERR(p); + } + cfg->pctl = p; + + state = pinctrl_lookup_state(p, "ac97-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-reset\n"); + return PTR_ERR(state); + } + cfg->pstate_reset = state; + + state = pinctrl_lookup_state(p, "ac97-warm-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n"); + return PTR_ERR(state); + } + cfg->pstate_warm_reset = state; + + state = pinctrl_lookup_state(p, "ac97-running"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-running\n"); + return PTR_ERR(state); + } + cfg->pstate_run = state; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sync gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sync"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sync gpio\n"); + return ret; + } + cfg->gpio_sync = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sdata gpio\n"); + return ret; + } + cfg->gpio_sdata = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-reset gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link reset"); + if (ret) { + dev_err(dev, "Failed requesting ac97-reset gpio\n"); + return ret; + } + cfg->gpio_reset = gpio; + + return 0; +} + +struct snd_ac97_bus_ops *soc_ac97_ops; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) +{ + if (ops == soc_ac97_ops) + return 0; + + if (soc_ac97_ops && ops) + return -EBUSY; + + soc_ac97_ops = ops; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops); + +/** + * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions + * + * This function sets the reset and warm_reset properties of ops and parses + * the device node of pdev to get pinctrl states and gpio numbers to use. + */ +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_ac97_reset_cfg cfg; + int ret; + + ret = snd_soc_ac97_parse_pinctl(dev, &cfg); + if (ret) + return ret; + + ret = snd_soc_set_ac97_ops(ops); + if (ret) + return ret; + + ops->warm_reset = snd_soc_ac97_warm_reset; + ops->reset = snd_soc_ac97_reset; + + snd_ac97_rst_cfg = cfg; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); + +int snd_soc_ac97_register_dai_links(struct snd_soc_card *card) +{ + int i; + int ret; + + /* register any AC97 codecs */ + for (i = 0; i < card->num_rtd; i++) { + ret = soc_register_ac97_dai_link(&card->rtd[i]); + if (ret < 0) + goto err; + } + + return 0; +err: + dev_err(card->dev, + "ASoC: failed to register AC97: %d\n", ret); + while (--i >= 0) + soc_unregister_ac97_dai_link(&card->rtd[i]); + return ret; +} + +void snd_soc_ac97_add_pdata(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int i; + + /* add platform data for AC97 devices */ + for (i = 0; i < rtd->num_codecs; i++) { + if (rtd->codec_dais[i]->driver->ac97_control) + snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97, + rtd->cpu_dai->ac97_pdata); + } +} diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7084c6f..026722f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -34,9 +34,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -69,16 +66,6 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); -struct snd_ac97_reset_cfg { - struct pinctrl *pctl; - struct pinctrl_state *pstate_reset; - struct pinctrl_state *pstate_warm_reset; - struct pinctrl_state *pstate_run; - int gpio_sdata; - int gpio_sync; - int gpio_reset; -}; - /* returns the minimum number of bytes needed to represent * a particular given value */ static int min_bytes_needed(unsigned long val) @@ -499,36 +486,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); -#ifdef CONFIG_SND_SOC_AC97_BUS -/* unregister ac97 codec */ -static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) -{ - if (codec->ac97->dev.bus) - device_del(&codec->ac97->dev); - return 0; -} - -/* register ac97 codec to bus */ -static int soc_ac97_dev_register(struct snd_soc_codec *codec) -{ - int err; - - codec->ac97->dev.bus = &ac97_bus_type; - codec->ac97->dev.parent = codec->component.card->dev; - - dev_set_name(&codec->ac97->dev, "%d-%d:%s", - codec->component.card->snd_card->number, 0, - codec->component.name); - err = device_add(&codec->ac97->dev); - if (err < 0) { - dev_err(codec->dev, "ASoC: Can't register ac97 bus\n"); - codec->ac97->dev.bus = NULL; - return err; - } - return 0; -} -#endif - static void codec2codec_close_delayed_work(struct work_struct *work) { /* Currently nothing to do for c2c links @@ -1418,84 +1375,11 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } } - /* add platform data for AC97 devices */ - for (i = 0; i < rtd->num_codecs; i++) { - if (rtd->codec_dais[i]->driver->ac97_control) - snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97, - rtd->cpu_dai->ac97_pdata); - } - - return 0; -} - -#ifdef CONFIG_SND_SOC_AC97_BUS -static int soc_register_ac97_codec(struct snd_soc_codec *codec, - struct snd_soc_dai *codec_dai) -{ - int ret; - - /* Only instantiate AC97 if not already done by the adaptor - * for the generic AC97 subsystem. - */ - if (codec_dai->driver->ac97_control && !codec->ac97_registered) { - /* - * It is possible that the AC97 device is already registered to - * the device subsystem. This happens when the device is created - * via snd_ac97_mixer(). Currently only SoC codec that does so - * is the generic AC97 glue but others migh emerge. - * - * In those cases we don't try to register the device again. - */ - if (!codec->ac97_created) - return 0; - - ret = soc_ac97_dev_register(codec); - if (ret < 0) { - dev_err(codec->dev, - "ASoC: AC97 device register failed: %d\n", ret); - return ret; - } - - codec->ac97_registered = 1; - } - return 0; -} - -static void soc_unregister_ac97_codec(struct snd_soc_codec *codec) -{ - if (codec->ac97_registered) { - soc_ac97_dev_unregister(codec); - codec->ac97_registered = 0; - } -} - -static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) -{ - int i, ret; - - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - - ret = soc_register_ac97_codec(codec_dai->codec, codec_dai); - if (ret) { - while (--i >= 0) - soc_unregister_ac97_codec(codec_dai->codec); - return ret; - } - } + snd_soc_ac97_add_pdata(rtd); return 0; } -static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) -{ - int i; - - for (i = 0; i < rtd->num_codecs; i++) - soc_unregister_ac97_codec(rtd->codec_dais[i]->codec); -} -#endif - static int soc_bind_aux_dev(struct snd_soc_card *card, int num) { struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num]; @@ -1789,19 +1673,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) goto probe_aux_dev_err; } -#ifdef CONFIG_SND_SOC_AC97_BUS - /* register any AC97 codecs */ - for (i = 0; i < card->num_rtd; i++) { - ret = soc_register_ac97_dai_link(&card->rtd[i]); - if (ret < 0) { - dev_err(card->dev, - "ASoC: failed to register AC97: %d\n", ret); - while (--i >= 0) - soc_unregister_ac97_dai_link(&card->rtd[i]); - goto probe_aux_dev_err; - } - } -#endif + ret = snd_soc_ac97_register_dai_links(card); + if (ret < 0) + goto probe_aux_dev_err; card->instantiated = 1; snd_soc_dapm_sync(&card->dapm); @@ -1944,224 +1818,6 @@ static struct platform_driver soc_driver = { .remove = soc_remove, }; -static void soc_ac97_device_release(struct device *dev) -{ - kfree(to_ac97_t(dev)); -} - -/** - * snd_soc_new_ac97_codec - initailise AC97 device - * @codec: audio codec - * @ops: AC97 bus operations - * @num: AC97 codec number - * - * Initialises AC97 codec resources for use by ad-hoc devices only. - */ -int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, - struct snd_ac97_bus_ops *ops, int num) -{ - codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); - if (codec->ac97 == NULL) - return -ENOMEM; - - codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL); - if (codec->ac97->bus == NULL) { - kfree(codec->ac97); - codec->ac97 = NULL; - return -ENOMEM; - } - - codec->ac97->bus->ops = ops; - codec->ac97->num = num; - codec->ac97->dev.release = soc_ac97_device_release; - - /* - * Mark the AC97 device to be created by us. This way we ensure that the - * device will be registered with the device subsystem later on. - */ - codec->ac97_created = 1; - device_initialize(&codec->ac97->dev); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); - -static struct snd_ac97_reset_cfg snd_ac97_rst_cfg; - -static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) -{ - struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; - - pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); - - gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); - - udelay(10); - - gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); - - pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); - msleep(2); -} - -static void snd_soc_ac97_reset(struct snd_ac97 *ac97) -{ - struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; - - pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); - - gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); - gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); - gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); - - udelay(10); - - gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); - - pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); - msleep(2); -} - -static int snd_soc_ac97_parse_pinctl(struct device *dev, - struct snd_ac97_reset_cfg *cfg) -{ - struct pinctrl *p; - struct pinctrl_state *state; - int gpio; - int ret; - - p = devm_pinctrl_get(dev); - if (IS_ERR(p)) { - dev_err(dev, "Failed to get pinctrl\n"); - return PTR_ERR(p); - } - cfg->pctl = p; - - state = pinctrl_lookup_state(p, "ac97-reset"); - if (IS_ERR(state)) { - dev_err(dev, "Can't find pinctrl state ac97-reset\n"); - return PTR_ERR(state); - } - cfg->pstate_reset = state; - - state = pinctrl_lookup_state(p, "ac97-warm-reset"); - if (IS_ERR(state)) { - dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n"); - return PTR_ERR(state); - } - cfg->pstate_warm_reset = state; - - state = pinctrl_lookup_state(p, "ac97-running"); - if (IS_ERR(state)) { - dev_err(dev, "Can't find pinctrl state ac97-running\n"); - return PTR_ERR(state); - } - cfg->pstate_run = state; - - gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); - if (gpio < 0) { - dev_err(dev, "Can't find ac97-sync gpio\n"); - return gpio; - } - ret = devm_gpio_request(dev, gpio, "AC97 link sync"); - if (ret) { - dev_err(dev, "Failed requesting ac97-sync gpio\n"); - return ret; - } - cfg->gpio_sync = gpio; - - gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); - if (gpio < 0) { - dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); - return gpio; - } - ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); - if (ret) { - dev_err(dev, "Failed requesting ac97-sdata gpio\n"); - return ret; - } - cfg->gpio_sdata = gpio; - - gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); - if (gpio < 0) { - dev_err(dev, "Can't find ac97-reset gpio\n"); - return gpio; - } - ret = devm_gpio_request(dev, gpio, "AC97 link reset"); - if (ret) { - dev_err(dev, "Failed requesting ac97-reset gpio\n"); - return ret; - } - cfg->gpio_reset = gpio; - - return 0; -} - -struct snd_ac97_bus_ops *soc_ac97_ops; -EXPORT_SYMBOL_GPL(soc_ac97_ops); - -int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) -{ - if (ops == soc_ac97_ops) - return 0; - - if (soc_ac97_ops && ops) - return -EBUSY; - - soc_ac97_ops = ops; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops); - -/** - * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions - * - * This function sets the reset and warm_reset properties of ops and parses - * the device node of pdev to get pinctrl states and gpio numbers to use. - */ -int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, - struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct snd_ac97_reset_cfg cfg; - int ret; - - ret = snd_soc_ac97_parse_pinctl(dev, &cfg); - if (ret) - return ret; - - ret = snd_soc_set_ac97_ops(ops); - if (ret) - return ret; - - ops->warm_reset = snd_soc_ac97_warm_reset; - ops->reset = snd_soc_ac97_reset; - - snd_ac97_rst_cfg = cfg; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); - -/** - * snd_soc_free_ac97_codec - free AC97 codec device - * @codec: audio codec - * - * Frees AC97 codec device resources. - */ -void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) -{ -#ifdef CONFIG_SND_SOC_AC97_BUS - soc_unregister_ac97_codec(codec); -#endif - kfree(codec->ac97->bus); - codec->ac97->bus = NULL; - put_device(&codec->ac97->dev); - codec->ac97 = NULL; - codec->ac97_created = 0; -} -EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); - /** * snd_soc_cnew - create new control * @_template: control template -- cgit v0.10.2 From eda1a701fd9589b6ed15b109558bd4f6202e3829 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:47 +0100 Subject: ASoC: ac97: Use static ac97_bus We always pass soc_ac97_ops to snd_soc_new_ac97_codec(). So instead of allocating a snd_ac97_bus in snd_soc_new_ac97_codec() just use a static one that gets initialized when snd_soc_set_ac97_ops() is called. Also drop the device number parameter from snd_soc_new_ac97_codec(). We currently only support one device per bus and all drivers pass 0 for the device number. And if we should ever support multiple devices per bus it wouldn't be up to individual AC'97 device drivers to pick their number, but rather either the AC'97 adapter driver or the core code will assign them. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index adef34f..44b3ce5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -499,8 +499,7 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value); #ifdef CONFIG_SND_SOC_AC97_BUS -int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, - struct snd_ac97_bus_ops *ops, int num); +int snd_soc_new_ac97_codec(struct snd_soc_codec *codec); void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 9ed4e12..f71cc21 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -220,7 +220,7 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) u16 vendor_id2; u16 ext_status; - ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); + ret = snd_soc_new_ac97_codec(codec); if (ret < 0) { dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 53b810d..45ac4a7 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -336,7 +336,7 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec) { int ret = 0; - ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); + ret = snd_soc_new_ac97_codec(codec); if (ret < 0) goto codec_err; diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 1650195..2cb8a31 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -337,7 +337,7 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) { int ret = 0; - ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); + ret = snd_soc_new_ac97_codec(codec); if (ret < 0) { dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 3fad37e..6b36223 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -666,7 +666,7 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) { int ret = 0; - ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); + ret = snd_soc_new_ac97_codec(codec); if (ret < 0) { dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 998e4c7..2071df7 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1219,7 +1219,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) { int ret = 0, reg; - ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); + ret = snd_soc_new_ac97_codec(codec); if (ret < 0) return ret; diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index da7b031..dbfca7e 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -38,6 +38,10 @@ struct snd_ac97_reset_cfg { int gpio_reset; }; +static struct snd_ac97_bus soc_ac97_bus = { + .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */ +}; + /* unregister ac97 codec */ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) { @@ -140,27 +144,17 @@ static void soc_ac97_device_release(struct device *dev) /** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec - * @ops: AC97 bus operations - * @num: AC97 codec number * * Initialises AC97 codec resources for use by ad-hoc devices only. */ -int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, - struct snd_ac97_bus_ops *ops, int num) +int snd_soc_new_ac97_codec(struct snd_soc_codec *codec) { codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); if (codec->ac97 == NULL) return -ENOMEM; - codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL); - if (codec->ac97->bus == NULL) { - kfree(codec->ac97); - codec->ac97 = NULL; - return -ENOMEM; - } - - codec->ac97->bus->ops = ops; - codec->ac97->num = num; + codec->ac97->bus = &soc_ac97_bus; + codec->ac97->num = 0; codec->ac97->dev.release = soc_ac97_device_release; /* @@ -183,7 +177,6 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) { soc_unregister_ac97_codec(codec); - kfree(codec->ac97->bus); codec->ac97->bus = NULL; put_device(&codec->ac97->dev); codec->ac97 = NULL; @@ -314,6 +307,7 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) return -EBUSY; soc_ac97_ops = ops; + soc_ac97_bus.ops = ops; return 0; } -- cgit v0.10.2 From bdfd60e3c0affb914549f1d22e8aeef71e7828e6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:48 +0100 Subject: ASoC: ac97: Merge soc_ac97_dev_{un,}register()/soc_{un,}register_ac97_codec() soc_{un,}register_ac97_codec() is just a simple wrapper around soc_ac97_dev_{un,}register(). There is no need to split these up into two different sets of functions. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index dbfca7e..b5d23c9 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -42,18 +42,28 @@ static struct snd_ac97_bus soc_ac97_bus = { .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */ }; -/* unregister ac97 codec */ -static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) -{ - if (codec->ac97->dev.bus) - device_del(&codec->ac97->dev); - return 0; -} - /* register ac97 codec to bus */ -static int soc_ac97_dev_register(struct snd_soc_codec *codec) +static int soc_register_ac97_codec(struct snd_soc_codec *codec, + struct snd_soc_dai *codec_dai) { - int err; + int ret; + + /* Only instantiate AC97 if not already done by the adaptor + * for the generic AC97 subsystem. + */ + if (!codec_dai->driver->ac97_control || codec->ac97_registered) + return 0; + + /* + * It is possible that the AC97 device is already registered to + * the device subsystem. This happens when the device is created + * via snd_ac97_mixer(). Currently only SoC codec that does so + * is the generic AC97 glue but others migh emerge. + * + * In those cases we don't try to register the device again. + */ + if (!codec->ac97_created) + return 0; codec->ac97->dev.bus = &ac97_bus_type; codec->ac97->dev.parent = codec->component.card->dev; @@ -61,53 +71,23 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) dev_set_name(&codec->ac97->dev, "%d-%d:%s", codec->component.card->snd_card->number, 0, codec->component.name); - err = device_add(&codec->ac97->dev); - if (err < 0) { - dev_err(codec->dev, "ASoC: Can't register ac97 bus\n"); - codec->ac97->dev.bus = NULL; - return err; + ret = device_add(&codec->ac97->dev); + if (ret < 0) { + dev_err(codec->dev, "ASoC: AC97 device register failed: %d\n", + ret); + return ret; } - return 0; -} - -static int soc_register_ac97_codec(struct snd_soc_codec *codec, - struct snd_soc_dai *codec_dai) -{ - int ret; + codec->ac97_registered = 1; - /* Only instantiate AC97 if not already done by the adaptor - * for the generic AC97 subsystem. - */ - if (codec_dai->driver->ac97_control && !codec->ac97_registered) { - /* - * It is possible that the AC97 device is already registered to - * the device subsystem. This happens when the device is created - * via snd_ac97_mixer(). Currently only SoC codec that does so - * is the generic AC97 glue but others migh emerge. - * - * In those cases we don't try to register the device again. - */ - if (!codec->ac97_created) - return 0; - - ret = soc_ac97_dev_register(codec); - if (ret < 0) { - dev_err(codec->dev, - "ASoC: AC97 device register failed: %d\n", ret); - return ret; - } - - codec->ac97_registered = 1; - } return 0; } static void soc_unregister_ac97_codec(struct snd_soc_codec *codec) { - if (codec->ac97_registered) { - soc_ac97_dev_unregister(codec); - codec->ac97_registered = 0; - } + if (!codec->ac97_registered) + return; + device_del(&codec->ac97->dev); + codec->ac97_registered = 0; } static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) -- cgit v0.10.2 From ca005f324ee38308b319c693f40523d959027acf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:49 +0100 Subject: ASoC: ac97: Drop support for setting platform data via the CPU DAI This has no users since commit f0fba2ad1b6b ("ASoC: multi-component - ASoC Multi-Component Support") which was almost 5 years ago. Given that this runs after CODEC probe functions have been run it also doesn't seem to be that useful. So drop it altogether to make the code simpler. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index e8b3080..c0e0468 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -241,7 +241,6 @@ struct snd_soc_dai { const char *name; int id; struct device *dev; - void *ac97_pdata; /* platform_data for the ac97 codec */ /* driver ops */ struct snd_soc_dai_driver *driver; diff --git a/include/sound/soc.h b/include/sound/soc.h index 44b3ce5..5b4dec6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -509,7 +509,6 @@ int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, extern struct snd_ac97_bus_ops *soc_ac97_ops; int snd_soc_ac97_register_dai_links(struct snd_soc_card *card); -void snd_soc_ac97_add_pdata(struct snd_soc_pcm_runtime *rtd); #else static inline int snd_soc_ac97_register_dai_links(struct snd_soc_card *card) @@ -517,8 +516,6 @@ static inline int snd_soc_ac97_register_dai_links(struct snd_soc_card *card) return 0; } -static inline void snd_soc_ac97_add_pdata(struct snd_soc_pcm_runtime *rtd) {} - static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, struct platform_device *pdev) { diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index b5d23c9..f2ed77b 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -342,15 +342,3 @@ err: soc_unregister_ac97_dai_link(&card->rtd[i]); return ret; } - -void snd_soc_ac97_add_pdata(struct snd_soc_pcm_runtime *rtd) -{ - unsigned int i; - - /* add platform data for AC97 devices */ - for (i = 0; i < rtd->num_codecs; i++) { - if (rtd->codec_dais[i]->driver->ac97_control) - snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97, - rtd->cpu_dai->ac97_pdata); - } -} diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 026722f..d883b4a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1375,8 +1375,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } } - snd_soc_ac97_add_pdata(rtd); - return 0; } -- cgit v0.10.2 From 6794f709b7124ff1e574c4f4c9494418ab56c4b4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:50 +0100 Subject: ASoC: ac97: Drop delayed device registration We have all the information and dependencies we need to initialize and register the device available in snd_soc_new_ac97_codec(). So there is no need to delay the device registration until after the card itself as been registered. This makes the code significantly simpler and also makes it possible to use the AC'97 device in the CODECs probe function. The later will be required to be able to convert the AC'97 CODEC drivers to regmap. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 5b4dec6..206cc8d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -507,15 +507,7 @@ int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, struct platform_device *pdev); extern struct snd_ac97_bus_ops *soc_ac97_ops; - -int snd_soc_ac97_register_dai_links(struct snd_soc_card *card); #else - -static inline int snd_soc_ac97_register_dai_links(struct snd_soc_card *card) -{ - return 0; -} - static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, struct platform_device *pdev) { @@ -808,8 +800,6 @@ struct snd_soc_codec { struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int cache_bypass:1; /* Suppress access to the cache */ unsigned int suspended:1; /* Codec is in suspend PM state */ - unsigned int ac97_registered:1; /* Codec has been AC97 registered */ - unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int cache_init:1; /* codec cache has been initialized */ u32 cache_sync; /* Cache needs to be synced to hardware */ diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index f2ed77b..920d76c 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -42,80 +42,6 @@ static struct snd_ac97_bus soc_ac97_bus = { .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */ }; -/* register ac97 codec to bus */ -static int soc_register_ac97_codec(struct snd_soc_codec *codec, - struct snd_soc_dai *codec_dai) -{ - int ret; - - /* Only instantiate AC97 if not already done by the adaptor - * for the generic AC97 subsystem. - */ - if (!codec_dai->driver->ac97_control || codec->ac97_registered) - return 0; - - /* - * It is possible that the AC97 device is already registered to - * the device subsystem. This happens when the device is created - * via snd_ac97_mixer(). Currently only SoC codec that does so - * is the generic AC97 glue but others migh emerge. - * - * In those cases we don't try to register the device again. - */ - if (!codec->ac97_created) - return 0; - - codec->ac97->dev.bus = &ac97_bus_type; - codec->ac97->dev.parent = codec->component.card->dev; - - dev_set_name(&codec->ac97->dev, "%d-%d:%s", - codec->component.card->snd_card->number, 0, - codec->component.name); - ret = device_add(&codec->ac97->dev); - if (ret < 0) { - dev_err(codec->dev, "ASoC: AC97 device register failed: %d\n", - ret); - return ret; - } - codec->ac97_registered = 1; - - return 0; -} - -static void soc_unregister_ac97_codec(struct snd_soc_codec *codec) -{ - if (!codec->ac97_registered) - return; - device_del(&codec->ac97->dev); - codec->ac97_registered = 0; -} - -static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) -{ - int i, ret; - - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - - ret = soc_register_ac97_codec(codec_dai->codec, codec_dai); - if (ret) { - while (--i >= 0) - soc_unregister_ac97_codec(codec_dai->codec); - return ret; - } - } - - return 0; -} - -static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) -{ - int i; - - for (i = 0; i < rtd->num_codecs; i++) - soc_unregister_ac97_codec(rtd->codec_dais[i]->codec); -} - static void soc_ac97_device_release(struct device *dev) { kfree(to_ac97_t(dev)); @@ -129,22 +55,28 @@ static void soc_ac97_device_release(struct device *dev) */ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec) { + int ret; + codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); if (codec->ac97 == NULL) return -ENOMEM; codec->ac97->bus = &soc_ac97_bus; codec->ac97->num = 0; + + codec->ac97->dev.bus = &ac97_bus_type; + codec->ac97->dev.parent = codec->component.card->dev; codec->ac97->dev.release = soc_ac97_device_release; - /* - * Mark the AC97 device to be created by us. This way we ensure that the - * device will be registered with the device subsystem later on. - */ - codec->ac97_created = 1; - device_initialize(&codec->ac97->dev); + dev_set_name(&codec->ac97->dev, "%d-%d:%s", + codec->component.card->snd_card->number, 0, + codec->component.name); + + ret = device_register(&codec->ac97->dev); + if (ret) + put_device(&codec->ac97->dev); - return 0; + return ret; } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); @@ -156,11 +88,10 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); */ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) { - soc_unregister_ac97_codec(codec); + device_del(&codec->ac97->dev); codec->ac97->bus = NULL; put_device(&codec->ac97->dev); codec->ac97 = NULL; - codec->ac97_created = 0; } EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); @@ -321,24 +252,3 @@ int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, return 0; } EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); - -int snd_soc_ac97_register_dai_links(struct snd_soc_card *card) -{ - int i; - int ret; - - /* register any AC97 codecs */ - for (i = 0; i < card->num_rtd; i++) { - ret = soc_register_ac97_dai_link(&card->rtd[i]); - if (ret < 0) - goto err; - } - - return 0; -err: - dev_err(card->dev, - "ASoC: failed to register AC97: %d\n", ret); - while (--i >= 0) - soc_unregister_ac97_dai_link(&card->rtd[i]); - return ret; -} diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d883b4a..fba6e28 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1671,10 +1671,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) goto probe_aux_dev_err; } - ret = snd_soc_ac97_register_dai_links(card); - if (ret < 0) - goto probe_aux_dev_err; - card->instantiated = 1; snd_soc_dapm_sync(&card->dapm); mutex_unlock(&card->mutex); -- cgit v0.10.2 From 4bafcf074aca3bd191e4d93c6a140ca52654f192 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:51 +0100 Subject: ASoC: Drop ac97_control initialization from CODEC driver DAIs This is no longer necessary as there is no code anymore that uses this for CODEC DAIs. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index bd9b183..5d90924 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -53,7 +53,6 @@ static const struct snd_soc_dai_ops ac97_dai_ops = { static struct snd_soc_dai_driver ac97_dai = { .name = "ac97-hifi", - .ac97_control = 1, .playback = { .stream_name = "AC97 Playback", .channels_min = 1, diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index f71cc21..c6cb101 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -170,7 +170,6 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, static struct snd_soc_dai_driver ad1980_dai = { .name = "ad1980-hifi", - .ac97_control = 1, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 45ac4a7..c080806 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -294,7 +294,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = { static struct snd_soc_dai_driver stac9766_dai[] = { { .name = "stac9766-hifi-analog", - .ac97_control = 1, /* stream cababilities */ .playback = { @@ -316,7 +315,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = { }, { .name = "stac9766-hifi-IEC958", - .ac97_control = 1, /* stream cababilities */ .playback = { diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 2cb8a31..5b5118b 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -263,7 +263,6 @@ static const struct snd_soc_dai_ops wm9705_dai_ops = { static struct snd_soc_dai_driver wm9705_dai[] = { { .name = "wm9705-hifi", - .ac97_control = 1, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 6b36223..9fa794b 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -565,7 +565,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = { static struct snd_soc_dai_driver wm9712_dai[] = { { .name = "wm9712-hifi", - .ac97_control = 1, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 2071df7..cd1b266 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1076,7 +1076,6 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = { static struct snd_soc_dai_driver wm9713_dai[] = { { .name = "wm9713-hifi", - .ac97_control = 1, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, -- cgit v0.10.2 From bc2632140435cc84f9817f1c362479b23dbdfebc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:52 +0100 Subject: ASoC: Rename snd_soc_dai_driver struct ac97_control field to bus_control Setting the ac97_control field on a CPU DAI tells the ASoC core that this DAI in addition to audio data also transports control data to the CODEC. This causes the core to suspend the DAI after the CODEC and resume it before the CODEC so communication to the CODEC is still possible. This is not necessarily something that is specific to AC'97 and can be used by other buses with the same requirement. This patch renames the flag from ac97_control to bus_control to make this explicit. While we are at it also change the type from int to bool. The following semantich patch was used for automatic conversion of the drivers: // @@ identifier drv; @@ struct snd_soc_dai_driver drv = { - .ac97_control + .bus_control = - 1 + true }; // Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index c0e0468..a3738be 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -206,7 +206,6 @@ struct snd_soc_dai_driver { /* DAI description */ const char *name; unsigned int id; - int ac97_control; unsigned int base; /* DAI driver callbacks */ @@ -216,6 +215,8 @@ struct snd_soc_dai_driver { int (*resume)(struct snd_soc_dai *dai); /* compress dai */ bool compress_dai; + /* DAI is also used for the control bus */ + bool bus_control; /* ops */ const struct snd_soc_dai_ops *ops; diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c index c8a2de1..5159a50 100644 --- a/sound/soc/au1x/ac97c.c +++ b/sound/soc/au1x/ac97c.c @@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver au1xac97c_dai_driver = { .name = "alchemy-ac97c", - .ac97_control = 1, + .bus_control = true, .probe = au1xac97c_dai_probe, .playback = { .rates = AC97_RATES, diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 84f31e1..c6daec9 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { }; static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = { - .ac97_control = 1, + .bus_control = true, .probe = au1xpsc_ac97_probe, .playback = { .rates = AC97_RATES, diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index e82eb37..6bf21a6 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai) #endif static struct snd_soc_dai_driver bfin_ac97_dai = { - .ac97_control = 1, + .bus_control = true, .suspend = bf5xx_ac97_suspend, .resume = bf5xx_ac97_resume, .playback = { diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index f30dadf..6b8a366 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = { static struct snd_soc_dai_driver ep93xx_ac97_dai = { .name = "ep93xx-ac97", .id = 0, - .ac97_control = 1, + .bus_control = true, .probe = ep93xx_ac97_dai_probe, .playback = { .stream_name = "AC97 Playback", diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index e695517..7fd3cbc 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1099,7 +1099,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = { }; static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index ab2fdd7..60b0a5b 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = { static struct snd_soc_dai_driver imx_ac97_dai = { .probe = imx_ssi_dai_probe, - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 640801a..c6ed6ba 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = { static struct snd_soc_dai_driver psc_ac97_dai[] = { { .name = "mpc5200-psc-ac97.0", - .ac97_control = 1, + .bus_control = true, .probe = psc_ac97_probe, .playback = { .stream_name = "AC97 Playback", @@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = { }, { .name = "mpc5200-psc-ac97.1", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 SPDIF", .channels_min = 1, diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c index f2f67942..dff443e 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = { static struct snd_soc_dai_driver nuc900_ac97_dai = { .probe = nuc900_ac97_probe, .remove = nuc900_ac97_remove, - .ac97_control = 1, + .bus_control = true, .playback = { .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index ae956e3..73ca282 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { { .name = "pxa2xx-ac97", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { }, { .name = "pxa2xx-ac97-aux", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Aux Playback", .channels_min = 1, @@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { }, { .name = "pxa2xx-ac97-mic", - .ac97_control = 1, + .bus_control = true, .capture = { .stream_name = "AC97 Mic Capture", .channels_min = 1, diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index e161511..7952a62 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver s3c_ac97_dai[] = { [S3C_AC97_DAI_PCM] = { .name = "samsung-ac97", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = { }, [S3C_AC97_DAI_MIC] = { .name = "samsung-ac97-mic", - .ac97_control = 1, + .bus_control = true, .capture = { .stream_name = "AC97 Mic Capture", .channels_min = 1, diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index 0af2e4d..d5f567e 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c @@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = { static struct snd_soc_dai_driver sh4_hac_dai[] = { { .name = "hac-dai.0", - .ac97_control = 1, + .bus_control = true, .playback = { .rates = AC97_RATES, .formats = AC97_FMTS, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fba6e28..f5bebca 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -550,7 +550,7 @@ int snd_soc_suspend(struct device *dev) if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control) + if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control) cpu_dai->driver->suspend(cpu_dai); if (platform->driver->suspend && !platform->suspended) { platform->driver->suspend(cpu_dai); @@ -629,7 +629,7 @@ int snd_soc_suspend(struct device *dev) if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control) + if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control) cpu_dai->driver->suspend(cpu_dai); /* deactivate pins to sleep state */ @@ -665,14 +665,14 @@ static void soc_resume_deferred(struct work_struct *work) if (card->resume_pre) card->resume_pre(card); - /* resume AC97 DAIs */ + /* resume control bus DAIs */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control) + if (cpu_dai->driver->resume && cpu_dai->driver->bus_control) cpu_dai->driver->resume(cpu_dai); } @@ -733,7 +733,7 @@ static void soc_resume_deferred(struct work_struct *work) if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control) + if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control) cpu_dai->driver->resume(cpu_dai); if (platform->driver->resume && platform->suspended) { platform->driver->resume(cpu_dai); @@ -758,7 +758,8 @@ static void soc_resume_deferred(struct work_struct *work) int snd_soc_resume(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - int i, ac97_control = 0; + bool bus_control = false; + int i; /* If the card is not initialized yet there is nothing to do */ if (!card->instantiated) @@ -781,17 +782,18 @@ int snd_soc_resume(struct device *dev) } } - /* AC97 devices might have other drivers hanging off them so - * need to resume immediately. Other drivers don't have that - * problem and may take a substantial amount of time to resume + /* + * DAIs that also act as the control bus master might have other drivers + * hanging off them so need to resume immediately. Other drivers don't + * have that problem and may take a substantial amount of time to resume * due to I/O costs and anti-pop so handle them out of line. */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - ac97_control |= cpu_dai->driver->ac97_control; + bus_control |= cpu_dai->driver->bus_control; } - if (ac97_control) { - dev_dbg(dev, "ASoC: Resuming AC97 immediately\n"); + if (bus_control) { + dev_dbg(dev, "ASoC: Resuming control bus master immediately\n"); soc_resume_deferred(&card->deferred_resume_work); } else { dev_dbg(dev, "ASoC: Scheduling resume work\n"); diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 3b0fa12..29a9957 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver tegra20_ac97_dai = { .name = "tegra-ac97-pcm", - .ac97_control = 1, + .bus_control = true, .probe = tegra20_ac97_probe, .playback = { .stream_name = "PCM Playback", diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index 9edd68d..f7135cd 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai) } static struct snd_soc_dai_driver txx9aclc_ac97_dai = { - .ac97_control = 1, + .bus_control = true, .probe = txx9aclc_ac97_probe, .remove = txx9aclc_ac97_remove, .playback = { -- cgit v0.10.2 From 358a8bb5628420529e4f0b77068155ca8fa8973b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:53 +0100 Subject: ASoC: ac97: Push snd_ac97 pointer to the driver level Now that the ASoC core no longer needs a handle to the AC'97 device that is associated with a CODEC we can remove it from the snd_soc_codec struct and push it into the individual driver state structs like we do for other communication buses. Doing so creates a clean separation between the AC'97 bus support and the ASoC core. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 206cc8d..9e513ae 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -499,8 +499,8 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value); #ifdef CONFIG_SND_SOC_AC97_BUS -int snd_soc_new_ac97_codec(struct snd_soc_codec *codec); -void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); +struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec); +void snd_soc_free_ac97_codec(struct snd_ac97 *ac97); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, @@ -797,7 +797,6 @@ struct snd_soc_codec { struct list_head card_list; /* runtime */ - struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int cache_bypass:1; /* Suppress access to the cache */ unsigned int suspended:1; /* Codec is in suspend PM state */ unsigned int cache_init:1; /* codec cache has been initialized */ diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 5d90924..c6e5a31 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -37,10 +37,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; - return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate); + return snd_ac97_set_rate(ac97, reg, substream->runtime->rate); } #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ @@ -70,6 +71,7 @@ static struct snd_soc_dai_driver ac97_dai = { static int ac97_soc_probe(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97; struct snd_ac97_bus *ac97_bus; struct snd_ac97_template ac97_template; int ret; @@ -81,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec) return ret; memset(&ac97_template, 0, sizeof(struct snd_ac97_template)); - ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97); + ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97); if (ret < 0) return ret; + snd_soc_codec_set_drvdata(codec, ac97); + return 0; } #ifdef CONFIG_PM static int ac97_soc_suspend(struct snd_soc_codec *codec) { - snd_ac97_suspend(codec->ac97); + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + snd_ac97_suspend(ac97); return 0; } static int ac97_soc_resume(struct snd_soc_codec *codec) { - snd_ac97_resume(codec->ac97); + + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + snd_ac97_resume(ac97); return 0; } diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index c6cb101..93bd47d 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -135,6 +135,7 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = { static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; switch (reg) { @@ -144,7 +145,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, case AC97_EXTENDED_STATUS: case AC97_VENDOR_ID1: case AC97_VENDOR_ID2: - return soc_ac97_ops->read(codec->ac97, reg); + return soc_ac97_ops->read(ac97, reg); default: reg = reg >> 1; @@ -158,9 +159,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(ac97, reg, val); reg = reg >> 1; if (reg < ARRAY_SIZE(ad1980_reg)) cache[reg] = val; @@ -186,16 +188,17 @@ static struct snd_soc_dai_driver ad1980_dai = { static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); unsigned int retry_cnt = 0; do { if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(codec->ac97); + soc_ac97_ops->warm_reset(ac97); if (ac97_read(codec, AC97_RESET) == 0x0090) return 1; } - soc_ac97_ops->reset(codec->ac97); + soc_ac97_ops->reset(ac97); /* * Set bit 16slot in register 74h, then every slot will has only * 16 bits. This command is sent out in 20bit mode, in which @@ -215,16 +218,20 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) static int ad1980_soc_probe(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97; int ret; u16 vendor_id2; u16 ext_status; - ret = snd_soc_new_ac97_codec(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to register AC97 codec\n"); + ac97 = snd_soc_new_ac97_codec(codec); + if (IS_ERR(ac97)) { + ret = PTR_ERR(ac97); + dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); return ret; } + snd_soc_codec_set_drvdata(codec, ac97); + ret = ad1980_reset(codec, 0); if (ret < 0) goto reset_err; @@ -261,13 +268,15 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) return 0; reset_err: - snd_soc_free_ac97_codec(codec); + snd_soc_free_ac97_codec(ac97); return ret; } static int ad1980_soc_remove(struct snd_soc_codec *codec) { - snd_soc_free_ac97_codec(codec); + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + snd_soc_free_ac97_codec(ac97); return 0; } diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index c080806..f37a79e 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -139,18 +139,19 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = { static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; if (reg > AC97_STAC_PAGE0) { stac9766_ac97_write(codec, AC97_INT_PAGING, 0); - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(ac97, reg, val); stac9766_ac97_write(codec, AC97_INT_PAGING, 1); return 0; } if (reg / 2 >= ARRAY_SIZE(stac9766_reg)) return -EIO; - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(ac97, reg, val); cache[reg / 2] = val; return 0; } @@ -158,11 +159,12 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg, static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec, unsigned int reg) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); u16 val = 0, *cache = codec->reg_cache; if (reg > AC97_STAC_PAGE0) { stac9766_ac97_write(codec, AC97_INT_PAGING, 0); - val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0); + val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0); stac9766_ac97_write(codec, AC97_INT_PAGING, 1); return val; } @@ -173,7 +175,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec, reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2) { - val = soc_ac97_ops->read(codec->ac97, reg); + val = soc_ac97_ops->read(ac97, reg); return val; } return cache[reg / 2]; @@ -240,15 +242,17 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec, static int stac9766_reset(struct snd_soc_codec *codec, int try_warm) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(codec->ac97); + soc_ac97_ops->warm_reset(ac97); if (stac9766_ac97_read(codec, 0) == stac9766_reg[0]) return 1; } - soc_ac97_ops->reset(codec->ac97); + soc_ac97_ops->reset(ac97); if (soc_ac97_ops->warm_reset) - soc_ac97_ops->warm_reset(codec->ac97); + soc_ac97_ops->warm_reset(ac97); if (stac9766_ac97_read(codec, 0) != stac9766_reg[0]) return -EIO; return 0; @@ -262,6 +266,7 @@ static int stac9766_codec_suspend(struct snd_soc_codec *codec) static int stac9766_codec_resume(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); u16 id, reset; reset = 0; @@ -271,8 +276,8 @@ reset: printk(KERN_ERR "stac9766 failed to resume"); return -EIO; } - codec->ac97->bus->ops->warm_reset(codec->ac97); - id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2); + ac97->bus->ops->warm_reset(ac97); + id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2); if (id != 0x4c13) { stac9766_reset(codec, 0); reset++; @@ -332,11 +337,14 @@ static struct snd_soc_dai_driver stac9766_dai[] = { static int stac9766_codec_probe(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97; int ret = 0; - ret = snd_soc_new_ac97_codec(codec); - if (ret < 0) - goto codec_err; + ac97 = snd_soc_new_ac97_codec(codec); + if (IS_ERR(ac97)) + return PTR_ERR(ac97); + + snd_soc_codec_set_drvdata(codec, ac97); /* do a cold reset for the controller and then try * a warm reset followed by an optional cold reset for codec */ @@ -355,13 +363,15 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec) return 0; codec_err: - snd_soc_free_ac97_codec(codec); + snd_soc_free_ac97_codec(ac97); return ret; } static int stac9766_codec_remove(struct snd_soc_codec *codec) { - snd_soc_free_ac97_codec(codec); + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + snd_soc_free_ac97_codec(ac97); return 0; } diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 5b5118b..d3a800f 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -203,13 +203,14 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = { /* We use a register cache to enhance read performance. */ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; switch (reg) { case AC97_RESET: case AC97_VENDOR_ID1: case AC97_VENDOR_ID2: - return soc_ac97_ops->read(codec->ac97, reg); + return soc_ac97_ops->read(ac97, reg); default: reg = reg >> 1; @@ -223,9 +224,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9705_reg))) cache[reg] = val; @@ -293,8 +295,10 @@ static struct snd_soc_dai_driver wm9705_dai[] = { static int wm9705_reset(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + if (soc_ac97_ops->reset) { - soc_ac97_ops->reset(codec->ac97); + soc_ac97_ops->reset(ac97); if (ac97_read(codec, 0) == wm9705_reg[0]) return 0; /* Success */ } @@ -307,13 +311,16 @@ static int wm9705_reset(struct snd_soc_codec *codec) #ifdef CONFIG_PM static int wm9705_soc_suspend(struct snd_soc_codec *codec) { - soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff); + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff); return 0; } static int wm9705_soc_resume(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); int i, ret; u16 *cache = codec->reg_cache; @@ -322,7 +329,7 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) return ret; for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) { - soc_ac97_ops->write(codec->ac97, i, cache[i>>1]); + soc_ac97_ops->write(ac97, i, cache[i>>1]); } return 0; @@ -334,14 +341,18 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) static int wm9705_soc_probe(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97; int ret = 0; - ret = snd_soc_new_ac97_codec(codec); - if (ret < 0) { + ac97 = snd_soc_new_ac97_codec(codec); + if (IS_ERR(ac97)) { + ret = PTR_ERR(ac97); dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } + snd_soc_codec_set_drvdata(codec, ac97); + ret = wm9705_reset(codec); if (ret) goto reset_err; @@ -349,13 +360,15 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) return 0; reset_err: - snd_soc_free_ac97_codec(codec); + snd_soc_free_ac97_codec(ac97); return ret; } static int wm9705_soc_remove(struct snd_soc_codec *codec) { - snd_soc_free_ac97_codec(codec); + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + snd_soc_free_ac97_codec(ac97); return 0; } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 9fa794b..52a211b 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -24,6 +24,7 @@ #include "wm9712.h" struct wm9712_priv { + struct snd_ac97 *ac97; unsigned int hp_mixer[2]; struct mutex lock; }; @@ -484,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = { static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || reg == AC97_REC_GAIN) - return soc_ac97_ops->read(codec->ac97, reg); + return soc_ac97_ops->read(wm9712->ac97, reg); else { reg = reg >> 1; @@ -503,9 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(wm9712->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9712_reg))) cache[reg] = val; @@ -613,15 +616,17 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) { + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); + if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(codec->ac97); + soc_ac97_ops->warm_reset(wm9712->ac97); if (ac97_read(codec, 0) == wm9712_reg[0]) return 1; } - soc_ac97_ops->reset(codec->ac97); + soc_ac97_ops->reset(wm9712->ac97); if (soc_ac97_ops->warm_reset) - soc_ac97_ops->warm_reset(codec->ac97); + soc_ac97_ops->warm_reset(wm9712->ac97); if (ac97_read(codec, 0) != wm9712_reg[0]) goto err; return 0; @@ -639,6 +644,7 @@ static int wm9712_soc_suspend(struct snd_soc_codec *codec) static int wm9712_soc_resume(struct snd_soc_codec *codec) { + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); int i, ret; u16 *cache = codec->reg_cache; @@ -654,7 +660,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) if (i == AC97_INT_PAGING || i == AC97_POWERDOWN || (i > 0x58 && i != 0x5c)) continue; - soc_ac97_ops->write(codec->ac97, i, cache[i>>1]); + soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]); } } @@ -663,11 +669,13 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) static int wm9712_soc_probe(struct snd_soc_codec *codec) { + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); int ret = 0; - ret = snd_soc_new_ac97_codec(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to register AC97 codec\n"); + wm9712->ac97 = snd_soc_new_ac97_codec(codec); + if (IS_ERR(wm9712->ac97)) { + ret = PTR_ERR(wm9712->ac97); + dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); return ret; } @@ -683,13 +691,15 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) return 0; reset_err: - snd_soc_free_ac97_codec(codec); + snd_soc_free_ac97_codec(wm9712->ac97); return ret; } static int wm9712_soc_remove(struct snd_soc_codec *codec) { - snd_soc_free_ac97_codec(codec); + struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); + + snd_soc_free_ac97_codec(wm9712->ac97); return 0; } diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index cd1b266..6c95d98 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -30,6 +30,7 @@ #include "wm9713.h" struct wm9713_priv { + struct snd_ac97 *ac97; u32 pll_in; /* PLL input frequency */ unsigned int hp_mixer[2]; struct mutex lock; @@ -674,12 +675,13 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = { static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { + struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); u16 *cache = codec->reg_cache; if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || reg == AC97_CD) - return soc_ac97_ops->read(codec->ac97, reg); + return soc_ac97_ops->read(wm9713->ac97, reg); else { reg = reg >> 1; @@ -693,8 +695,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { + struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + u16 *cache = codec->reg_cache; - soc_ac97_ops->write(codec->ac97, reg, val); + soc_ac97_ops->write(wm9713->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9713_reg))) cache[reg] = val; @@ -1121,15 +1125,17 @@ static struct snd_soc_dai_driver wm9713_dai[] = { int wm9713_reset(struct snd_soc_codec *codec, int try_warm) { + struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(codec->ac97); + soc_ac97_ops->warm_reset(wm9713->ac97); if (ac97_read(codec, 0) == wm9713_reg[0]) return 1; } - soc_ac97_ops->reset(codec->ac97); + soc_ac97_ops->reset(wm9713->ac97); if (soc_ac97_ops->warm_reset) - soc_ac97_ops->warm_reset(codec->ac97); + soc_ac97_ops->warm_reset(wm9713->ac97); if (ac97_read(codec, 0) != wm9713_reg[0]) { dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; @@ -1207,7 +1213,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID || i == AC97_EXTENDED_MSTATUS || i > 0x66) continue; - soc_ac97_ops->write(codec->ac97, i, cache[i>>1]); + soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]); } } @@ -1216,11 +1222,12 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { + struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); int ret = 0, reg; - ret = snd_soc_new_ac97_codec(codec); - if (ret < 0) - return ret; + wm9713->ac97 = snd_soc_new_ac97_codec(codec); + if (IS_ERR(wm9713->ac97)) + return PTR_ERR(wm9713->ac97); /* do a cold reset for the controller and then try * a warm reset followed by an optional cold reset for codec */ @@ -1238,13 +1245,15 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) return 0; reset_err: - snd_soc_free_ac97_codec(codec); + snd_soc_free_ac97_codec(wm9713->ac97); return ret; } static int wm9713_soc_remove(struct snd_soc_codec *codec) { - snd_soc_free_ac97_codec(codec); + struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + + snd_soc_free_ac97_codec(wm9713->ac97); return 0; } diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 920d76c..2e10e9a 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -53,30 +53,33 @@ static void soc_ac97_device_release(struct device *dev) * * Initialises AC97 codec resources for use by ad-hoc devices only. */ -int snd_soc_new_ac97_codec(struct snd_soc_codec *codec) +struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec) { + struct snd_ac97 *ac97; int ret; - codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); - if (codec->ac97 == NULL) - return -ENOMEM; + ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); + if (ac97 == NULL) + return ERR_PTR(-ENOMEM); - codec->ac97->bus = &soc_ac97_bus; - codec->ac97->num = 0; + ac97->bus = &soc_ac97_bus; + ac97->num = 0; - codec->ac97->dev.bus = &ac97_bus_type; - codec->ac97->dev.parent = codec->component.card->dev; - codec->ac97->dev.release = soc_ac97_device_release; + ac97->dev.bus = &ac97_bus_type; + ac97->dev.parent = codec->component.card->dev; + ac97->dev.release = soc_ac97_device_release; - dev_set_name(&codec->ac97->dev, "%d-%d:%s", + dev_set_name(&ac97->dev, "%d-%d:%s", codec->component.card->snd_card->number, 0, codec->component.name); - ret = device_register(&codec->ac97->dev); - if (ret) - put_device(&codec->ac97->dev); + ret = device_register(&ac97->dev); + if (ret) { + put_device(&ac97->dev); + return ERR_PTR(ret); + } - return ret; + return ac97; } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); @@ -86,12 +89,11 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); * * Frees AC97 codec device resources. */ -void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) +void snd_soc_free_ac97_codec(struct snd_ac97 *ac97) { - device_del(&codec->ac97->dev); - codec->ac97->bus = NULL; - put_device(&codec->ac97->dev); - codec->ac97 = NULL; + device_del(&ac97->dev); + ac97->bus = NULL; + put_device(&ac97->dev); } EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); -- cgit v0.10.2 From a5a267cf9ca9937b0ef946b502657ae7638282f6 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 18 Nov 2014 17:42:54 +0530 Subject: ASoC: rt286: build warning of section mismatch while building we were getting the following build warning: Section mismatch in reference from the function rt286_i2c_probe() to the variable .init.data:force_combo_jack_table The function rt286_i2c_probe() references the variable __initdata force_combo_jack_table. This is often because rt286_i2c_probe lacks a __initdata annotation or the annotation of force_combo_jack_table is wrong. we were getting the warning as force_combo_jack_table was marked with __initdata Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 2e818aa..2cd4fe4 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1206,7 +1206,7 @@ static const struct acpi_device_id rt286_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, rt286_acpi_match); -static struct dmi_system_id force_combo_jack_table[] __initdata = { +static struct dmi_system_id force_combo_jack_table[] = { { .ident = "Intel Wilson Beach", .matches = { -- cgit v0.10.2 From d6d521799fac14e14dead4e9428158340ff6b95f Mon Sep 17 00:00:00 2001 From: JS Park Date: Tue, 18 Nov 2014 16:07:22 +0000 Subject: ASoC: wm_adsp: Fix memory leak in wm_adsp_setup_algs Signed-off-by: JS Park Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 0a08ef5e..6a2a035 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1053,8 +1053,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp1_alg[i].zm)); region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP1_DM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].dm); @@ -1071,8 +1073,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) } region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP1_ZM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].zm); @@ -1101,8 +1105,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp2_alg[i].zm)); region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP2_XM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].xm); @@ -1119,8 +1125,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) } region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP2_YM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].ym); @@ -1137,8 +1145,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) } region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP2_ZM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].zm); -- cgit v0.10.2 From 00e4c3b6e285da90e736fbefff3d9e74a200ee54 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 18 Nov 2014 16:25:27 +0000 Subject: ASoC: wm_adsp: Move core_ena to be co-located with start bit Many firmwares do not wait for the start bit before they begin processing audio, whilst this is a bug on the firmware side there are too many such firmwares in the wild to ignore the situation. This patch moves the core enable to happen at same time as the start, the firmware looses the ability to overlap its own startup with the audio path bring up but we ensure that all firmwares behave. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 6712478..cce9020 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1595,13 +1595,6 @@ static void wm_adsp2_boot_work(struct work_struct *work) 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; @@ -1651,8 +1644,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_START, - ADSP2_START); + ADSP2_CORE_ENA | ADSP2_START, + ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; break; -- cgit v0.10.2 From 2dfe2b08d280c15cc7266de40412c2a911643148 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 19 Nov 2014 13:52:18 +0800 Subject: ASoC: rt5677: Align the reg_default table with tab character Align the reg_default table with tab character Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 9ae2e84..b2d88bb 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -55,13 +55,13 @@ static const struct regmap_range_cfg rt5677_ranges[] = { }; static const struct reg_default init_list[] = { - {RT5677_ASRC_12, 0x0018}, - {RT5677_PR_BASE + 0x3d, 0x364d}, - {RT5677_PR_BASE + 0x17, 0x4fc0}, - {RT5677_PR_BASE + 0x13, 0x0312}, - {RT5677_PR_BASE + 0x1e, 0x0000}, - {RT5677_PR_BASE + 0x12, 0x0eaa}, - {RT5677_PR_BASE + 0x14, 0x018a}, + {RT5677_ASRC_12, 0x0018}, + {RT5677_PR_BASE + 0x3d, 0x364d}, + {RT5677_PR_BASE + 0x17, 0x4fc0}, + {RT5677_PR_BASE + 0x13, 0x0312}, + {RT5677_PR_BASE + 0x1e, 0x0000}, + {RT5677_PR_BASE + 0x12, 0x0eaa}, + {RT5677_PR_BASE + 0x14, 0x018a}, }; #define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list) -- cgit v0.10.2 From 35d40d10e95f52569570dc4e26da19f072aa256d Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 19 Nov 2014 13:52:19 +0800 Subject: ASoC: rt5677: Follow the gpio naming rule to rename the irq function Follow the gpio naming rule to rename the irq function. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index b2d88bb..dd080cd 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4552,7 +4552,7 @@ static struct regmap_irq_chip rt5677_irq_chip = { .mask_invert = 1, }; -static int rt5677_irq_init(struct i2c_client *i2c) +static int rt5677_init_irq(struct i2c_client *i2c) { int ret; struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); @@ -4579,7 +4579,7 @@ static int rt5677_irq_init(struct i2c_client *i2c) return 0; } -static void rt5677_irq_exit(struct i2c_client *i2c) +static void rt5677_free_irq(struct i2c_client *i2c) { struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); @@ -4693,7 +4693,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, } rt5677_init_gpio(i2c); - rt5677_irq_init(i2c); + rt5677_init_irq(i2c); return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677, rt5677_dai, ARRAY_SIZE(rt5677_dai)); @@ -4701,9 +4701,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, static int rt5677_i2c_remove(struct i2c_client *i2c) { - rt5677_irq_exit(i2c); - snd_soc_unregister_codec(&i2c->dev); + rt5677_free_irq(i2c); rt5677_free_gpio(i2c); return 0; -- cgit v0.10.2 From 683996cb2255373c2055e7b69584ac153eb49f42 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 19 Nov 2014 13:52:20 +0800 Subject: ASoC: rt5677: Set the slow charge of the vref in the end of the power sequences Set the slow charge of the vref in the end of the power sequences Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index dd080cd..f2211f1 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -2184,6 +2184,31 @@ static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5677_vref_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (codec->dapm.bias_level != SND_SOC_BIAS_ON && + !rt5677->is_vref_slow) { + mdelay(20); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_PWR_FV1 | RT5677_PWR_FV2, + RT5677_PWR_FV1 | RT5677_PWR_FV2); + rt5677->is_vref_slow = true; + } + break; + + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT, 0, rt5677_set_pll1_event, SND_SOC_DAPM_POST_PMU), @@ -2669,13 +2694,20 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_MUX("PDM2 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_R_SFT, 1, &rt5677_pdm2_r_mux), - SND_SOC_DAPM_PGA_S("LOUT1 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT, + SND_SOC_DAPM_PGA_S("LOUT1 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("LOUT2 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT, + SND_SOC_DAPM_PGA_S("LOUT2 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("LOUT3 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT, + SND_SOC_DAPM_PGA_S("LOUT3 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("LOUT1 vref", 1, SND_SOC_NOPM, 0, 0, + rt5677_vref_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT2 vref", 1, SND_SOC_NOPM, 0, 0, + rt5677_vref_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT3 vref", 1, SND_SOC_NOPM, 0, 0, + rt5677_vref_event, SND_SOC_DAPM_POST_PMU), + /* Output Lines */ SND_SOC_DAPM_OUTPUT("LOUT1"), SND_SOC_DAPM_OUTPUT("LOUT2"), @@ -2684,6 +2716,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("PDM1R"), SND_SOC_DAPM_OUTPUT("PDM2L"), SND_SOC_DAPM_OUTPUT("PDM2R"), + + SND_SOC_DAPM_POST("vref", rt5677_vref_event), }; static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { @@ -3572,9 +3606,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "LOUT2 amp", NULL, "DAC 2" }, { "LOUT3 amp", NULL, "DAC 3" }, - { "LOUT1", NULL, "LOUT1 amp" }, - { "LOUT2", NULL, "LOUT2 amp" }, - { "LOUT3", NULL, "LOUT3 amp" }, + { "LOUT1 vref", NULL, "LOUT1 amp" }, + { "LOUT2 vref", NULL, "LOUT2 amp" }, + { "LOUT3 vref", NULL, "LOUT3 amp" }, + + { "LOUT1", NULL, "LOUT1 vref" }, + { "LOUT2", NULL, "LOUT2 vref" }, + { "LOUT3", NULL, "LOUT3 vref" }, { "PDM1L", NULL, "PDM1 L Mux" }, { "PDM1R", NULL, "PDM1 R Mux" }, @@ -3957,14 +3995,12 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_PWR_FV1 | RT5677_PWR_FV2 | RT5677_PWR_VREF1 | RT5677_PWR_MB | RT5677_PWR_BG | RT5677_PWR_VREF2, RT5677_PWR_VREF1 | RT5677_PWR_MB | RT5677_PWR_BG | RT5677_PWR_VREF2); - mdelay(20); - regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, - RT5677_PWR_FV1 | RT5677_PWR_FV2, - RT5677_PWR_FV1 | RT5677_PWR_FV2); + rt5677->is_vref_slow = false; regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, RT5677_PWR_CORE, RT5677_PWR_CORE); regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 2979d5a..a02f64c 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1687,6 +1687,7 @@ struct rt5677_priv { bool dsp_vad_en; struct regmap_irq_chip_data *irq_data; bool is_dsp_mode; + bool is_vref_slow; }; #endif /* __RT5677_H__ */ -- cgit v0.10.2 From 22853223d15b3a626de62cf9e40ce2d3881bc0a8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Nov 2014 19:45:51 +0100 Subject: regmap: ac97: Add generic AC'97 callbacks Use the recently added support for bus operations to provide a standard mapping for AC'97 register I/O. Signed-off-by: Mark Brown Signed-off-by: Lars-Peter Clausen diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 8a3f51f..db9d00c3 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -3,12 +3,15 @@ # subsystems should select the appropriate symbols. config REGMAP - default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ) + default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) select LZO_COMPRESS select LZO_DECOMPRESS select IRQ_DOMAIN if REGMAP_IRQ bool +config REGMAP_AC97 + tristate + config REGMAP_I2C tristate depends on I2C diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index a7c670b..0a53365 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_REGMAP) += regmap.o regcache.o obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o +obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c new file mode 100644 index 0000000..e4c45d2 --- /dev/null +++ b/drivers/base/regmap/regmap-ac97.c @@ -0,0 +1,114 @@ +/* + * Register map access API - AC'97 support + * + * Copyright 2013 Linaro Ltd. 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET: + case AC97_POWERDOWN: + case AC97_INT_PAGING: + case AC97_EXTENDED_ID: + case AC97_EXTENDED_STATUS: + case AC97_EXTENDED_MID: + case AC97_EXTENDED_MSTATUS: + case AC97_GPIO_STATUS: + case AC97_MISC_AFE: + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + case AC97_CODEC_CLASS_REV: + case AC97_PCI_SVID: + case AC97_PCI_SID: + case AC97_FUNC_SELECT: + case AC97_FUNC_INFO: + case AC97_SENSE_INFO: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile); + +static int regmap_ac97_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct snd_ac97 *ac97 = context; + + *val = ac97->bus->ops->read(ac97, reg); + + return 0; +} + +static int regmap_ac97_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct snd_ac97 *ac97 = context; + + ac97->bus->ops->write(ac97, reg, val); + + return 0; +} + +static const struct regmap_bus ac97_regmap_bus = { + .reg_write = regmap_ac97_reg_write, + .reg_read = regmap_ac97_reg_read, +}; + +/** + * regmap_init_ac97(): Initialise AC'97 register map + * + * @ac97: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config) +{ + return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); +} +EXPORT_SYMBOL_GPL(regmap_init_ac97); + +/** + * devm_regmap_init_ac97(): Initialise AC'97 register map + * + * @ac97: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config) +{ + return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_ac97); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index c5ed83f..4419b99 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -27,6 +27,7 @@ struct spmi_device; struct regmap; struct regmap_range_cfg; struct regmap_field; +struct snd_ac97; /* An enum of all the supported cache types */ enum regcache_type { @@ -340,6 +341,8 @@ struct regmap *regmap_init_spmi_ext(struct spmi_device *dev, struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, void __iomem *regs, const struct regmap_config *config); +struct regmap *regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config); struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, @@ -356,6 +359,10 @@ struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev, struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, void __iomem *regs, const struct regmap_config *config); +struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config); + +bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); /** * regmap_init_mmio(): Initialise register map -- cgit v0.10.2 From 20feb881988cdf5f53304c355ae8ee3bf82e80ec Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Nov 2014 19:45:52 +0100 Subject: ASoC: Add helper functions for deferred regmap setup Some drivers (most notably the AC'97 drivers) do not have access to their regmap struct when the component/codec is registered. For those drivers the automatic regmap setup will not work and needs to be done manually, typically from the component/CODEC drivers probe callback. This patch adds a set of helper function to handle deferred regmap initialization as well as early regmap tear-down. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ba7130..342b43b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1277,6 +1277,41 @@ void snd_soc_component_async_complete(struct snd_soc_component *component); int snd_soc_component_test_bits(struct snd_soc_component *component, unsigned int reg, unsigned int mask, unsigned int value); +void snd_soc_component_init_regmap(struct snd_soc_component *component, + struct regmap *regmap); +void snd_soc_component_exit_regmap(struct snd_soc_component *component); + +/** + * snd_soc_codec_init_regmap() - Initialize regmap instance for the CODEC + * @codec: The CODEC for which to initialize the regmap instance + * @regmap: The regmap instance that should be used by the CODEC + * + * This function allows deferred assignment of the regmap instance that is + * associated with the CODEC. Only use this if the regmap instance is not yet + * ready when the CODEC is registered. The function must also be called before + * the first IO attempt of the CODEC. + */ +static inline void snd_soc_codec_init_regmap(struct snd_soc_codec *codec, + struct regmap *regmap) +{ + snd_soc_component_init_regmap(&codec->component, regmap); +} + +/** + * snd_soc_codec_exit_regmap() - De-initialize regmap instance for the CODEC + * @codec: The CODEC for which to de-initialize the regmap instance + * + * Calls regmap_exit() on the regmap instance associated to the CODEC and + * removes the regmap instance from the CODEC. + * + * This function should only be used if snd_soc_codec_init_regmap() was used to + * initialize the regmap instance. + */ +static inline void snd_soc_codec_exit_regmap(struct snd_soc_codec *codec) +{ + snd_soc_component_exit_regmap(&codec->component); +} + /* device driver data */ static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a2..5fd5f08 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3996,22 +3996,58 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, return 0; } -static void snd_soc_component_init_regmap(struct snd_soc_component *component) +static void snd_soc_component_setup_regmap(struct snd_soc_component *component) { - if (!component->regmap) - component->regmap = dev_get_regmap(component->dev, NULL); - if (component->regmap) { - int val_bytes = regmap_get_val_bytes(component->regmap); - /* Errors are legitimate for non-integer byte multiples */ - if (val_bytes > 0) - component->val_bytes = val_bytes; - } + int val_bytes = regmap_get_val_bytes(component->regmap); + + /* Errors are legitimate for non-integer byte multiples */ + if (val_bytes > 0) + component->val_bytes = val_bytes; +} + +/** + * snd_soc_component_init_regmap() - Initialize regmap instance for the component + * @component: The component for which to initialize the regmap instance + * @regmap: The regmap instance that should be used by the component + * + * This function allows deferred assignment of the regmap instance that is + * associated with the component. Only use this if the regmap instance is not + * yet ready when the component is registered. The function must also be called + * before the first IO attempt of the component. + */ +void snd_soc_component_init_regmap(struct snd_soc_component *component, + struct regmap *regmap) +{ + component->regmap = regmap; + snd_soc_component_setup_regmap(component); } +EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap); + +/** + * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component + * @component: The component for which to de-initialize the regmap instance + * + * Calls regmap_exit() on the regmap instance associated to the component and + * removes the regmap instance from the component. + * + * This function should only be used if snd_soc_component_init_regmap() was used + * to initialize the regmap instance. + */ +void snd_soc_component_exit_regmap(struct snd_soc_component *component) +{ + regmap_exit(component->regmap); + component->regmap = NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); static void snd_soc_component_add_unlocked(struct snd_soc_component *component) { - if (!component->write && !component->read) - snd_soc_component_init_regmap(component); + if (!component->write && !component->read) { + if (!component->regmap) + component->regmap = dev_get_regmap(component->dev, NULL); + if (component->regmap) + snd_soc_component_setup_regmap(component); + } list_add(&component->list, &component_list); } -- cgit v0.10.2 From 82d14636418299b4f54511a02373796b38747b48 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Nov 2014 19:45:53 +0100 Subject: ASoC: ad1980: Convert to regmap This patch converts the ad1980 driver to use regmap for its IO. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a68d173..6a66216 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -223,6 +223,7 @@ config SND_SOC_AD193X_I2C select SND_SOC_AD193X config SND_SOC_AD1980 + select REGMAP_AC97 tristate config SND_SOC_AD73311 diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 93bd47d..5fd4a29 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -24,32 +24,86 @@ #include #include #include +#include #include #include #include #include #include -/* - * AD1980 register cache - */ -static const u16 ad1980_reg[] = { - 0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */ - 0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */ - 0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */ - 0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */ - 0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */ - 0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */ - 0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */ - 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ - 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ - 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ - 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ - 0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ - 0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */ - 0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */ +static const struct reg_default ad1980_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x28, 0x03c7 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x30, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x36, 0x8080 }, + { 0x38, 0x8080 }, + { 0x3a, 0x2000 }, + { 0x60, 0x0000 }, + { 0x62, 0x0000 }, + { 0x72, 0x0000 }, + { 0x74, 0x1001 }, + { 0x76, 0x0000 }, +}; + +static bool ad1980_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_MASTER_MONO: + case AC97_PHONE ... AC97_CD: + case AC97_AUX ... AC97_GENERAL_PURPOSE: + case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE: + case AC97_SPDIF: + case AC97_CODEC_CLASS_REV: + case AC97_PCI_SVID: + case AC97_AD_CODEC_CFG: + case AC97_AD_JACK_SPDIF: + case AC97_AD_SERIAL_CFG: + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool ad1980_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return ad1980_readable_reg(dev, reg); + } +} + +static const struct regmap_config ad1980_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = ad1980_readable_reg, + .writeable_reg = ad1980_writeable_reg, + + .reg_defaults = ad1980_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults), }; static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line", @@ -135,39 +189,13 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = { static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - switch (reg) { - case AC97_RESET: - case AC97_INT_PAGING: - case AC97_POWERDOWN: - case AC97_EXTENDED_STATUS: - case AC97_VENDOR_ID1: - case AC97_VENDOR_ID2: - return soc_ac97_ops->read(ac97, reg); - default: - reg = reg >> 1; - - if (reg >= ARRAY_SIZE(ad1980_reg)) - return -EINVAL; - - return cache[reg]; - } + return snd_soc_read(codec, reg); } static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - soc_ac97_ops->write(ac97, reg, val); - reg = reg >> 1; - if (reg < ARRAY_SIZE(ad1980_reg)) - cache[reg] = val; - - return 0; + return snd_soc_write(codec, reg, val); } static struct snd_soc_dai_driver ad1980_dai = { @@ -219,6 +247,7 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) static int ad1980_soc_probe(struct snd_soc_codec *codec) { struct snd_ac97 *ac97; + struct regmap *regmap; int ret; u16 vendor_id2; u16 ext_status; @@ -230,6 +259,13 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) return ret; } + regmap = regmap_init_ac97(ac97, &ad1980_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97; + } + + snd_soc_codec_init_regmap(codec, regmap); snd_soc_codec_set_drvdata(codec, ac97); ret = ad1980_reset(codec, 0); @@ -268,6 +304,8 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) return 0; reset_err: + snd_soc_codec_exit_regmap(codec); +err_free_ac97: snd_soc_free_ac97_codec(ac97); return ret; } @@ -276,6 +314,7 @@ static int ad1980_soc_remove(struct snd_soc_codec *codec) { struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + snd_soc_codec_exit_regmap(codec); snd_soc_free_ac97_codec(ac97); return 0; } @@ -283,12 +322,6 @@ static int ad1980_soc_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_ad1980 = { .probe = ad1980_soc_probe, .remove = ad1980_soc_remove, - .reg_cache_size = ARRAY_SIZE(ad1980_reg), - .reg_word_size = sizeof(u16), - .reg_cache_default = ad1980_reg, - .reg_cache_step = 2, - .write = ac97_write, - .read = ac97_read, .controls = ad1980_snd_ac97_controls, .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls), -- cgit v0.10.2 From 17bb577328a00e7251c8e552471b6583173ca77d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Nov 2014 19:45:54 +0100 Subject: ASoC: ad1980: Remove ac97_read/ac97_write wrappers Since the regmap conversion ac97_read/ac97_write are just simple wrappers around snd_soc_read/snd_soc_write. Use those instead directly and remove the wrappers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 5fd4a29..2860eef 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -186,18 +186,6 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = { { "HP_OUT_R", NULL, "Playback" }, }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return snd_soc_read(codec, reg); -} - -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - return snd_soc_write(codec, reg, val); -} - static struct snd_soc_dai_driver ad1980_dai = { .name = "ad1980-hifi", .playback = { @@ -222,7 +210,7 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) do { if (try_warm && soc_ac97_ops->warm_reset) { soc_ac97_ops->warm_reset(ac97); - if (ac97_read(codec, AC97_RESET) == 0x0090) + if (snd_soc_read(codec, AC97_RESET) == 0x0090) return 1; } @@ -233,9 +221,9 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) * case the first nibble of data is eaten by the addr. (Tag is * always 16 bit) */ - ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900); + snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900); - if (ac97_read(codec, AC97_RESET) == 0x0090) + if (snd_soc_read(codec, AC97_RESET) == 0x0090) return 0; } while (retry_cnt++ < 10); @@ -273,12 +261,12 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) goto reset_err; /* Read out vendor ID to make sure it is ad1980 */ - if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) { + if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) { ret = -ENODEV; goto reset_err; } - vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2); + vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2); if (vendor_id2 != 0x5370) { if (vendor_id2 != 0x5374) { @@ -291,15 +279,15 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) } /* unmute captures and playbacks volume */ - ac97_write(codec, AC97_MASTER, 0x0000); - ac97_write(codec, AC97_PCM, 0x0000); - ac97_write(codec, AC97_REC_GAIN, 0x0000); - ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000); - ac97_write(codec, AC97_SURROUND_MASTER, 0x0000); + snd_soc_write(codec, AC97_MASTER, 0x0000); + snd_soc_write(codec, AC97_PCM, 0x0000); + snd_soc_write(codec, AC97_REC_GAIN, 0x0000); + snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000); + snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000); /*power on LFE/CENTER/Surround DACs*/ - ext_status = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800); + ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS); + snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800); return 0; -- cgit v0.10.2 From 92a6e2a227da5fcaa5b31c9124eabf8c64a6d9f9 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 19 Nov 2014 15:13:26 +0530 Subject: ASoC: Intel: cleanup runtime_pm initialization For ACPI we missed to pm_runtime_enable() call which is required to tell PM core that runtime on this device is enabled now. Since this is common to both PCI and APCI move it out. Also for ACPI we do not require pm_runtime_allow() call, so remove that Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index b2b5604..9e68a7c 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -378,13 +378,13 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) * initially active. So change the state to active before * enabling the pm */ - if (acpi_disabled) { + pm_runtime_enable(ctx->dev); + + if (acpi_disabled) pm_runtime_set_active(ctx->dev); - pm_runtime_enable(ctx->dev); - } else { - pm_runtime_allow(ctx->dev); + else pm_runtime_put_noidle(ctx->dev); - } + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); } EXPORT_SYMBOL_GPL(sst_configure_runtime_pm); -- cgit v0.10.2 From 996cc8494d663cb03c5ec23ced0e09e4bcd845de Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Wed, 19 Nov 2014 15:13:27 +0530 Subject: ASoC: Intel: add BYTCR machine driver with RT5640 Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index a992e85..a26e8e8 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -86,3 +86,15 @@ config SND_SOC_INTEL_BROADWELL_MACH Ultrabook platforms. Say Y if you have such a device If unsure select "N". + +config SND_SOC_INTEL_BYTCR_RT5640_MACH + tristate "ASoC Audio DSP Support for MID BYT Platform" + depends on X86 + select SND_SOC_RT5640 + select SND_SST_MFLD_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) MID Baytrail platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 9ab43be..fbde4b07 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -26,11 +26,13 @@ snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-broadwell-objs := broadwell.o +snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o +obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o # DSP driver obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c new file mode 100644 index 0000000..f5d0fc1 --- /dev/null +++ b/sound/soc/intel/bytcr_dpcm_rt5640.c @@ -0,0 +1,230 @@ +/* + * byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform + * + * Copyright (C) 2014 Intel Corp + * Author: Subhransu S. Prusty + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/rt5640.h" +#include "sst-atom-controls.h" + +static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route byt_audio_map[] = { + {"IN2P", NULL, "Headset Mic"}, + {"IN2N", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "MICBIAS1"}, + {"IN1P", NULL, "MICBIAS1"}, + {"LDO2", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Ext Spk", NULL, "SPOLP"}, + {"Ext Spk", NULL, "SPOLN"}, + {"Ext Spk", NULL, "SPORP"}, + {"Ext Spk", NULL, "SPORN"}, + + {"AIF1 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx"}, + {"codec_in1", NULL, "ssp2 Rx"}, + {"ssp2 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_kcontrol_new byt_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static int byt_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + snd_soc_dai_set_bclk_ratio(codec_dai, 50); + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec clock %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, + params_rate(params) * 50, + params_rate(params) * 512); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct snd_soc_pcm_stream byt_dai_params = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int byt_aif1_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops byt_aif1_ops = { + .startup = byt_aif1_startup, +}; + +static struct snd_soc_ops byt_be_ssp2_ops = { + .hw_params = byt_aif1_hw_params, +}; + +static struct snd_soc_dai_link byt_dailink[] = { + [MERR_DPCM_AUDIO] = { + .name = "Baytrail Audio Port", + .stream_name = "Baytrail Audio", + .cpu_dai_name = "media-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .ignore_suspend = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &byt_aif1_ops, + }, + [MERR_DPCM_COMPR] = { + .name = "Baytrail Compressed Port", + .stream_name = "Baytrail Compress", + .cpu_dai_name = "compress-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + }, + /* back ends */ + { + .name = "SSP2-Codec", + .be_id = 1, + .cpu_dai_name = "ssp2-port", + .platform_name = "sst-mfld-platform", + .no_pcm = 1, + .codec_dai_name = "rt5640-aif1", + .codec_name = "i2c-10EC5640:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .be_hw_params_fixup = byt_codec_fixup, + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &byt_be_ssp2_ops, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_byt = { + .name = "baytrailcraudio", + .dai_link = byt_dailink, + .num_links = ARRAY_SIZE(byt_dailink), + .dapm_widgets = byt_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets), + .dapm_routes = byt_audio_map, + .num_dapm_routes = ARRAY_SIZE(byt_audio_map), + .controls = byt_mc_controls, + .num_controls = ARRAY_SIZE(byt_mc_controls), +}; + +static int snd_byt_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + + /* register the soc card */ + snd_soc_card_byt.dev = &pdev->dev; + + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt); + if (ret_val) { + dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, &snd_soc_card_byt); + return ret_val; +} + +static struct platform_driver snd_byt_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bytt100_rt5640", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_byt_mc_probe, +}; + +module_platform_driver(snd_byt_mc_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); +MODULE_AUTHOR("Subhransu S. Prusty "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bytrt5640-audio"); -- cgit v0.10.2 From 50c0f21b42dd4cd02b51f82274f66912d9a7fa32 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 19 Nov 2014 18:29:02 +0100 Subject: ASoC: sigmadsp: Refuse to load firmware files with a non-supported version Make sure to check the version field of the firmware header to make sure to not accidentally try to parse a firmware file with a different layout. Trying to do so can result in loading invalid firmware code to the device. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index f2de7e0..81a38dd 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -159,6 +159,13 @@ int _process_sigma_firmware(struct device *dev, goto done; } + if (ssfw_head->version != 1) { + dev_err(dev, + "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n", + ssfw_head->version); + goto done; + } + crc = crc32(0, fw->data + sizeof(*ssfw_head), fw->size - sizeof(*ssfw_head)); pr_debug("%s: crc=%x\n", __func__, crc); -- cgit v0.10.2 From 6b25730f68073ee95079d241ea6aa7be00805254 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 19 Nov 2014 18:29:04 +0100 Subject: ASoC: sigmadsp: Drop support support SIGMA_ACTION_DELAY The official firmware generation tool never emitted any SIGMA_ACTION_DELAY instructions. Keeping support for it with the new restructured loader that also supports v2 will be difficult, so drop support for it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 81a38dd..4fd3143 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -7,7 +7,6 @@ */ #include -#include #include #include #include @@ -28,9 +27,6 @@ enum { SIGMA_ACTION_WRITEXBYTES = 0, SIGMA_ACTION_WRITESINGLE, SIGMA_ACTION_WRITESAFELOAD, - SIGMA_ACTION_DELAY, - SIGMA_ACTION_PLLWAIT, - SIGMA_ACTION_NOOP, SIGMA_ACTION_END, }; @@ -79,10 +75,6 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) if (ret < 0) return -EINVAL; break; - case SIGMA_ACTION_DELAY: - udelay(len); - len = 0; - break; case SIGMA_ACTION_END: return 0; default: -- cgit v0.10.2 From d48b088e3ec45eeccf0fce0b75378e41428f47e9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 19 Nov 2014 18:29:05 +0100 Subject: ASoC: sigmadsp: Restructure in preparation for fw v2 support The v2 file format of the SigmaDSP takes a more declarative style compared to the imperative style of the v1 format. In addition some features that are supported with v2 require the driver to keep state around for the firmware. This requires a bit of restructuring of both the firmware loader itself and the drivers making use of the firmware loader. Instead of loading and executing the firmware in place when the DSP is configured the firmware is now loaded at driver probe time. This is required since the new firmware format will in addition to the firmware data itself contain meta information describing the firmware and its requirements and capabilities. Those will for example be used to restrict the supported samplerates advertised by the driver to userspace to the list of samplerates supported for the firmware. This only does the restructuring required by the v2 format, but does not yet add support for the new format itself. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 370b742..05d5eb5 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -103,6 +103,8 @@ struct adau1701 { unsigned int sysclk; struct regmap *regmap; u8 pin_config[12]; + + struct sigmadsp *sigmadsp; }; static const struct snd_kcontrol_new adau1701_controls[] = { @@ -238,12 +240,14 @@ static int adau1701_reg_read(void *context, unsigned int reg, return 0; } -static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) +static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv, + unsigned int rate) { struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); - struct i2c_client *client = to_i2c_client(codec->dev); int ret; + sigmadsp_reset(adau1701->sigmadsp); + if (clkdiv != ADAU1707_CLKDIV_UNSET && gpio_is_valid(adau1701->gpio_pll_mode[0]) && gpio_is_valid(adau1701->gpio_pll_mode[1])) { @@ -284,7 +288,7 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) * know the correct PLL setup */ if (clkdiv != ADAU1707_CLKDIV_UNSET) { - ret = process_sigma_firmware(client, ADAU1701_FIRMWARE); + ret = sigmadsp_setup(adau1701->sigmadsp, rate); if (ret) { dev_warn(codec->dev, "Failed to load firmware\n"); return ret; @@ -385,7 +389,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream, * firmware upload. */ if (clkdiv != adau1701->pll_clkdiv) { - ret = adau1701_reset(codec, clkdiv); + ret = adau1701_reset(codec, clkdiv, params_rate(params)); if (ret < 0) return ret; } @@ -554,6 +558,14 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id, return 0; } +static int adau1701_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec); + + return sigmadsp_restrict_params(adau1701->sigmadsp, substream); +} + #define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ SNDRV_PCM_RATE_192000) @@ -564,6 +576,7 @@ static const struct snd_soc_dai_ops adau1701_dai_ops = { .set_fmt = adau1701_set_dai_fmt, .hw_params = adau1701_hw_params, .digital_mute = adau1701_digital_mute, + .startup = adau1701_startup, }; static struct snd_soc_dai_driver adau1701_dai = { @@ -600,6 +613,10 @@ static int adau1701_probe(struct snd_soc_codec *codec) unsigned int val; struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + ret = sigmadsp_attach(adau1701->sigmadsp, &codec->component); + if (ret) + return ret; + /* * Let the pll_clkdiv variable default to something that won't happen * at runtime. That way, we can postpone the firmware download from @@ -609,7 +626,7 @@ static int adau1701_probe(struct snd_soc_codec *codec) adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET; /* initalize with pre-configured pll mode settings */ - ret = adau1701_reset(codec, adau1701->pll_clkdiv); + ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0); if (ret < 0) return ret; @@ -722,6 +739,12 @@ static int adau1701_i2c_probe(struct i2c_client *client, adau1701->gpio_pll_mode[1] = gpio_pll_mode[1]; i2c_set_clientdata(client, adau1701); + + adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, NULL, + ADAU1701_FIRMWARE); + if (IS_ERR(adau1701->sigmadsp)) + return PTR_ERR(adau1701->sigmadsp); + ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, &adau1701_dai, 1); return ret; diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 5518ebd..0ae1501 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -698,11 +698,6 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec) ARRAY_SIZE(adau1761_dapm_routes)); if (ret) return ret; - - ret = adau17x1_load_firmware(adau, codec->dev, - ADAU1761_FIRMWARE); - if (ret) - dev_warn(codec->dev, "Failed to firmware\n"); } ret = adau17x1_add_routes(codec); @@ -771,16 +766,20 @@ int adau1761_probe(struct device *dev, struct regmap *regmap, enum adau17x1_type type, void (*switch_mode)(struct device *dev)) { struct snd_soc_dai_driver *dai_drv; + const char *firmware_name; int ret; - ret = adau17x1_probe(dev, regmap, type, switch_mode); - if (ret) - return ret; - - if (type == ADAU1361) + if (type == ADAU1361) { dai_drv = &adau1361_dai_driver; - else + firmware_name = NULL; + } else { dai_drv = &adau1761_dai_driver; + firmware_name = ADAU1761_FIRMWARE; + } + + ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name); + if (ret) + return ret; return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1); } diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index e9fc00f..4c8ddc3 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -385,7 +385,6 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec) { struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev); struct adau *adau = snd_soc_codec_get_drvdata(codec); - const char *firmware; int ret; ret = adau17x1_add_widgets(codec); @@ -422,25 +421,10 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec) return ret; } - switch (adau->type) { - case ADAU1381: - firmware = ADAU1381_FIRMWARE; - break; - case ADAU1781: - firmware = ADAU1781_FIRMWARE; - break; - default: - return -EINVAL; - } - ret = adau17x1_add_routes(codec); if (ret < 0) return ret; - ret = adau17x1_load_firmware(adau, codec->dev, firmware); - if (ret) - dev_warn(codec->dev, "Failed to load firmware\n"); - return 0; } @@ -495,9 +479,21 @@ EXPORT_SYMBOL_GPL(adau1781_regmap_config); int adau1781_probe(struct device *dev, struct regmap *regmap, enum adau17x1_type type, void (*switch_mode)(struct device *dev)) { + const char *firmware_name; int ret; - ret = adau17x1_probe(dev, regmap, type, switch_mode); + switch (type) { + case ADAU1381: + firmware_name = ADAU1381_FIRMWARE; + break; + case ADAU1781: + firmware_name = ADAU1781_FIRMWARE; + break; + default: + return -EINVAL; + } + + ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name); if (ret) return ret; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 3e16c1c..1cab34c 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -307,6 +307,7 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream, struct adau *adau = snd_soc_codec_get_drvdata(codec); unsigned int val, div, dsp_div; unsigned int freq; + int ret; if (adau->clk_src == ADAU17X1_CLK_SRC_PLL) freq = adau->pll_freq; @@ -356,6 +357,12 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream, regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div); } + if (adau->sigmadsp) { + ret = adau17x1_setup_firmware(adau, params_rate(params)); + if (ret < 0) + return ret; + } + if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J) return 0; @@ -661,12 +668,24 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int adau17x1_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau *adau = snd_soc_codec_get_drvdata(dai->codec); + + if (adau->sigmadsp) + return sigmadsp_restrict_params(adau->sigmadsp, substream); + + return 0; +} + const struct snd_soc_dai_ops adau17x1_dai_ops = { .hw_params = adau17x1_hw_params, .set_sysclk = adau17x1_set_dai_sysclk, .set_fmt = adau17x1_set_dai_fmt, .set_pll = adau17x1_set_dai_pll, .set_tdm_slot = adau17x1_set_dai_tdm_slot, + .startup = adau17x1_startup, }; EXPORT_SYMBOL_GPL(adau17x1_dai_ops); @@ -745,8 +764,7 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_GPL(adau17x1_volatile_register); -int adau17x1_load_firmware(struct adau *adau, struct device *dev, - const char *firmware) +int adau17x1_setup_firmware(struct adau *adau, unsigned int rate) { int ret; int dspsr; @@ -758,7 +776,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev, regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1); regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf); - ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware); + ret = sigmadsp_setup(adau->sigmadsp, rate); if (ret) { regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0); return ret; @@ -767,7 +785,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev, return 0; } -EXPORT_SYMBOL_GPL(adau17x1_load_firmware); +EXPORT_SYMBOL_GPL(adau17x1_setup_firmware); int adau17x1_add_widgets(struct snd_soc_codec *codec) { @@ -787,8 +805,21 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec) ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dsp_dapm_widgets, ARRAY_SIZE(adau17x1_dsp_dapm_widgets)); + if (ret) + return ret; + + if (!adau->sigmadsp) + return 0; + + ret = sigmadsp_attach(adau->sigmadsp, &codec->component); + if (ret) { + dev_err(codec->dev, "Failed to attach firmware: %d\n", + ret); + return ret; + } } - return ret; + + return 0; } EXPORT_SYMBOL_GPL(adau17x1_add_widgets); @@ -829,7 +860,8 @@ int adau17x1_resume(struct snd_soc_codec *codec) EXPORT_SYMBOL_GPL(adau17x1_resume); int adau17x1_probe(struct device *dev, struct regmap *regmap, - enum adau17x1_type type, void (*switch_mode)(struct device *dev)) + enum adau17x1_type type, void (*switch_mode)(struct device *dev), + const char *firmware_name) { struct adau *adau; @@ -846,6 +878,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, adau); + if (firmware_name) { + adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL, + firmware_name); + if (IS_ERR(adau->sigmadsp)) { + dev_warn(dev, "Could not find firmware file: %ld\n", + PTR_ERR(adau->sigmadsp)); + adau->sigmadsp = NULL; + } + } + if (switch_mode) switch_mode(dev); diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index e4a557f..6861aa3 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -4,6 +4,8 @@ #include #include +#include "sigmadsp.h" + enum adau17x1_type { ADAU1361, ADAU1761, @@ -42,12 +44,14 @@ struct adau { bool dsp_bypass[2]; struct regmap *regmap; + struct sigmadsp *sigmadsp; }; int adau17x1_add_widgets(struct snd_soc_codec *codec); int adau17x1_add_routes(struct snd_soc_codec *codec); int adau17x1_probe(struct device *dev, struct regmap *regmap, - enum adau17x1_type type, void (*switch_mode)(struct device *dev)); + enum adau17x1_type type, void (*switch_mode)(struct device *dev), + const char *firmware_name); int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec, enum adau17x1_micbias_voltage micbias); bool adau17x1_readable_register(struct device *dev, unsigned int reg); @@ -56,8 +60,7 @@ int adau17x1_resume(struct snd_soc_codec *codec); extern const struct snd_soc_dai_ops adau17x1_dai_ops; -int adau17x1_load_firmware(struct adau *adau, struct device *dev, - const char *firmware); +int adau17x1_setup_firmware(struct adau *adau, unsigned int rate); bool adau17x1_has_dsp(struct adau *adau); #define ADAU17X1_CLOCK_CONTROL 0x4000 diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c index 246081a..bf6a2be 100644 --- a/sound/soc/codecs/sigmadsp-i2c.c +++ b/sound/soc/codecs/sigmadsp-i2c.c @@ -6,29 +6,59 @@ * Licensed under the GPL-2 or later. */ -#include #include +#include #include +#include +#include #include "sigmadsp.h" -static int sigma_action_write_i2c(void *control_data, - const struct sigma_action *sa, size_t len) +static int sigmadsp_write_i2c(void *control_data, + unsigned int addr, const uint8_t data[], size_t len) { - return i2c_master_send(control_data, (const unsigned char *)&sa->addr, - len); + uint8_t *buf; + int ret; + + buf = kzalloc(2 + len, GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + + put_unaligned_be16(addr, buf); + memcpy(buf + 2, data, len); + + ret = i2c_master_send(control_data, buf, len + 2); + + kfree(buf); + + return ret; } -int process_sigma_firmware(struct i2c_client *client, const char *name) +/** + * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance + * @client: The parent I2C device + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client, + const struct sigmadsp_ops *ops, const char *firmware_name) { - struct sigma_firmware ssfw; + struct sigmadsp *sigmadsp; + + sigmadsp = devm_sigmadsp_init(&client->dev, ops, firmware_name); + if (IS_ERR(sigmadsp)) + return sigmadsp; - ssfw.control_data = client; - ssfw.write = sigma_action_write_i2c; + sigmadsp->control_data = client; + sigmadsp->write = sigmadsp_write_i2c; - return _process_sigma_firmware(&client->dev, &ssfw, name); + return sigmadsp; } -EXPORT_SYMBOL(process_sigma_firmware); +EXPORT_SYMBOL_GPL(devm_sigmadsp_init_i2c); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("SigmaDSP I2C firmware loader"); diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c index f78ed8d..cdc5dda 100644 --- a/sound/soc/codecs/sigmadsp-regmap.c +++ b/sound/soc/codecs/sigmadsp-regmap.c @@ -12,24 +12,40 @@ #include "sigmadsp.h" -static int sigma_action_write_regmap(void *control_data, - const struct sigma_action *sa, size_t len) +static int sigmadsp_write_regmap(void *control_data, + unsigned int addr, const uint8_t data[], size_t len) { - return regmap_raw_write(control_data, be16_to_cpu(sa->addr), - sa->payload, len - 2); + return regmap_raw_write(control_data, addr, + data, len); } -int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap, - const char *name) +/** + * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance + * @dev: The parent device + * @regmap: Regmap instance to use + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev, + struct regmap *regmap, const struct sigmadsp_ops *ops, + const char *firmware_name) { - struct sigma_firmware ssfw; + struct sigmadsp *sigmadsp; + + sigmadsp = devm_sigmadsp_init(dev, ops, firmware_name); + if (IS_ERR(sigmadsp)) + return sigmadsp; - ssfw.control_data = regmap; - ssfw.write = sigma_action_write_regmap; + sigmadsp->control_data = regmap; + sigmadsp->write = sigmadsp_write_regmap; - return _process_sigma_firmware(dev, &ssfw, name); + return sigmadsp; } -EXPORT_SYMBOL(process_sigma_firmware_regmap); +EXPORT_SYMBOL_GPL(devm_sigmadsp_init_regmap); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("SigmaDSP regmap firmware loader"); diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 4fd3143..34e63b5 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -1,7 +1,7 @@ /* * Load Analog Devices SigmaStudio firmware files * - * Copyright 2009-2011 Analog Devices Inc. + * Copyright 2009-2014 Analog Devices Inc. * * Licensed under the GPL-2 or later. */ @@ -12,11 +12,21 @@ #include #include #include +#include + +#include #include "sigmadsp.h" #define SIGMA_MAGIC "ADISIGM" +struct sigmadsp_data { + struct list_head head; + unsigned int addr; + unsigned int length; + uint8_t data[]; +}; + struct sigma_firmware_header { unsigned char magic[7]; u8 version; @@ -30,6 +40,20 @@ enum { SIGMA_ACTION_END, }; +struct sigma_action { + u8 instr; + u8 len_hi; + __le16 len; + __be16 addr; + unsigned char payload[]; +} __packed; + +static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t data[], size_t len) +{ + return sigmadsp->write(sigmadsp->control_data, addr, data, len); +} + static inline u32 sigma_action_len(struct sigma_action *sa) { return (sa->len_hi << 16) | le16_to_cpu(sa->len); @@ -58,11 +82,11 @@ static size_t sigma_action_size(struct sigma_action *sa) * Returns a negative error value in case of an error, 0 if processing of * the firmware should be stopped after this action, 1 otherwise. */ -static int -process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) +static int process_sigma_action(struct sigmadsp *sigmadsp, + struct sigma_action *sa) { size_t len = sigma_action_len(sa); - int ret; + struct sigmadsp_data *data; pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__, sa->instr, sa->addr, len); @@ -71,9 +95,17 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) case SIGMA_ACTION_WRITEXBYTES: case SIGMA_ACTION_WRITESINGLE: case SIGMA_ACTION_WRITESAFELOAD: - ret = ssfw->write(ssfw->control_data, sa, len); - if (ret < 0) + if (len < 3) return -EINVAL; + + data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = be16_to_cpu(sa->addr); + data->length = len - 2; + memcpy(data->data, sa->payload, data->length); + list_add_tail(&data->head, &sigmadsp->data_list); break; case SIGMA_ACTION_END: return 0; @@ -84,22 +116,24 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) return 1; } -static int -process_sigma_actions(struct sigma_firmware *ssfw) +static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp, + const struct firmware *fw) { struct sigma_action *sa; - size_t size; + size_t size, pos; int ret; - while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) { - sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos); + pos = sizeof(struct sigma_firmware_header); + + while (pos + sizeof(*sa) <= fw->size) { + sa = (struct sigma_action *)(fw->data + pos); size = sigma_action_size(sa); - ssfw->pos += size; - if (ssfw->pos > ssfw->fw->size || size == 0) + pos += size; + if (pos > fw->size || size == 0) break; - ret = process_sigma_action(ssfw, sa); + ret = process_sigma_action(sigmadsp, sa); pr_debug("%s: action returned %i\n", __func__, ret); @@ -107,29 +141,40 @@ process_sigma_actions(struct sigma_firmware *ssfw) return ret; } - if (ssfw->pos != ssfw->fw->size) + if (pos != fw->size) return -EINVAL; return 0; } -int _process_sigma_firmware(struct device *dev, - struct sigma_firmware *ssfw, const char *name) +static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp) { - int ret; - struct sigma_firmware_header *ssfw_head; + struct sigmadsp_data *data, *_data; + + list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head) + kfree(data); + + INIT_LIST_HEAD(&sigmadsp->data_list); +} + +static void devm_sigmadsp_release(struct device *dev, void *res) +{ + sigmadsp_firmware_release((struct sigmadsp *)res); +} + +static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) +{ + const struct sigma_firmware_header *ssfw_head; const struct firmware *fw; + int ret; u32 crc; - pr_debug("%s: loading firmware %s\n", __func__, name); - /* first load the blob */ - ret = request_firmware(&fw, name, dev); + ret = request_firmware(&fw, name, sigmadsp->dev); if (ret) { pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); - return ret; + goto done; } - ssfw->fw = fw; /* then verify the header */ ret = -EINVAL; @@ -141,20 +186,13 @@ int _process_sigma_firmware(struct device *dev, * overflows later in the loading process. */ if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { - dev_err(dev, "Failed to load firmware: Invalid size\n"); + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); goto done; } ssfw_head = (void *)fw->data; if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { - dev_err(dev, "Failed to load firmware: Invalid magic\n"); - goto done; - } - - if (ssfw_head->version != 1) { - dev_err(dev, - "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n", - ssfw_head->version); + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); goto done; } @@ -162,23 +200,160 @@ int _process_sigma_firmware(struct device *dev, fw->size - sizeof(*ssfw_head)); pr_debug("%s: crc=%x\n", __func__, crc); if (crc != le32_to_cpu(ssfw_head->crc)) { - dev_err(dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", + dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", le32_to_cpu(ssfw_head->crc), crc); goto done; } - ssfw->pos = sizeof(*ssfw_head); + switch (ssfw_head->version) { + case 1: + ret = sigmadsp_fw_load_v1(sigmadsp, fw); + break; + default: + dev_err(sigmadsp->dev, + "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n", + ssfw_head->version); + ret = -EINVAL; + break; + } - /* finally process all of the actions */ - ret = process_sigma_actions(ssfw); + if (ret) + sigmadsp_firmware_release(sigmadsp); - done: +done: release_firmware(fw); - pr_debug("%s: loaded %s\n", __func__, name); + return ret; +} + +static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name) +{ + sigmadsp->ops = ops; + sigmadsp->dev = dev; + + INIT_LIST_HEAD(&sigmadsp->data_list); + + return sigmadsp_firmware_load(sigmadsp, firmware_name); +} + +/** + * devm_sigmadsp_init() - Initialize SigmaDSP instance + * @dev: The parent device + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init(struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name) +{ + struct sigmadsp *sigmadsp; + int ret; + + sigmadsp = devres_alloc(devm_sigmadsp_release, sizeof(*sigmadsp), + GFP_KERNEL); + if (!sigmadsp) + return ERR_PTR(-ENOMEM); + + ret = sigmadsp_init(sigmadsp, dev, ops, firmware_name); + if (ret) { + devres_free(sigmadsp); + return ERR_PTR(ret); + } + + devres_add(dev, sigmadsp); + + return sigmadsp; +} +EXPORT_SYMBOL_GPL(devm_sigmadsp_init); + +/** + * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component + * @sigmadsp: The sigmadsp instance to attach + * @component: The component to attach to + * + * Typically called in the components probe callback. + * + * Note, once this function has been called the firmware must not be released + * until after the ALSA snd_card that the component belongs to has been + * disconnected, even if sigmadsp_attach() returns an error. + */ +int sigmadsp_attach(struct sigmadsp *sigmadsp, + struct snd_soc_component *component) +{ + sigmadsp->component = component; + + return 0; +} +EXPORT_SYMBOL_GPL(sigmadsp_attach); + +/** + * sigmadsp_setup() - Setup the DSP for the specified samplerate + * @sigmadsp: The sigmadsp instance to configure + * @samplerate: The samplerate the DSP should be configured for + * + * Loads the appropriate firmware program and parameter memory (if not already + * loaded) and enables the controls for the specified samplerate. Any control + * parameter changes that have been made previously will be restored. + * + * Returns 0 on success, a negative error code otherwise. + */ +int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate) +{ + struct sigmadsp_data *data; + int ret; + + if (sigmadsp->current_samplerate == samplerate) + return 0; + + list_for_each_entry(data, &sigmadsp->data_list, head) { + ret = sigmadsp_write(sigmadsp, data->addr, data->data, + data->length); + if (ret) + goto err; + } + + sigmadsp->current_samplerate = samplerate; + + return 0; +err: + sigmadsp_reset(sigmadsp); return ret; } -EXPORT_SYMBOL_GPL(_process_sigma_firmware); +EXPORT_SYMBOL_GPL(sigmadsp_setup); + +/** + * sigmadsp_reset() - Notify the sigmadsp instance that the DSP has been reset + * @sigmadsp: The sigmadsp instance to reset + * + * Should be called whenever the DSP has been reset and parameter and program + * memory need to be re-loaded. + */ +void sigmadsp_reset(struct sigmadsp *sigmadsp) +{ + sigmadsp->current_samplerate = 0; +} +EXPORT_SYMBOL_GPL(sigmadsp_reset); + +/** + * sigmadsp_restrict_params() - Applies DSP firmware specific constraints + * @sigmadsp: The sigmadsp instance + * @substream: The substream to restrict + * + * Applies samplerate constraints that may be required by the firmware Should + * typically be called from the CODEC/component drivers startup callback. + * + * Returns 0 on success, a negative error code otherwise. + */ +int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, + struct snd_pcm_substream *substream) +{ + return 0; +} +EXPORT_SYMBOL_GPL(sigmadsp_restrict_params); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h index c47cd23..a6be91a 100644 --- a/sound/soc/codecs/sigmadsp.h +++ b/sound/soc/codecs/sigmadsp.h @@ -11,31 +11,50 @@ #include #include +#include -struct sigma_action { - u8 instr; - u8 len_hi; - __le16 len; - __be16 addr; - unsigned char payload[]; -} __packed; +#include -struct sigma_firmware { - const struct firmware *fw; - size_t pos; +struct sigmadsp; +struct snd_soc_component; +struct snd_pcm_substream; + +struct sigmadsp_ops { + int (*safeload)(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t *data, size_t len); +}; + +struct sigmadsp { + const struct sigmadsp_ops *ops; + + struct list_head data_list; + + unsigned int current_samplerate; + struct snd_soc_component *component; + struct device *dev; void *control_data; - int (*write)(void *control_data, const struct sigma_action *sa, - size_t len); + int (*write)(void *, unsigned int, const uint8_t *, size_t); }; -int _process_sigma_firmware(struct device *dev, - struct sigma_firmware *ssfw, const char *name); +struct sigmadsp *devm_sigmadsp_init(struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name); +void sigmadsp_reset(struct sigmadsp *sigmadsp); + +int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, + struct snd_pcm_substream *substream); struct i2c_client; -extern int process_sigma_firmware(struct i2c_client *client, const char *name); -extern int process_sigma_firmware_regmap(struct device *dev, - struct regmap *regmap, const char *name); +struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev, + struct regmap *regmap, const struct sigmadsp_ops *ops, + const char *firmware_name); +struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client, + const struct sigmadsp_ops *ops, const char *firmware_name); + +int sigmadsp_attach(struct sigmadsp *sigmadsp, + struct snd_soc_component *component); +int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate); +void sigmadsp_reset(struct sigmadsp *sigmadsp); #endif -- cgit v0.10.2 From a35daac77a0397d4f23b642d3dc0684682e56cc5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 19 Nov 2014 18:29:06 +0100 Subject: ASoC: sigmadsp: Add support for fw v2 This patch adds support for the v2 version of the SigmaDSP firmware file format. The new format has support for having different program and parameter settings for different samplerates. In addition it stores metadata describing the firmware. This metadata includes the set of supported samplerates which will be used to restrict the samplerates that can be selected by userspace. Also included is information about the modifiable parameters. Those will be exposed as ALSA controls so they can be changed at runtime. The new format is based on a binary type-length-value structure that makes it both forward and backwards compatible. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c index bf6a2be..21ca3a5 100644 --- a/sound/soc/codecs/sigmadsp-i2c.c +++ b/sound/soc/codecs/sigmadsp-i2c.c @@ -34,6 +34,34 @@ static int sigmadsp_write_i2c(void *control_data, return ret; } +static int sigmadsp_read_i2c(void *control_data, + unsigned int addr, uint8_t data[], size_t len) +{ + struct i2c_client *client = control_data; + struct i2c_msg msgs[2]; + uint8_t buf[2]; + int ret; + + put_unaligned_be16(addr, buf); + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(buf); + msgs[0].buf = buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = len; + msgs[1].buf = data; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + return 0; +} + /** * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance * @client: The parent I2C device @@ -55,6 +83,7 @@ struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client, sigmadsp->control_data = client; sigmadsp->write = sigmadsp_write_i2c; + sigmadsp->read = sigmadsp_read_i2c; return sigmadsp; } diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c index cdc5dda..912861b 100644 --- a/sound/soc/codecs/sigmadsp-regmap.c +++ b/sound/soc/codecs/sigmadsp-regmap.c @@ -19,6 +19,13 @@ static int sigmadsp_write_regmap(void *control_data, data, len); } +static int sigmadsp_read_regmap(void *control_data, + unsigned int addr, uint8_t data[], size_t len) +{ + return regmap_raw_read(control_data, addr, + data, len); +} + /** * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance * @dev: The parent device @@ -42,6 +49,7 @@ struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev, sigmadsp->control_data = regmap; sigmadsp->write = sigmadsp_write_regmap; + sigmadsp->read = sigmadsp_read_regmap; return sigmadsp; } diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 34e63b5..55af596 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -14,19 +14,61 @@ #include #include +#include #include #include "sigmadsp.h" #define SIGMA_MAGIC "ADISIGM" +#define SIGMA_FW_CHUNK_TYPE_DATA 0 +#define SIGMA_FW_CHUNK_TYPE_CONTROL 1 +#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2 + +struct sigmadsp_control { + struct list_head head; + uint32_t samplerates; + unsigned int addr; + unsigned int num_bytes; + const char *name; + struct snd_kcontrol *kcontrol; + bool cached; + uint8_t cache[]; +}; + struct sigmadsp_data { struct list_head head; + uint32_t samplerates; unsigned int addr; unsigned int length; uint8_t data[]; }; +struct sigma_fw_chunk { + __le32 length; + __le32 tag; + __le32 samplerates; +} __packed; + +struct sigma_fw_chunk_data { + struct sigma_fw_chunk chunk; + __le16 addr; + uint8_t data[]; +} __packed; + +struct sigma_fw_chunk_control { + struct sigma_fw_chunk chunk; + __le16 type; + __le16 addr; + __le16 num_bytes; + const char name[]; +} __packed; + +struct sigma_fw_chunk_samplerate { + struct sigma_fw_chunk chunk; + __le32 samplerates[]; +} __packed; + struct sigma_firmware_header { unsigned char magic[7]; u8 version; @@ -54,6 +96,269 @@ static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr, return sigmadsp->write(sigmadsp->control_data, addr, data, len); } +static int sigmadsp_read(struct sigmadsp *sigmadsp, unsigned int addr, + uint8_t data[], size_t len) +{ + return sigmadsp->read(sigmadsp->control_data, addr, data, len); +} + +static int sigmadsp_ctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + + info->type = SNDRV_CTL_ELEM_TYPE_BYTES; + info->count = ctrl->num_bytes; + + return 0; +} + +static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, void *data) +{ + /* safeload loads up to 20 bytes in a atomic operation */ + if (ctrl->num_bytes > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops && + sigmadsp->ops->safeload) + return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data, + ctrl->num_bytes); + else + return sigmadsp_write(sigmadsp, ctrl->addr, data, + ctrl->num_bytes); +} + +static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + uint8_t *data; + int ret = 0; + + mutex_lock(&sigmadsp->lock); + + data = ucontrol->value.bytes.data; + + if (!(kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) + ret = sigmadsp_ctrl_write(sigmadsp, ctrl, data); + + if (ret == 0) { + memcpy(ctrl->cache, data, ctrl->num_bytes); + ctrl->cached = true; + } + + mutex_unlock(&sigmadsp->lock); + + return ret; +} + +static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + int ret = 0; + + mutex_lock(&sigmadsp->lock); + + if (!ctrl->cached) { + ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, + ctrl->num_bytes); + } + + if (ret == 0) { + ctrl->cached = true; + memcpy(ucontrol->value.bytes.data, ctrl->cache, + ctrl->num_bytes); + } + + mutex_unlock(&sigmadsp->lock); + + return ret; +} + +static void sigmadsp_control_free(struct snd_kcontrol *kcontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + + ctrl->kcontrol = NULL; +} + +static bool sigma_fw_validate_control_name(const char *name, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) { + /* Normal ASCII characters are valid */ + if (name[i] < ' ' || name[i] > '~') + return false; + } + + return true; +} + +static int sigma_fw_load_control(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_control *ctrl_chunk; + struct sigmadsp_control *ctrl; + unsigned int num_bytes; + size_t name_len; + char *name; + int ret; + + if (length <= sizeof(*ctrl_chunk)) + return -EINVAL; + + ctrl_chunk = (const struct sigma_fw_chunk_control *)chunk; + + name_len = length - sizeof(*ctrl_chunk); + if (name_len >= SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1; + + /* Make sure there are no non-displayable characaters in the string */ + if (!sigma_fw_validate_control_name(ctrl_chunk->name, name_len)) + return -EINVAL; + + num_bytes = le16_to_cpu(ctrl_chunk->num_bytes); + ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + name = kzalloc(name_len + 1, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto err_free_ctrl; + } + memcpy(name, ctrl_chunk->name, name_len); + name[name_len] = '\0'; + ctrl->name = name; + + ctrl->addr = le16_to_cpu(ctrl_chunk->addr); + ctrl->num_bytes = num_bytes; + ctrl->samplerates = chunk->samplerates; + + list_add_tail(&ctrl->head, &sigmadsp->ctrl_list); + + return 0; + +err_free_ctrl: + kfree(ctrl); + + return ret; +} + +static int sigma_fw_load_data(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_data *data_chunk; + struct sigmadsp_data *data; + + if (length <= sizeof(*data_chunk)) + return -EINVAL; + + data_chunk = (struct sigma_fw_chunk_data *)chunk; + + length -= sizeof(*data_chunk); + + data = kzalloc(sizeof(*data) + length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = le16_to_cpu(data_chunk->addr); + data->length = length; + data->samplerates = chunk->samplerates; + memcpy(data->data, data_chunk->data, length); + list_add_tail(&data->head, &sigmadsp->data_list); + + return 0; +} + +static int sigma_fw_load_samplerates(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_samplerate *rate_chunk; + unsigned int num_rates; + unsigned int *rates; + unsigned int i; + + rate_chunk = (const struct sigma_fw_chunk_samplerate *)chunk; + + num_rates = (length - sizeof(*rate_chunk)) / sizeof(__le32); + + if (num_rates > 32 || num_rates == 0) + return -EINVAL; + + /* We only allow one samplerates block per file */ + if (sigmadsp->rate_constraints.count) + return -EINVAL; + + rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); + if (!rates) + return -ENOMEM; + + for (i = 0; i < num_rates; i++) + rates[i] = le32_to_cpu(rate_chunk->samplerates[i]); + + sigmadsp->rate_constraints.count = num_rates; + sigmadsp->rate_constraints.list = rates; + + return 0; +} + +static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp, + const struct firmware *fw) +{ + struct sigma_fw_chunk *chunk; + unsigned int length, pos; + int ret; + + /* + * Make sure that there is at least one chunk to avoid integer + * underflows later on. Empty firmware is still valid though. + */ + if (fw->size < sizeof(*chunk) + sizeof(struct sigma_firmware_header)) + return 0; + + pos = sizeof(struct sigma_firmware_header); + + while (pos < fw->size - sizeof(*chunk)) { + chunk = (struct sigma_fw_chunk *)(fw->data + pos); + + length = le32_to_cpu(chunk->length); + + if (length > fw->size - pos || length < sizeof(*chunk)) + return -EINVAL; + + switch (chunk->tag) { + case SIGMA_FW_CHUNK_TYPE_DATA: + ret = sigma_fw_load_data(sigmadsp, chunk, length); + break; + case SIGMA_FW_CHUNK_TYPE_CONTROL: + ret = sigma_fw_load_control(sigmadsp, chunk, length); + break; + case SIGMA_FW_CHUNK_TYPE_SAMPLERATES: + ret = sigma_fw_load_samplerates(sigmadsp, chunk, length); + break; + default: + dev_warn(sigmadsp->dev, "Unknown chunk type: %d\n", + chunk->tag); + ret = 0; + break; + } + + if (ret) + return ret; + + /* + * This can not overflow since if length is larger than the + * maximum firmware size (0x4000000) we'll error out earilier. + */ + pos += ALIGN(length, sizeof(__le32)); + } + + return 0; +} + static inline u32 sigma_action_len(struct sigma_action *sa) { return (sa->len_hi << 16) | le16_to_cpu(sa->len); @@ -149,11 +454,18 @@ static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp, static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp) { + struct sigmadsp_control *ctrl, *_ctrl; struct sigmadsp_data *data, *_data; + list_for_each_entry_safe(ctrl, _ctrl, &sigmadsp->ctrl_list, head) { + kfree(ctrl->name); + kfree(ctrl); + } + list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head) kfree(data); + INIT_LIST_HEAD(&sigmadsp->ctrl_list); INIT_LIST_HEAD(&sigmadsp->data_list); } @@ -209,9 +521,12 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) case 1: ret = sigmadsp_fw_load_v1(sigmadsp, fw); break; + case 2: + ret = sigmadsp_fw_load_v2(sigmadsp, fw); + break; default: dev_err(sigmadsp->dev, - "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n", + "Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2\n", ssfw_head->version); ret = -EINVAL; break; @@ -232,7 +547,9 @@ static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev, sigmadsp->ops = ops; sigmadsp->dev = dev; + INIT_LIST_HEAD(&sigmadsp->ctrl_list); INIT_LIST_HEAD(&sigmadsp->data_list); + mutex_init(&sigmadsp->lock); return sigmadsp_firmware_load(sigmadsp, firmware_name); } @@ -270,6 +587,114 @@ struct sigmadsp *devm_sigmadsp_init(struct device *dev, } EXPORT_SYMBOL_GPL(devm_sigmadsp_init); +static int sigmadsp_rate_to_index(struct sigmadsp *sigmadsp, unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < sigmadsp->rate_constraints.count; i++) { + if (sigmadsp->rate_constraints.list[i] == rate) + return i; + } + + return -EINVAL; +} + +static unsigned int sigmadsp_get_samplerate_mask(struct sigmadsp *sigmadsp, + unsigned int samplerate) +{ + int samplerate_index; + + if (samplerate == 0) + return 0; + + if (sigmadsp->rate_constraints.count) { + samplerate_index = sigmadsp_rate_to_index(sigmadsp, samplerate); + if (samplerate_index < 0) + return 0; + + return BIT(samplerate_index); + } else { + return ~0; + } +} + +static bool sigmadsp_samplerate_valid(unsigned int supported, + unsigned int requested) +{ + /* All samplerates are supported */ + if (!supported) + return true; + + return supported & requested; +} + +static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, unsigned int samplerate_mask) +{ + struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + int ret; + + memset(&template, 0, sizeof(template)); + template.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + template.name = ctrl->name; + template.info = sigmadsp_ctrl_info; + template.get = sigmadsp_ctrl_get; + template.put = sigmadsp_ctrl_put; + template.private_value = (unsigned long)ctrl; + template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (!sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask)) + template.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + + kcontrol = snd_ctl_new1(&template, sigmadsp); + if (!kcontrol) + return -ENOMEM; + + kcontrol->private_free = sigmadsp_control_free; + ctrl->kcontrol = kcontrol; + + ret = snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol); + if (ret) + return ret; + + return 0; +} + +static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, unsigned int samplerate_mask) +{ + struct snd_card *card = sigmadsp->component->card->snd_card; + struct snd_kcontrol_volatile *vd; + struct snd_ctl_elem_id id; + bool active, changed; + + active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask); + + down_write(&card->controls_rwsem); + if (!ctrl->kcontrol) { + up_write(&card->controls_rwsem); + return; + } + + id = ctrl->kcontrol->id; + vd = &ctrl->kcontrol->vd[0]; + if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) { + vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + changed = true; + } + up_write(&card->controls_rwsem); + + if (active && changed) { + mutex_lock(&sigmadsp->lock); + if (ctrl->cached) + sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); + mutex_unlock(&sigmadsp->lock); + } + + if (changed) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id); +} + /** * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component * @sigmadsp: The sigmadsp instance to attach @@ -284,8 +709,21 @@ EXPORT_SYMBOL_GPL(devm_sigmadsp_init); int sigmadsp_attach(struct sigmadsp *sigmadsp, struct snd_soc_component *component) { + struct sigmadsp_control *ctrl; + unsigned int samplerate_mask; + int ret; + sigmadsp->component = component; + samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, + sigmadsp->current_samplerate); + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) { + ret = sigmadsp_alloc_control(sigmadsp, ctrl, samplerate_mask); + if (ret) + return ret; + } + return 0; } EXPORT_SYMBOL_GPL(sigmadsp_attach); @@ -303,19 +741,31 @@ EXPORT_SYMBOL_GPL(sigmadsp_attach); */ int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate) { + struct sigmadsp_control *ctrl; + unsigned int samplerate_mask; struct sigmadsp_data *data; int ret; if (sigmadsp->current_samplerate == samplerate) return 0; + samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, samplerate); + if (samplerate_mask == 0) + return -EINVAL; + list_for_each_entry(data, &sigmadsp->data_list, head) { + if (!sigmadsp_samplerate_valid(data->samplerates, + samplerate_mask)) + continue; ret = sigmadsp_write(sigmadsp, data->addr, data->data, data->length); if (ret) goto err; } + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) + sigmadsp_activate_ctrl(sigmadsp, ctrl, samplerate_mask); + sigmadsp->current_samplerate = samplerate; return 0; @@ -335,6 +785,11 @@ EXPORT_SYMBOL_GPL(sigmadsp_setup); */ void sigmadsp_reset(struct sigmadsp *sigmadsp) { + struct sigmadsp_control *ctrl; + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) + sigmadsp_activate_ctrl(sigmadsp, ctrl, false); + sigmadsp->current_samplerate = 0; } EXPORT_SYMBOL_GPL(sigmadsp_reset); @@ -352,7 +807,11 @@ EXPORT_SYMBOL_GPL(sigmadsp_reset); int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, struct snd_pcm_substream *substream) { - return 0; + if (sigmadsp->rate_constraints.count == 0) + return 0; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &sigmadsp->rate_constraints); } EXPORT_SYMBOL_GPL(sigmadsp_restrict_params); diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h index a6be91a..614475c 100644 --- a/sound/soc/codecs/sigmadsp.h +++ b/sound/soc/codecs/sigmadsp.h @@ -27,14 +27,20 @@ struct sigmadsp_ops { struct sigmadsp { const struct sigmadsp_ops *ops; + struct list_head ctrl_list; struct list_head data_list; + struct snd_pcm_hw_constraint_list rate_constraints; + unsigned int current_samplerate; struct snd_soc_component *component; struct device *dev; + struct mutex lock; + void *control_data; int (*write)(void *, unsigned int, const uint8_t *, size_t); + int (*read)(void *, unsigned int, uint8_t *, size_t); }; struct sigmadsp *devm_sigmadsp_init(struct device *dev, -- cgit v0.10.2 From a3a1ec66d6c9320e676fc99dbaf18db4f8dcda93 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 19 Nov 2014 18:29:07 +0100 Subject: ASoC: adau1701: Implement sigmadsp safeload The safeload feature allows to load up to 5 parameter memory registers atomically. This is helpful for switching between e.g. filter settings without causing any glitches. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 05d5eb5..d4e219b 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -22,9 +22,14 @@ #include #include +#include + #include "sigmadsp.h" #include "adau1701.h" +#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i)) +#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i)) + #define ADAU1701_DSPCTRL 0x081c #define ADAU1701_SEROCTL 0x081e #define ADAU1701_SERICTL 0x081f @@ -42,6 +47,7 @@ #define ADAU1701_DSPCTRL_CR (1 << 2) #define ADAU1701_DSPCTRL_DAM (1 << 3) #define ADAU1701_DSPCTRL_ADM (1 << 4) +#define ADAU1701_DSPCTRL_IST (1 << 5) #define ADAU1701_DSPCTRL_SR_48 0x00 #define ADAU1701_DSPCTRL_SR_96 0x01 #define ADAU1701_DSPCTRL_SR_192 0x02 @@ -102,6 +108,7 @@ struct adau1701 { unsigned int pll_clkdiv; unsigned int sysclk; struct regmap *regmap; + struct i2c_client *client; u8 pin_config[12]; struct sigmadsp *sigmadsp; @@ -161,6 +168,7 @@ static bool adau1701_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case ADAU1701_DACSET: + case ADAU1701_DSPCTRL: return true; default: return false; @@ -240,6 +248,50 @@ static int adau1701_reg_read(void *context, unsigned int reg, return 0; } +static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t bytes[], size_t len) +{ + struct i2c_client *client = to_i2c_client(sigmadsp->dev); + struct adau1701 *adau1701 = i2c_get_clientdata(client); + unsigned int val; + unsigned int i; + uint8_t buf[10]; + int ret; + + ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val); + if (ret) + return ret; + + if (val & ADAU1701_DSPCTRL_IST) + msleep(50); + + for (i = 0; i < len / 4; i++) { + put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf); + buf[2] = 0x00; + memcpy(buf + 3, bytes + i * 4, 4); + ret = i2c_master_send(client, buf, 7); + if (ret < 0) + return ret; + else if (ret != 7) + return -EIO; + + put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf); + put_unaligned_le16(addr + i, buf + 2); + ret = i2c_master_send(client, buf, 4); + if (ret < 0) + return ret; + else if (ret != 4) + return -EIO; + } + + return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, + ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST); +} + +static const struct sigmadsp_ops adau1701_sigmadsp_ops = { + .safeload = adau1701_safeload, +}; + static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv, unsigned int rate) { @@ -684,6 +736,7 @@ static int adau1701_i2c_probe(struct i2c_client *client, if (!adau1701) return -ENOMEM; + adau1701->client = client; adau1701->regmap = devm_regmap_init(dev, NULL, client, &adau1701_regmap); if (IS_ERR(adau1701->regmap)) @@ -740,8 +793,8 @@ static int adau1701_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, adau1701); - adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, NULL, - ADAU1701_FIRMWARE); + adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, + &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE); if (IS_ERR(adau1701->sigmadsp)) return PTR_ERR(adau1701->sigmadsp); -- cgit v0.10.2 From 27caca9d2e01c92b26d0690f065aad093fea01c7 Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Tue, 18 Nov 2014 21:00:58 +0400 Subject: i2c: omap: fix NACK and Arbitration Lost irq handling commit 1d7afc95946487945cc7f5019b41255b72224b70 (i2c: omap: ack IRQ in parts) changed the interrupt handler to complete transfers without clearing XRDY (AL case) and ARDY (NACK case) flags. XRDY or ARDY interrupts will be fired again. As a result, ISR keep processing transfer after it was already complete (from the driver code point of view). A didn't see real impacts of the 1d7afc9, but it is really bad idea to have ISR running on user data after transfer was complete. It looks, what 1d7afc9 violate TI specs in what how AL and NACK should be handled (see Note 1, sprugn4r, Figure 17-31 and Figure 17-32). According to specs (if I understood correctly), in case of NACK and AL driver must reset NACK, AL, ARDY, RDR, and RRDY (Master Receive Mode), and NACK, AL, ARDY, and XDR (Master Transmitter Mode). All that is done down the code under the if condition: if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) ... The patch restore pre 1d7afc9 logic of handling NACK and AL interrupts, so no interrupts is fired after ISR informs the rest of driver what transfer complete. Note: instead of removing break under NACK case, we could just replace 'break' with 'continue' and allow NACK transfer to finish using ARDY event. I found that NACK and ARDY bits usually set together. That case confirm TI wiki: http://processors.wiki.ti.com/index.php/I2C_Tips#Detecting_and_handling_NACK In order if someone interested in the event traces for NACK and AL cases, I sent them to mailing list. Tested on Beagleboard XM C. Signed-off-by: Alexander Kochetkov Fixes: 1d7afc9 i2c: omap: ack IRQ in parts Cc: # v3.7+ Acked-by: Felipe Balbi Tested-by: Aaro Koskinen Signed-off-by: Wolfram Sang diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 26942c1..32dc651 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -922,14 +922,12 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) if (stat & OMAP_I2C_STAT_NACK) { err |= OMAP_I2C_STAT_NACK; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK); - break; } if (stat & OMAP_I2C_STAT_AL) { dev_err(dev->dev, "Arbitration lost\n"); err |= OMAP_I2C_STAT_AL; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL); - break; } /* -- cgit v0.10.2 From d39f77b06a712fcba6185a20bb209e357923d980 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 7 Nov 2014 12:10:44 +0000 Subject: i2c: designware: prevent early stop on TX FIFO empty If the Designware core is configured with IC_EMPTYFIFO_HOLD_MASTER_EN set to zero, allowing the TX FIFO to become empty causes a STOP condition to be generated on the I2C bus. If the transmit FIFO threshold is set too high, an erroneous STOP condition can be generated on long transfers - particularly where the interrupt latency is extended. Signed-off-by: Andrew Jackson Signed-off-by: Liviu Dudau Tested-by: Mika Westerberg Signed-off-by: Wolfram Sang diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index edca99d..23628b7 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -359,7 +359,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev) } /* Configure Tx/Rx FIFO threshold levels */ - dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL); + dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL); dw_writel(dev, 0, DW_IC_RX_TL); /* configure the i2c master */ -- cgit v0.10.2 From 1a28fc190c60e9bb04649384826f3224c8463efc Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 21 Nov 2014 19:06:15 +0800 Subject: ASoC: Intel: byt_rvp_platform_data can be static sound/soc/intel/sst/sst_acpi.c:124:26: sparse: symbol 'byt_rvp_platform_data' was not declared. Should it be static? Signed-off-by: Fengguang Wu Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index b261821..f94f007 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -121,7 +121,7 @@ static const struct sst_res_info byt_rvp_res_info = { .acpi_ipc_irq_index = 5, }; -struct sst_platform_info byt_rvp_platform_data = { +static struct sst_platform_info byt_rvp_platform_data = { .probe_data = &byt_fwparse_info, .ipc_info = &byt_ipc_info, .lib_info = &byt_lib_dnld_info, -- cgit v0.10.2 From 3c787b108fe0d1c341a76e718a25897ae14673cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Ha=C5=82asa?= Date: Fri, 14 Nov 2014 09:35:06 -0300 Subject: [media] solo6x10: fix a race in IRQ handler The IRQs have to be acknowledged before they are serviced, otherwise some events may be skipped. Also, acknowledging IRQs just before returning from the handler doesn't leave enough time for the device to deassert the INTx line, and for bridges to propagate this change. This resulted in twice the IRQ rate on ARMv6 dual core CPU. Signed-off-by: Krzysztof Ha?asa Acked-by: Andrey Utkin Tested-by: Andrey Utkin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 172583d..8cbe6b4 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -105,11 +105,8 @@ static irqreturn_t solo_isr(int irq, void *data) if (!status) return IRQ_NONE; - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } + /* Acknowledge all interrupts immediately */ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); if (status & SOLO_IRQ_PCI_ERR) solo_p2m_error_isr(solo_dev); @@ -132,9 +129,6 @@ static irqreturn_t solo_isr(int irq, void *data) if (status & SOLO_IRQ_G723) solo_g723_isr(solo_dev); - /* Clear all interrupts handled */ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); - return IRQ_HANDLED; } -- cgit v0.10.2 From 9661975dded8d39fa320218a6396b8be0972bb2f Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Mon, 17 Nov 2014 13:59:23 -0300 Subject: [media] Update MAINTAINERS for solo6x10 Signed-off-by: Andrey Utkin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index dab92a7..5af7cb6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8641,7 +8641,9 @@ S: Maintained F: drivers/leds/leds-net48xx.c SOFTLOGIC 6x10 MPEG CODEC -M: Ismael Luceno +M: Bluecherry Maintainers +M: Andrey Utkin +M: Andrey Utkin L: linux-media@vger.kernel.org S: Supported F: drivers/media/pci/solo6x10/ -- cgit v0.10.2 From 1f391217ad8d7cd7b1e48e6e2abf49970cd91d18 Mon Sep 17 00:00:00 2001 From: sensoray-dev Date: Mon, 17 Nov 2014 19:50:36 -0300 Subject: [media] s2255drv: fix payload size for JPG, MJPEG length is the size of the buffer, not the payload. That's set using vb2_set_plane_payload(). Signed-off-by: Dean Anderson Cc: # for v3.15 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index ccc0009..1c0dbf4 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -632,7 +632,7 @@ static void s2255_fillbuff(struct s2255_vc *vc, break; case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: - buf->vb.v4l2_buf.length = jpgsize; + vb2_set_plane_payload(&buf->vb, 0, jpgsize); memcpy(vbuf, tmpbuf, jpgsize); break; case V4L2_PIX_FMT_YUV422P: -- cgit v0.10.2 From 7675fe99d280ea83388a4382c54573c80db37cda Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 21 Nov 2014 06:20:56 -0300 Subject: [media] cx23885: use sg = sg_next(sg) instead of sg++ The cx23885 driver still used sg++ instead of sg = sg_next(sg). This worked with vb1 since that filled in the sglist manually, page-by-page, but it fails with vb2 which uses core scatterlist code that can combine contiguous scatterlist entries into one larger entry. This bug led to the following crash as reported by Mariusz: [20712.990258] BUG: Bad page state in process vb2-cx23885[0] pfn:2ca34 [20712.990265] page:ffffea00009c3b60 count:-1 mapcount:0 mapping: (null) index:0x0 [20712.990266] flags: 0x4000000000000000() [20712.990268] page dumped because: nonzero _count [20712.990269] Modules linked in: tun binfmt_misc nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables xt_mark xt_REDIRECT xt_limit xt_conntrack xt_nat xt_tcpudp iptable_mangle iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_filter ip_tables x_tables sit ip_tunnel nvidia(PO) stb6100 stv090x cx88_dvb videobuf_dvb cx88_vp3054_i2c tuner kvm_amd kvm cx8802 k10temp cx8800 cx88xx btcx_risc videobuf_dma_sg videobuf_core usb_storage ds2490 usbhid ftdi_sio cx23885 tveeprom cx2341x videobuf2_dvb videobuf2_core videobuf2_dma_sg videobuf2_memops asus_atk0110 snd_emu10k1 snd_hwdep snd_util_mem snd_ac97_codec ac97_bus snd_rawmidi snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd w1_therm wire ipv6 [20712.990301] CPU: 2 PID: 26942 Comm: vb2-cx23885[0] Tainted: P B W O 3.18.0-rc5-00001-gb3652d1 #2 [20712.990303] Hardware name: System manufacturer System Product Name/M4A785TD-V EVO, BIOS 2105 07/23/2010 [20712.990305] ffffffff81765734 ffff880137683a78 ffffffff815b6b32 0000000000000006 [20712.990307] ffffea00009c3b60 ffff880137683aa8 ffffffff8108ec27 ffffffff81765712 [20712.990309] ffffffff8189c840 0000000000000246 ffffea00009c3b60 ffff880137683b78 [20712.990312] Call Trace: [20712.990317] [] dump_stack+0x46/0x58 [20712.990321] [] bad_page+0xe9/0x107 [20712.990323] [] get_page_from_freelist+0x3b2/0x505 [20712.990326] [] __alloc_pages_nodemask+0xed/0x65f [20712.990330] [] ? ttwu_do_activate.constprop.78+0x57/0x5c [20712.990332] [] ? try_to_wake_up+0x21b/0x22d [20712.990336] [] dma_generic_alloc_coherent+0x6e/0xf5 [20712.990339] [] gart_alloc_coherent+0x105/0x114 [20712.990341] [] ? flush_gart+0x39/0x3d [20712.990343] [] ? gart_map_sg+0x3a0/0x3a0 [20712.990349] [] cx23885_risc_databuffer+0xa7/0x133 [cx23885] [20712.990354] [] cx23885_buf_prepare+0x121/0x134 [cx23885] [20712.990359] [] buffer_prepare+0x14/0x16 [cx23885] [20712.990363] [] __buf_prepare+0x190/0x279 [videobuf2_core] [20712.990366] [] ? vb2_queue_or_prepare_buf+0xb8/0xc0 [videobuf2_core] [20712.990369] [] vb2_internal_qbuf+0x51/0x1e5 [videobuf2_core] [20712.990372] [] vb2_thread+0x199/0x1f6 [videobuf2_core] [20712.990376] [] ? vb2_fop_write+0xdf/0xdf [videobuf2_core] [20712.990379] [] kthread+0xdf/0xe7 [20712.990381] [] ? kthread_create_on_node+0x16d/0x16d [20712.990384] [] ret_from_fork+0x7c/0xb0 [20712.990386] [] ? kthread_create_on_node+0x16d/0x16d Signed-off-by: Hans Verkuil Reported-by: Mariusz Bialonczyk Tested-by: Mariusz Bialonczyk Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 331edda..3bd386c 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1078,7 +1078,7 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, for (line = 0; line < lines; line++) { while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); - sg++; + sg = sg_next(sg); } if (lpi && line > 0 && !(line % lpi)) @@ -1101,14 +1101,14 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, *(rp++) = cpu_to_le32(0); /* bits 63-32 */ todo -= (sg_dma_len(sg)-offset); offset = 0; - sg++; + sg = sg_next(sg); while (todo > sg_dma_len(sg)) { *(rp++) = cpu_to_le32(RISC_WRITE| sg_dma_len(sg)); *(rp++) = cpu_to_le32(sg_dma_address(sg)); *(rp++) = cpu_to_le32(0); /* bits 63-32 */ todo -= sg_dma_len(sg); - sg++; + sg = sg_next(sg); } *(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo); *(rp++) = cpu_to_le32(sg_dma_address(sg)); -- cgit v0.10.2 From 009a5410337224896e300a570e185308836a2f14 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Nov 2014 15:48:27 -0200 Subject: MAINTAINERS: Update mchehab's addresses I'm using the new Open Source Group address for my upstream work. While the other email is still valid, it is better for me to receive patches via the new address. So, replace it everywhere inside MAINTAINERS. Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 5af7cb6..629c5b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1827,7 +1827,7 @@ F: include/net/ax25.h F: net/ax25/ AZ6007 DVB DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -2196,7 +2196,7 @@ F: Documentation/filesystems/btrfs.txt F: fs/btrfs/ BTTV VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -2717,7 +2717,7 @@ F: drivers/media/common/cx2341x* F: include/media/cx2341x* CX88 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -3386,7 +3386,7 @@ F: fs/ecryptfs/ EDAC-CORE M: Doug Thompson M: Borislav Petkov -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Supported @@ -3435,7 +3435,7 @@ S: Maintained F: drivers/edac/e7xxx_edac.c EDAC-GHES -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained @@ -3463,21 +3463,21 @@ S: Maintained F: drivers/edac/i5000_edac.c EDAC-I5400 -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i5400_edac.c EDAC-I7300 -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i7300_edac.c EDAC-I7CORE -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained @@ -3520,7 +3520,7 @@ S: Maintained F: drivers/edac/r82600_edac.c EDAC-SBRIDGE -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained @@ -3580,7 +3580,7 @@ S: Maintained F: drivers/net/ethernet/ibm/ehea/ EM28XX VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -5941,7 +5941,7 @@ S: Maintained F: drivers/media/radio/radio-maxiradio* MEDIA INPUT INFRASTRUCTURE (V4L/DVB) -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab P: LinuxTV.org Project L: linux-media@vger.kernel.org W: http://linuxtv.org @@ -7970,7 +7970,7 @@ S: Odd Fixes F: drivers/media/i2c/saa6588* SAA7134 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -8428,7 +8428,7 @@ S: Maintained F: drivers/media/radio/si4713/radio-usb-si4713.c SIANO DVB DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -9117,7 +9117,7 @@ S: Maintained F: drivers/media/i2c/tda9840* TEA5761 TUNER DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -9125,7 +9125,7 @@ S: Odd fixes F: drivers/media/tuners/tea5761.* TEA5767 TUNER DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -9437,7 +9437,7 @@ F: include/linux/shmem_fs.h F: mm/shmem.c TM6000 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -10264,7 +10264,7 @@ S: Maintained F: arch/x86/kernel/cpu/mcheck/* XC2028/3028 TUNER DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://linuxtv.org/media_tree.git -- cgit v0.10.2 From d2a74581390d8e5ed09b12c9d4736847d918dfa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 20 Nov 2014 18:09:54 -0300 Subject: [media] rc-core: fix toggle handling in the rc6 decoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The toggle bit shouldn't be cleared before the toggle value is calculated. This should probably go into 3.17.x as well. Fixes: 120703f9eb32 ([media] rc-core: document the protocol type) Cc: stable@vger.kernel.org # For v3.17 Tested-by: Stephan Raue Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index f1f098e..d16bc67 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -259,8 +259,8 @@ again: case 32: if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { protocol = RC_TYPE_RC6_MCE; - scancode &= ~RC6_6A_MCE_TOGGLE_MASK; toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); + scancode &= ~RC6_6A_MCE_TOGGLE_MASK; } else { protocol = RC_BIT_RC6_6A_32; toggle = 0; -- cgit v0.10.2 From b2de1d20a05d8691cb1889c859de2ab56938b82a Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 20 Nov 2014 15:33:17 +0530 Subject: ASoC: samsung: ASoC: samsung: Fix IISMOD setting in i2s_set_sysclk() In the i2s_set_sysclk() callback we are currently clearing all bits of the IISMOD register in i2s_set_sysclk. It's due to an incorrect mask used for the AND operation which is introduced in commit a5a56871f804edac93a53b5e871c0e9818fb9033 (ASoC: samsung: add support for exynos7 I2S controller) and also adds the missing break statement. Signed-off-by: Sylwester Nawrocki Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 947352d..0d76bc1 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -494,7 +494,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, if (dir == SND_SOC_CLOCK_IN) mod |= 1 << i2s_regs->cdclkcon_off; else - mod &= 0 << i2s_regs->cdclkcon_off; + mod &= ~(1 << i2s_regs->cdclkcon_off); i2s->rfs = rfs; break; @@ -551,10 +551,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } if (clk_id == 0) - mod &= 0 << i2s_regs->rclksrc_off; + mod &= ~(1 << i2s_regs->rclksrc_off); else mod |= 1 << i2s_regs->rclksrc_off; + break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); return -EINVAL; -- cgit v0.10.2 From 335ca471eebf130d88cb94c1192568b6c75aa9b0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 20 Nov 2014 21:28:15 +0100 Subject: ASoC: sirf-audio-codec: Replace w->codec snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. While we are at it also replace dev_get_drvdata() with snd_soc_codec_get_drvdata(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c index 06ba492..07eea20 100644 --- a/sound/soc/codecs/sirf-audio-codec.c +++ b/sound/soc/codecs/sirf-audio-codec.c @@ -120,7 +120,8 @@ static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, { #define ATLAS6_CODEC_ENABLE_BITS (1 << 29) #define ATLAS6_CODEC_RESET_BITS (1 << 28) - struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_PRE_PMU: enable_and_reset_codec(sirf_audio_codec->regmap, @@ -142,7 +143,8 @@ static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, { #define PRIMA2_CODEC_ENABLE_BITS (1 << 27) #define PRIMA2_CODEC_RESET_BITS (1 << 26) - struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMU: enable_and_reset_codec(sirf_audio_codec->regmap, -- cgit v0.10.2 From 0b5155bbca8b5a8a1456ae462a47eeaedf8ce091 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 20 Nov 2014 21:21:53 +0100 Subject: ASoC: max98088: Replace w->codec snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 2cd3e54..abf3832 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -875,7 +875,7 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = { static int max98088_mic_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); switch (event) { @@ -905,7 +905,7 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w, static int max98088_line_pga(struct snd_soc_dapm_widget *w, int event, int line, u8 channel) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); u8 *state; -- cgit v0.10.2 From 24445f8c5eae926e402335bbe0292f09b1deb7a7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 20 Nov 2014 21:21:54 +0100 Subject: ASoC: max98090: Replace w->codec snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index a65861c..2ad381c 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -806,7 +806,7 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = { static int max98090_micinput_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); unsigned int val = snd_soc_read(codec, w->reg); -- cgit v0.10.2 From 0db5dc943e7649bbfbc2d2de8f5cb778b05ea5bd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 20 Nov 2014 21:21:55 +0100 Subject: ASoC: max98095: Replace w->codec snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 42103ca..d911d4c 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -864,7 +864,7 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = { static int max98095_mic_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); switch (event) { @@ -894,7 +894,7 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w, static int max98095_line_pga(struct snd_soc_dapm_widget *w, int event, u8 channel) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); u8 *state; @@ -942,7 +942,7 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w, static int max98095_lineout_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: -- cgit v0.10.2 From dee9cec42fc9cc4635ea2f45939e443210a638f8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 21 Nov 2014 18:53:51 +0100 Subject: ASoC: adau17x1: Mark DSP parameter memory as readable and precious To be able to read back data from the DSP parameter memory the register range needs to be marked as readable. At the same time we do not want them to e.g. appear in debugfs output so mark them as precious as well. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 0ae1501..4c018c5 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -793,6 +793,7 @@ const struct regmap_config adau1761_regmap_config = { .num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults), .readable_reg = adau1761_readable_register, .volatile_reg = adau17x1_volatile_register, + .precious_reg = adau17x1_precious_register, .cache_type = REGCACHE_RBTREE, }; EXPORT_SYMBOL_GPL(adau1761_regmap_config); diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index 4c8ddc3..926fc99 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -472,6 +472,7 @@ const struct regmap_config adau1781_regmap_config = { .num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults), .readable_reg = adau1781_readable_register, .volatile_reg = adau17x1_volatile_register, + .precious_reg = adau17x1_precious_register, .cache_type = REGCACHE_RBTREE, }; EXPORT_SYMBOL_GPL(adau1781_regmap_config); diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 1cab34c..5000047 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -706,8 +706,22 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage); +bool adau17x1_precious_register(struct device *dev, unsigned int reg) +{ + /* SigmaDSP parameter memory */ + if (reg < 0x400) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(adau17x1_precious_register); + bool adau17x1_readable_register(struct device *dev, unsigned int reg) { + /* SigmaDSP parameter memory */ + if (reg < 0x400) + return true; + switch (reg) { case ADAU17X1_CLOCK_CONTROL: case ADAU17X1_PLL_CONTROL: diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index 6861aa3..e13583e 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -56,6 +56,7 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec, enum adau17x1_micbias_voltage micbias); bool adau17x1_readable_register(struct device *dev, unsigned int reg); bool adau17x1_volatile_register(struct device *dev, unsigned int reg); +bool adau17x1_precious_register(struct device *dev, unsigned int reg); int adau17x1_resume(struct snd_soc_codec *codec); extern const struct snd_soc_dai_ops adau17x1_dai_ops; -- cgit v0.10.2 From 1fc10044d76e86b71f724988c7cbd8205bb903a8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 21 Nov 2014 18:53:52 +0100 Subject: ASoC: sigmadsp: Fix endianness conversion Make sure to always convert the firmware data to local endianness before using it. Reported-by: kbuild test robot Fixes: a35daac77a03 ("ASoC: sigmadsp: Add support for fw v2") Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 55af596..6abefd2 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -235,7 +235,7 @@ static int sigma_fw_load_control(struct sigmadsp *sigmadsp, ctrl->addr = le16_to_cpu(ctrl_chunk->addr); ctrl->num_bytes = num_bytes; - ctrl->samplerates = chunk->samplerates; + ctrl->samplerates = le32_to_cpu(chunk->samplerates); list_add_tail(&ctrl->head, &sigmadsp->ctrl_list); @@ -266,7 +266,7 @@ static int sigma_fw_load_data(struct sigmadsp *sigmadsp, data->addr = le16_to_cpu(data_chunk->addr); data->length = length; - data->samplerates = chunk->samplerates; + data->samplerates = le32_to_cpu(chunk->samplerates); memcpy(data->data, data_chunk->data, length); list_add_tail(&data->head, &sigmadsp->data_list); @@ -329,7 +329,7 @@ static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp, if (length > fw->size - pos || length < sizeof(*chunk)) return -EINVAL; - switch (chunk->tag) { + switch (le32_to_cpu(chunk->tag)) { case SIGMA_FW_CHUNK_TYPE_DATA: ret = sigma_fw_load_data(sigmadsp, chunk, length); break; -- cgit v0.10.2 From e2280c9040d8bc5039617af35ccf7b8ac4abb428 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Thu, 20 Nov 2014 19:07:48 +0800 Subject: ASoC: wm8960: Add device tree support Document the device tree binding for the WM8960 codec, and modify the driver to extract the platform data from device tree, if present. Signed-off-by: Zidan Wang Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/wm8960.txt b/Documentation/devicetree/bindings/sound/wm8960.txt new file mode 100644 index 0000000..2deb8a3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wm8960.txt @@ -0,0 +1,31 @@ +WM8960 audio CODEC + +This device supports I2C only. + +Required properties: + + - compatible : "wlf,wm8960" + + - reg : the I2C address of the device. + +Optional properties: + - wlf,shared-lrclk: This is a boolean property. If present, the LRCM bit of + R24 (Additional control 2) gets set, indicating that ADCLRC and DACLRC pins + will be disabled only when ADC (Left and Right) and DAC (Left and Right) + are disabled. + When wm8960 works on synchronize mode and DACLRC pin is used to supply + frame clock, it will no frame clock for captrue unless enable DAC to enable + DACLRC pin. If shared-lrclk is present, no need to enable DAC for captrue. + + - wlf,capless: This is a boolean property. If present, OUT3 pin will be + enabled and disabled together with HP_L and HP_R pins in response to jack + detect events. + +Example: + +codec: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + + wlf,shared-lrclk; +}; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 4dc4e85..99d6457 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -125,6 +125,7 @@ struct wm8960_priv { struct snd_soc_dapm_widget *out3; bool deemph; int playback_fs; + struct wm8960_data pdata; }; #define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) @@ -440,8 +441,8 @@ static const struct snd_soc_dapm_route audio_paths_capless[] = { static int wm8960_add_widgets(struct snd_soc_codec *codec) { - struct wm8960_data *pdata = codec->dev->platform_data; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_data *pdata = &wm8960->pdata; struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dapm_widget *w; @@ -961,17 +962,13 @@ static int wm8960_resume(struct snd_soc_codec *codec) static int wm8960_probe(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - struct wm8960_data *pdata = dev_get_platdata(codec->dev); + struct wm8960_data *pdata = &wm8960->pdata; int ret; - wm8960->set_bias_level = wm8960_set_bias_level_out3; - - if (!pdata) { - dev_warn(codec->dev, "No platform data supplied\n"); - } else { - if (pdata->capless) - wm8960->set_bias_level = wm8960_set_bias_level_capless; - } + if (pdata->capless) + wm8960->set_bias_level = wm8960_set_bias_level_capless; + else + wm8960->set_bias_level = wm8960_set_bias_level_out3; ret = wm8960_reset(codec); if (ret < 0) { @@ -1029,6 +1026,18 @@ static const struct regmap_config wm8960_regmap = { .volatile_reg = wm8960_volatile, }; +static void wm8960_set_pdata_from_of(struct i2c_client *i2c, + struct wm8960_data *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + + if (of_property_read_bool(np, "wlf,capless")) + pdata->capless = true; + + if (of_property_read_bool(np, "wlf,shared-lrclk")) + pdata->shared_lrclk = true; +} + static int wm8960_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1045,6 +1054,11 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, if (IS_ERR(wm8960->regmap)) return PTR_ERR(wm8960->regmap); + if (pdata) + memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data)); + else if (i2c->dev.of_node) + wm8960_set_pdata_from_of(i2c, &wm8960->pdata); + if (pdata && pdata->shared_lrclk) { ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 0x4, 0x4); @@ -1075,10 +1089,17 @@ static const struct i2c_device_id wm8960_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); +static const struct of_device_id wm8960_of_match[] = { + { .compatible = "wlf,wm8960", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8960_of_match); + static struct i2c_driver wm8960_i2c_driver = { .driver = { .name = "wm8960", .owner = THIS_MODULE, + .of_match_table = wm8960_of_match, }, .probe = wm8960_i2c_probe, .remove = wm8960_i2c_remove, -- cgit v0.10.2 From ceb3c0683cfc5dcc2b627985143105f6dfb0b324 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 20 Nov 2014 21:05:38 +0100 Subject: ASoC: cs42l51: Replace w->codec snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 09488d9..3142baf 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -153,15 +153,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = { static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + switch (event) { case SND_SOC_DAPM_PRE_PMD: - snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1, + snd_soc_update_bits(codec, CS42L51_POWER_CTL1, CS42L51_POWER_CTL1_PDN, CS42L51_POWER_CTL1_PDN); break; default: case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1, + snd_soc_update_bits(codec, CS42L51_POWER_CTL1, CS42L51_POWER_CTL1_PDN, 0); break; } -- cgit v0.10.2 From 6e2793b98e23372cc80d9b5d981ab2467e90acea Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 20 Nov 2014 21:05:39 +0100 Subject: ASoC: cs42l73: Replace w->codec snd_soc_dapm_to_codec(w->dapm) The codec field of the snd_soc_widget struct is eventually going to be removed, use snd_soc_dapm_to_codec(w->dapm) instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 2f8b946..7c55537 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -584,7 +584,7 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = { static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMD: @@ -600,7 +600,7 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w, static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMD: @@ -618,7 +618,7 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w, static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMD: -- cgit v0.10.2 From 026da220c512f6ab706cc9f738439f900b564967 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 21 Nov 2014 16:08:59 +0800 Subject: ASoC: Intel: Add Cherrytrail & Braswell machine driver cht_bsw_rt5672 Add machine driver for two Intel Cherryview-based platforms, Cherrytrail and Braswell, with RT5672 codec. Signed-off-by: Mengdong Lin Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index a26e8e8..e989ecf 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -98,3 +98,15 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH used as alsa device in audio substem in Intel(R) MID devices Say Y if you have such a device If unsure select "N". + +config SND_SOC_INTEL_CHT_BSW_RT5672_MACH + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" + depends on X86_INTEL_LPSS + select SND_SOC_RT5670 + select SND_SST_MFLD_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with RT5672 audio codec. + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index fbde4b07..e928ec3 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -27,12 +27,14 @@ snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o +snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o +obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o # DSP driver obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c new file mode 100644 index 0000000..9b8b561 --- /dev/null +++ b/sound/soc/intel/cht_bsw_rt5672.c @@ -0,0 +1,285 @@ +/* + * cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms + * Cherrytrail and Braswell, with RT5672 codec. + * + * Copyright (C) 2014 Intel Corp + * Author: Subhransu S. Prusty + * Mengdong Lin + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include "../codecs/rt5670.h" +#include "sst-atom-controls.h" + +/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ +#define CHT_PLAT_CLK_3_HZ 19200000 +#define CHT_CODEC_DAI "rt5670-aif1" + +static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) +{ + int i; + + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_pcm_runtime *rtd; + + rtd = card->rtd + i; + if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, + strlen(CHT_CODEC_DAI))) + return rtd->codec_dai; + } + return NULL; +} + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + + codec_dai = cht_get_codec_dai(card); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (!SND_SOC_DAPM_EVENT_OFF(event)) + return 0; + + /* Set codec sysclk source to its internal clock because codec PLL will + * be off when idle and MCLK will also be off by ACPI when codec is + * runtime suspended. Codec needs clock for jack detection and button + * press. + */ + snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, + 0, SND_SOC_CLOCK_IN); + + return 0; +} + +static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route cht_audio_map[] = { + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + {"DMIC L1", NULL, "Int Mic"}, + {"DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Ext Spk", NULL, "SPOLP"}, + {"Ext Spk", NULL, "SPOLN"}, + {"Ext Spk", NULL, "SPORP"}, + {"Ext Spk", NULL, "SPORN"}, + {"AIF1 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx"}, + {"codec_in1", NULL, "ssp2 Rx"}, + {"ssp2 Rx", NULL, "AIF1 Capture"}, + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Int Mic", NULL, "Platform Clock"}, + {"Ext Spk", NULL, "Platform Clock"}, +}; + +static const struct snd_kcontrol_new cht_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static int cht_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + /* set codec PLL source to the 19.2MHz platform clock (MCLK) */ + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK, + CHT_PLAT_CLK_3_HZ, params_rate(params) * 512); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + /* set codec sysclk source to PLL */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + return 0; +} + +static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); + return ret; + } + + return 0; +} + +static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cht_aif1_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops cht_aif1_ops = { + .startup = cht_aif1_startup, +}; + +static struct snd_soc_ops cht_be_ssp2_ops = { + .hw_params = cht_aif1_hw_params, +}; + +static struct snd_soc_dai_link cht_dailink[] = { + /* Front End DAI links */ + [MERR_DPCM_AUDIO] = { + .name = "Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "media-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .ignore_suspend = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cht_aif1_ops, + }, + [MERR_DPCM_COMPR] = { + .name = "Compressed Port", + .stream_name = "Compress", + .cpu_dai_name = "compress-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + }, + + /* Back End DAI links */ + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .be_id = 1, + .cpu_dai_name = "ssp2-port", + .platform_name = "sst-mfld-platform", + .no_pcm = 1, + .codec_dai_name = "rt5670-aif1", + .codec_name = "i2c-10EC5670:00", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .init = cht_codec_init, + .be_hw_params_fixup = cht_codec_fixup, + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cht_be_ssp2_ops, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cht = { + .name = "cherrytrailcraudio", + .dai_link = cht_dailink, + .num_links = ARRAY_SIZE(cht_dailink), + .dapm_widgets = cht_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), + .dapm_routes = cht_audio_map, + .num_dapm_routes = ARRAY_SIZE(cht_audio_map), + .controls = cht_mc_controls, + .num_controls = ARRAY_SIZE(cht_mc_controls), +}; + +static int snd_cht_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + + /* register the soc card */ + snd_soc_card_cht.dev = &pdev->dev; + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); + if (ret_val) { + dev_err(&pdev->dev, + "snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, &snd_soc_card_cht); + return ret_val; +} + +static struct platform_driver snd_cht_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cht-bsw-rt5672", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_cht_mc_probe, +}; + +module_platform_driver(snd_cht_mc_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); +MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cht-bsw-rt5672"); -- cgit v0.10.2 From bd01fdc3aa63b7ba0b035f9196d80551ad03f5d4 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 21 Nov 2014 16:09:17 +0800 Subject: ASoC: Intel: add support for Cherrytrail and Braswell in SST driver This patch add ACPI device ID and platform data for two Cherryview-based platforms, Cherrytrail and Braswell. Also reuse mfld driver ops in sst driver. Signed-off-by: Mengdong Lin Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 9e68a7c..8a8d56a 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -183,6 +183,7 @@ int sst_driver_ops(struct intel_sst_drv *sst) switch (sst->dev_id) { case SST_MRFLD_PCI_ID: case SST_BYT_ACPI_ID: + case SST_CHV_ACPI_ID: sst->tstamp = SST_TIME_STAMP_MRFLD; sst->ops = &mrfld_ops; return 0; diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index 683dc71..7f4bbfc 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -30,6 +30,7 @@ #define SST_DRV_NAME "intel_sst_driver" #define SST_MRFLD_PCI_ID 0x119A #define SST_BYT_ACPI_ID 0x80860F28 +#define SST_CHV_ACPI_ID 0x808622A8 #define SST_SUSPEND_DELAY 2000 #define FW_CONTEXT_MEM (64*1024) diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index f94f007..3f29721 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -129,6 +129,17 @@ static struct sst_platform_info byt_rvp_platform_data = { .platform = "sst-mfld-platform", }; +/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail, + * so pdata is same as Baytrail. + */ +struct sst_platform_info chv_platform_data = { + .probe_data = &byt_fwparse_info, + .ipc_info = &byt_ipc_info, + .lib_info = &byt_lib_dnld_info, + .res_info = &byt_rvp_res_info, + .platform = "sst-mfld-platform", +}; + static int sst_platform_get_resources(struct intel_sst_drv *ctx) { struct resource *rsrc; @@ -337,8 +348,16 @@ static struct sst_machines sst_acpi_bytcr[] = { {}, }; +/* Cherryview-based platforms: CherryTrail and Braswell */ +static struct sst_machines sst_acpi_chv[] = { + {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin", + &chv_platform_data }, + {}, +}; + static const struct acpi_device_id sst_acpi_ids[] = { { "80860F28", (unsigned long)&sst_acpi_bytcr}, + { "808622A8", (unsigned long) &sst_acpi_chv}, { }, }; -- cgit v0.10.2 From 075207d24a394bcdb3a864446f391d4014a04cd4 Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Mon, 17 Nov 2014 16:02:57 +0800 Subject: ASoC: soc-pcm: skip dpcm path checking with incapable/unready FE Skip dpcm path checking for playback or capture, if corresponding FE doesn't support playback or capture, or currently is not ready. It can reduce the unnecessary cost to search connected widgets. [Tweaked comments for clarity -- broonie] Signed-off-by: Qiao Zhou Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 002311a..2f4f074 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2248,7 +2248,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card) fe->dai_link->name); /* skip if FE doesn't have playback capability */ - if (!fe->cpu_dai->driver->playback.channels_min) + if (!fe->cpu_dai->driver->playback.channels_min + || !fe->codec_dai->driver->playback.channels_min) + goto capture; + + /* skip if FE isn't currently playing */ + if (!fe->cpu_dai->playback_active + || !fe->codec_dai->playback_active) goto capture; paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); @@ -2278,7 +2284,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card) dpcm_path_put(&list); capture: /* skip if FE doesn't have capture capability */ - if (!fe->cpu_dai->driver->capture.channels_min) + if (!fe->cpu_dai->driver->capture.channels_min + || !fe->codec_dai->driver->capture.channels_min) + continue; + + /* skip if FE isn't currently capturing */ + if (!fe->cpu_dai->capture_active + || !fe->codec_dai->capture_active) continue; paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); -- cgit v0.10.2 From 14cd7923122c7c4473848f7c737604bfe945b81b Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sat, 22 Nov 2014 04:50:33 +0800 Subject: ASoC: Intel: chv_platform_data can be static sound/soc/intel/sst/sst_acpi.c:135:26: sparse: symbol 'chv_platform_data' was not declared. Should it be static? Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index 3f29721..31124aa 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -132,7 +132,7 @@ static struct sst_platform_info byt_rvp_platform_data = { /* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail, * so pdata is same as Baytrail. */ -struct sst_platform_info chv_platform_data = { +static struct sst_platform_info chv_platform_data = { .probe_data = &byt_fwparse_info, .ipc_info = &byt_ipc_info, .lib_info = &byt_lib_dnld_info, -- cgit v0.10.2 From ccfc866356674cb3a61829d239c685af6e85f197 Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Fri, 21 Nov 2014 04:16:51 +0400 Subject: i2c: omap: fix i207 errata handling commit 6d9939f651419a63e091105663821f9c7d3fec37 (i2c: omap: split out [XR]DR and [XR]RDY) changed the way how errata i207 (I2C: RDR Flag May Be Incorrectly Set) get handled. 6d9939f6514 code doesn't correspond to workaround provided by errata. According to errata ISR must filter out spurious RDR before data read not after. ISR must read RXSTAT to get number of bytes available to read. Because RDR could be set while there could no data in the receive FIFO. Restored pre 6d9939f6514 way of handling errata. Found by code review. Real impact haven't seen. Tested on Beagleboard XM C. Signed-off-by: Alexander Kochetkov Fixes: 6d9939f651419a63e09110 i2c: omap: split out [XR]DR and [XR]RDY Tested-by: Felipe Balbi Reviewed-by: Felipe Balbi Signed-off-by: Wolfram Sang diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 32dc651..277a228 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -952,11 +952,13 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) if (dev->fifo_size) num_bytes = dev->buf_len; - omap_i2c_receive_data(dev, num_bytes, true); - - if (dev->errata & I2C_OMAP_ERRATA_I207) + if (dev->errata & I2C_OMAP_ERRATA_I207) { i2c_omap_errata_i207(dev, stat); + num_bytes = (omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F; + } + omap_i2c_receive_data(dev, num_bytes, true); omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); continue; } -- cgit v0.10.2 From e2e68ae688b0a3766cd75aedf4ed4e39be402009 Mon Sep 17 00:00:00 2001 From: Chris Clayton Date: Sat, 22 Nov 2014 09:51:10 +0000 Subject: x86: Use $(OBJDUMP) instead of plain objdump commit e6023367d779 'x86, kaslr: Prevent .bss from overlaping initrd' broke the cross compile of x86. It added a objdump invocation, which invokes the host native objdump and ignores an active cross tool chain. Use $(OBJDUMP) instead which takes the CROSS_COMPILE prefix into account. [ tglx: Massage changelog and use $(OBJDUMP) ] Fixes: e6023367d779 'x86, kaslr: Prevent .bss from overlaping initrd' Signed-off-by: Chris Clayton Acked-by: Kees Cook Acked-by: Borislav Petkov Cc: Junjie Mao Cc: Ingo Molnar Cc: H. Peter Anvin Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/54705C8E.1080400@googlemail.com Signed-off-by: Thomas Gleixner diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index be1e07d..45abc36 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -76,7 +76,7 @@ suffix-$(CONFIG_KERNEL_XZ) := xz suffix-$(CONFIG_KERNEL_LZO) := lzo suffix-$(CONFIG_KERNEL_LZ4) := lz4 -RUN_SIZE = $(shell objdump -h vmlinux | \ +RUN_SIZE = $(shell $(OBJDUMP) -h vmlinux | \ perl $(srctree)/arch/x86/tools/calc_run_size.pl) quiet_cmd_mkpiggy = MKPIGGY $@ cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false ) -- cgit v0.10.2 From 4cf703a7bca4c29d06028821db60f253390a84a7 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 24 Nov 2014 15:32:35 +0200 Subject: ASoC: max98090: Fix digital microphone Commit e409dfbfccf9 ("ASoC: dapm: Add a few supply widget sanity checks") broke digital microphone support in max98090.c: max98090 i2c-193C9890:00: Conditional paths are not supported for supply widgets (DMICL_ENA -> [DMIC] -> DMIC Mux) max98090 i2c-193C9890:00: ASoC: no dapm match for DMICL_ENA --> DMIC --> DMIC Mux max98090 i2c-193C9890:00: ASoC: Failed to add route DMICL_ENA -> DMIC -> DMIC Mux max98090 i2c-193C9890:00: Conditional paths are not supported for supply widgets (DMICR_ENA -> [DMIC] -> DMIC Mux) max98090 i2c-193C9890:00: ASoC: no dapm match for DMICR_ENA --> DMIC --> DMIC Mux max98090 i2c-193C9890:00: ASoC: Failed to add route DMICR_ENA -> DMIC -> DMIC Mux Problem is partially caused by commit f69e3caa9e18 ("ASoC: max98090: Enable both DMIC channels also when using mono configuration") which connects "DMICL_ENA" and "DMICR_ENA" supply widgets to "DMIC Mux". Fix the breakage by reverting f69e3caa9e18 and then by adding additional "DMICR_ENA" to "DMICL" and "DMICL_ENA" to "DMICR" cross-connections. This disconnects these supply widgets from the mux and makes sure that both DMIC data channels are still enabled together. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 1229554..994d02c 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1311,6 +1311,10 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"MIC1 Input", NULL, "MIC1"}, {"MIC2 Input", NULL, "MIC2"}, + {"DMICL", NULL, "DMICL_ENA"}, + {"DMICL", NULL, "DMICR_ENA"}, + {"DMICR", NULL, "DMICL_ENA"}, + {"DMICR", NULL, "DMICR_ENA"}, {"DMICL", NULL, "AHPF"}, {"DMICR", NULL, "AHPF"}, @@ -1368,8 +1372,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"DMIC Mux", "ADC", "ADCR"}, {"DMIC Mux", "DMIC", "DMICL"}, {"DMIC Mux", "DMIC", "DMICR"}, - {"DMIC Mux", "DMIC", "DMICL_ENA"}, - {"DMIC Mux", "DMIC", "DMICR_ENA"}, {"LBENL Mux", "Normal", "DMIC Mux"}, {"LBENL Mux", "Loopback", "LTENL Mux"}, -- cgit v0.10.2 From 48826ee590da03e9882922edf96d8d27bdfe9552 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 24 Nov 2014 15:32:36 +0200 Subject: ASoC: max98090: Fix ill-defined sidetone route Commit 5fe5b767dc6f ("ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets") revealed ill-defined control in a route between "STENL Mux" and DACs in max98090.c: max98090 i2c-193C9890:00: Control not supported for path STENL Mux -> [NULL] -> DACL max98090 i2c-193C9890:00: ASoC: no dapm match for STENL Mux --> NULL --> DACL max98090 i2c-193C9890:00: ASoC: Failed to add route STENL Mux -> NULL -> DACL max98090 i2c-193C9890:00: Control not supported for path STENL Mux -> [NULL] -> DACR max98090 i2c-193C9890:00: ASoC: no dapm match for STENL Mux --> NULL --> DACR max98090 i2c-193C9890:00: ASoC: Failed to add route STENL Mux -> NULL -> DACR Since there is no control between "STENL Mux" and DACs the control name must be NULL not "NULL". Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 994d02c..20b50e4 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1397,8 +1397,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"STENL Mux", "Sidetone Left", "DMICL"}, {"STENR Mux", "Sidetone Right", "ADCR"}, {"STENR Mux", "Sidetone Right", "DMICR"}, - {"DACL", "NULL", "STENL Mux"}, - {"DACR", "NULL", "STENL Mux"}, + {"DACL", NULL, "STENL Mux"}, + {"DACR", NULL, "STENL Mux"}, {"AIFINL", NULL, "SHDN"}, {"AIFINR", NULL, "SHDN"}, -- cgit v0.10.2 From 418382f29d99f1faffdd6636f378da41b44815db Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 24 Nov 2014 15:32:37 +0200 Subject: ASoC: max98090: Fix right sidetone connection It is right not left sidetone which goes to "DACR". Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 20b50e4..34ed9a9 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1398,7 +1398,7 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"STENR Mux", "Sidetone Right", "ADCR"}, {"STENR Mux", "Sidetone Right", "DMICR"}, {"DACL", NULL, "STENL Mux"}, - {"DACR", NULL, "STENL Mux"}, + {"DACR", NULL, "STENR Mux"}, {"AIFINL", NULL, "SHDN"}, {"AIFINR", NULL, "SHDN"}, -- cgit v0.10.2 From a6e4599f8d232b5911c46bb16f5a79b86f3dfb75 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 15:04:12 +0100 Subject: ASoC: uda134x: Remove is_powered_on_standby from platform data According to its documentation the is_powered_on_standby field of the uda134x platform data is supposed to prevent the the driver from shutting down the ADC and DAC in standby mode. This behavior was broken in commit commit f0fba2ad1b6b ("ASoC: multi-component - ASoC Multi-Component Support") almost 5 years ago and all the flag does now is cause the driver to go to SND_SOC_BIAS_ON in probe, just for the ASoC core to put it back into SND_SOC_BIAS_STANDBY right after probe. Apparently the intended behavior has not been missed, so just remove is_powered_on_standby from the platform data struct. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h index e475659..509efb0 100644 --- a/include/sound/uda134x.h +++ b/include/sound/uda134x.h @@ -18,18 +18,6 @@ struct uda134x_platform_data { struct l3_pins l3; void (*power) (int); int model; - /* - ALSA SOC usually puts the device in standby mode when it's not used - for sometime. If you unset is_powered_on_standby the driver will - turn off the ADC/DAC when this callback is invoked and turn it back - on when needed. Unfortunately this will result in a very light bump - (it can be audible only with good earphones). If this bothers you - set is_powered_on_standby, you will have slightly higher power - consumption. Please note that sending the L3 command for ADC is - enough to make the bump, so it doesn't make difference if you - completely take off power from the codec. - */ - int is_powered_on_standby; #define UDA134X_UDA1340 1 #define UDA134X_UDA1341 2 #define UDA134X_UDA1344 3 diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 32b2f78..54240f1 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -518,10 +518,7 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) uda134x_reset(codec); - if (pd->is_powered_on_standby) - uda134x_set_bias_level(codec, SND_SOC_BIAS_ON); - else - uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (pd->model == UDA134X_UDA1341) { widgets = uda1341_dapm_widgets; -- cgit v0.10.2 From e03b975506545d21b1daa5c8310b59d66e74919c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 15:04:13 +0100 Subject: ASoC: uda134x: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 54240f1..4056260 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -518,8 +518,6 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) uda134x_reset(codec); - uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (pd->model == UDA134X_UDA1341) { widgets = uda1341_dapm_widgets; num_widgets = ARRAY_SIZE(uda1341_dapm_widgets); @@ -571,44 +569,21 @@ static int uda134x_soc_remove(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); - uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF); - kfree(uda134x); return 0; } -#if defined(CONFIG_PM) -static int uda134x_soc_suspend(struct snd_soc_codec *codec) -{ - uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int uda134x_soc_resume(struct snd_soc_codec *codec) -{ - uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE); - uda134x_set_bias_level(codec, SND_SOC_BIAS_ON); - return 0; -} -#else -#define uda134x_soc_suspend NULL -#define uda134x_soc_resume NULL -#endif /* CONFIG_PM */ - static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .probe = uda134x_soc_probe, .remove = uda134x_soc_remove, - .suspend = uda134x_soc_suspend, - .resume = uda134x_soc_resume, .reg_cache_size = sizeof(uda134x_reg), .reg_word_size = sizeof(u8), .reg_cache_default = uda134x_reg, .reg_cache_step = 1, .read = uda134x_read_reg_cache, - .write = uda134x_write, .set_bias_level = uda134x_set_bias_level, + .suspend_bias_off = true, + .dapm_widgets = uda134x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), .dapm_routes = uda134x_dapm_routes, -- cgit v0.10.2 From e8125f04421f7757df0017a59cd9b756148ee769 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 15:04:14 +0100 Subject: ASoC: uda1380: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index e62e707..dc7778b 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -693,18 +693,6 @@ static struct snd_soc_dai_driver uda1380_dai[] = { }, }; -static int uda1380_suspend(struct snd_soc_codec *codec) -{ - uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int uda1380_resume(struct snd_soc_codec *codec) -{ - uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int uda1380_probe(struct snd_soc_codec *codec) { struct uda1380_platform_data *pdata =codec->dev->platform_data; @@ -739,8 +727,6 @@ static int uda1380_probe(struct snd_soc_codec *codec) INIT_WORK(&uda1380->work, uda1380_flush_work); - /* power on device */ - uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* set clock input */ switch (pdata->dac_clk) { case UDA1380_DAC_CLK_SYSCLK: @@ -766,8 +752,6 @@ static int uda1380_remove(struct snd_soc_codec *codec) { struct uda1380_platform_data *pdata =codec->dev->platform_data; - uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); - gpio_free(pdata->gpio_reset); gpio_free(pdata->gpio_power); @@ -777,11 +761,11 @@ static int uda1380_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { .probe = uda1380_probe, .remove = uda1380_remove, - .suspend = uda1380_suspend, - .resume = uda1380_resume, .read = uda1380_read_reg_cache, .write = uda1380_write, .set_bias_level = uda1380_set_bias_level, + .suspend_bias_off = true, + .reg_cache_size = ARRAY_SIZE(uda1380_reg), .reg_word_size = sizeof(u16), .reg_cache_default = uda1380_reg, -- cgit v0.10.2 From 2849bde56aac38645c5ed2af3971358b89a929f6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:47:50 +0100 Subject: ASoC: alc5623: Cleanup bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Also remove the manual sequencing back to SND_SOC_BIAS_ON in resume as this is already handled by the ASoC core. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 9d0755a..bdf8c5a 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -866,7 +866,6 @@ static int alc5623_suspend(struct snd_soc_codec *codec) { struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); - alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); regcache_cache_only(alc5623->regmap, true); return 0; @@ -887,15 +886,6 @@ static int alc5623_resume(struct snd_soc_codec *codec) return ret; } - alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* charge alc5623 caps */ - if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { - alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - codec->dapm.bias_level = SND_SOC_BIAS_ON; - alc5623_set_bias_level(codec, codec->dapm.bias_level); - } - return 0; } @@ -906,9 +896,6 @@ static int alc5623_probe(struct snd_soc_codec *codec) alc5623_reset(codec); - /* power on device */ - alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (alc5623->add_ctrl) { snd_soc_write(codec, ALC5623_ADD_CTRL_REG, alc5623->add_ctrl); @@ -964,19 +951,12 @@ static int alc5623_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int alc5623_remove(struct snd_soc_codec *codec) -{ - alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_device_alc5623 = { .probe = alc5623_probe, - .remove = alc5623_remove, .suspend = alc5623_suspend, .resume = alc5623_resume, .set_bias_level = alc5623_set_bias_level, + .suspend_bias_off = true, }; static const struct regmap_config alc5623_regmap = { -- cgit v0.10.2 From 5c9dc0898f343473efa3056fff9d5a9fbd577272 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:47:51 +0100 Subject: ASoC: alc5632: Cleanup bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 85942ca..d1fdbc2 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1038,23 +1038,15 @@ static struct snd_soc_dai_driver alc5632_dai = { }; #ifdef CONFIG_PM -static int alc5632_suspend(struct snd_soc_codec *codec) -{ - alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int alc5632_resume(struct snd_soc_codec *codec) { struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec); regcache_sync(alc5632->regmap); - alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } #else -#define alc5632_suspend NULL #define alc5632_resume NULL #endif @@ -1062,9 +1054,6 @@ static int alc5632_probe(struct snd_soc_codec *codec) { struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec); - /* power on device */ - alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - switch (alc5632->id) { case 0x5c: snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls, @@ -1077,19 +1066,12 @@ static int alc5632_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int alc5632_remove(struct snd_soc_codec *codec) -{ - alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_device_alc5632 = { .probe = alc5632_probe, - .remove = alc5632_remove, - .suspend = alc5632_suspend, .resume = alc5632_resume, .set_bias_level = alc5632_set_bias_level, + .suspend_bias_off = true, + .controls = alc5632_snd_controls, .num_controls = ARRAY_SIZE(alc5632_snd_controls), .dapm_widgets = alc5632_dapm_widgets, -- cgit v0.10.2 From e2dce944cc2bf22d0295330cbdcbd2ad7bd47cb4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:47:52 +0100 Subject: ASoC: rt5631: Cleanup bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 9425545..6d7b7ca 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1612,29 +1612,6 @@ static int rt5631_probe(struct snd_soc_codec *codec) return 0; } -static int rt5631_remove(struct snd_soc_codec *codec) -{ - rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -#ifdef CONFIG_PM -static int rt5631_suspend(struct snd_soc_codec *codec) -{ - rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int rt5631_resume(struct snd_soc_codec *codec) -{ - rt5631_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define rt5631_suspend NULL -#define rt5631_resume NULL -#endif - #define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000 #define RT5631_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ @@ -1672,10 +1649,8 @@ static struct snd_soc_dai_driver rt5631_dai[] = { static struct snd_soc_codec_driver soc_codec_dev_rt5631 = { .probe = rt5631_probe, - .remove = rt5631_remove, - .suspend = rt5631_suspend, - .resume = rt5631_resume, .set_bias_level = rt5631_set_bias_level, + .suspend_bias_off = true, .controls = rt5631_snd_controls, .num_controls = ARRAY_SIZE(rt5631_snd_controls), .dapm_widgets = rt5631_dapm_widgets, -- cgit v0.10.2 From 21a942fdd85efde65512f1458bcb952fda88886e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:17 +0100 Subject: ASoC: wm8350: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 628ec77..87f664b 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1242,19 +1242,6 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int wm8350_suspend(struct snd_soc_codec *codec) -{ - wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8350_resume(struct snd_soc_codec *codec) -{ - wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - static void wm8350_hp_work(struct wm8350_data *priv, struct wm8350_jack_data *jack, u16 mask) @@ -1565,9 +1552,6 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD, wm8350_mic_handler, 0, "Microphone detect", priv); - - wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; } @@ -1596,8 +1580,6 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) * wait for its completion */ flush_delayed_work(&codec->dapm.delayed_work); - wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); - wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); return 0; @@ -1613,10 +1595,9 @@ static struct regmap *wm8350_get_regmap(struct device *dev) static struct snd_soc_codec_driver soc_codec_dev_wm8350 = { .probe = wm8350_codec_probe, .remove = wm8350_codec_remove, - .suspend = wm8350_suspend, - .resume = wm8350_resume, .get_regmap = wm8350_get_regmap, .set_bias_level = wm8350_set_bias_level, + .suspend_bias_off = true, .controls = wm8350_snd_controls, .num_controls = ARRAY_SIZE(wm8350_snd_controls), -- cgit v0.10.2 From 098f6f17c3f1beeccdce78f9722ccaa7925b8041 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:18 +0100 Subject: ASoC: wm8400: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual asynchronous transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Also running this asynchronously has the problem of potential race conditions with the core. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 72471be..385894f 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -58,12 +58,10 @@ static struct regulator_bulk_data power[] = { /* codec private data */ struct wm8400_priv { - struct snd_soc_codec *codec; struct wm8400 *wm8400; u16 fake_register; unsigned int sysclk; unsigned int pcmclk; - struct work_struct work; int fll_in, fll_out; }; @@ -1278,30 +1276,6 @@ static struct snd_soc_dai_driver wm8400_dai = { .ops = &wm8400_dai_ops, }; -static int wm8400_suspend(struct snd_soc_codec *codec) -{ - wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int wm8400_resume(struct snd_soc_codec *codec) -{ - wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static void wm8400_probe_deferred(struct work_struct *work) -{ - struct wm8400_priv *priv = container_of(work, struct wm8400_priv, - work); - struct snd_soc_codec *codec = priv->codec; - - /* charge output caps */ - wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -} - static int wm8400_codec_probe(struct snd_soc_codec *codec) { struct wm8400 *wm8400 = dev_get_platdata(codec->dev); @@ -1316,7 +1290,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, priv); priv->wm8400 = wm8400; - priv->codec = codec; ret = devm_regulator_bulk_get(wm8400->dev, ARRAY_SIZE(power), &power[0]); @@ -1325,8 +1298,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec) return ret; } - INIT_WORK(&priv->work, wm8400_probe_deferred); - wm8400_codec_reset(codec); reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); @@ -1343,8 +1314,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec) snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); - if (!schedule_work(&priv->work)) - return -EINVAL; return 0; } @@ -1369,10 +1338,9 @@ static struct regmap *wm8400_get_regmap(struct device *dev) static struct snd_soc_codec_driver soc_codec_dev_wm8400 = { .probe = wm8400_codec_probe, .remove = wm8400_codec_remove, - .suspend = wm8400_suspend, - .resume = wm8400_resume, .get_regmap = wm8400_get_regmap, .set_bias_level = wm8400_set_bias_level, + .suspend_bias_off = true, .controls = wm8400_snd_controls, .num_controls = ARRAY_SIZE(wm8400_snd_controls), -- cgit v0.10.2 From 99b108c73f3876d71ac6631e85e0f093e53b7e66 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:19 +0100 Subject: ASoC: wm8510: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index e11127f..8736ad0 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -575,41 +575,17 @@ static struct snd_soc_dai_driver wm8510_dai = { .symmetric_rates = 1, }; -static int wm8510_suspend(struct snd_soc_codec *codec) -{ - wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8510_resume(struct snd_soc_codec *codec) -{ - wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int wm8510_probe(struct snd_soc_codec *codec) { wm8510_reset(codec); - /* power on device */ - wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -/* power down chip */ -static int wm8510_remove(struct snd_soc_codec *codec) -{ - wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } static struct snd_soc_codec_driver soc_codec_dev_wm8510 = { .probe = wm8510_probe, - .remove = wm8510_remove, - .suspend = wm8510_suspend, - .resume = wm8510_resume, .set_bias_level = wm8510_set_bias_level, + .suspend_bias_off = true, .controls = wm8510_snd_controls, .num_controls = ARRAY_SIZE(wm8510_snd_controls), -- cgit v0.10.2 From ca5e7c6afff94b4e103d79db835bc2990d3d340e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:20 +0100 Subject: ASoC: wm8523: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index ec1f574..b1cc94f 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -372,23 +372,6 @@ static struct snd_soc_dai_driver wm8523_dai = { .ops = &wm8523_dai_ops, }; -#ifdef CONFIG_PM -static int wm8523_suspend(struct snd_soc_codec *codec) -{ - wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8523_resume(struct snd_soc_codec *codec) -{ - wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define wm8523_suspend NULL -#define wm8523_resume NULL -#endif - static int wm8523_probe(struct snd_soc_codec *codec) { struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec); @@ -402,23 +385,13 @@ static int wm8523_probe(struct snd_soc_codec *codec) WM8523_DACR_VU, WM8523_DACR_VU); snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC); - wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int wm8523_remove(struct snd_soc_codec *codec) -{ - wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } static struct snd_soc_codec_driver soc_codec_dev_wm8523 = { .probe = wm8523_probe, - .remove = wm8523_remove, - .suspend = wm8523_suspend, - .resume = wm8523_resume, .set_bias_level = wm8523_set_bias_level, + .suspend_bias_off = true, .controls = wm8523_controls, .num_controls = ARRAY_SIZE(wm8523_controls), -- cgit v0.10.2 From 4d0a4c3c6dd2359c3d5facac7a306d513d79bff2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:21 +0100 Subject: ASoC: wm8580: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 911605e..0a887c5 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -882,8 +882,6 @@ static int wm8580_probe(struct snd_soc_codec *codec) goto err_regulator_enable; } - wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; err_regulator_enable: @@ -897,8 +895,6 @@ static int wm8580_remove(struct snd_soc_codec *codec) { struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); - wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF); - regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); return 0; -- cgit v0.10.2 From 0bd324b1ad5c0922ac3f157763123d1550bdffd7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:22 +0100 Subject: ASoC: wm8711: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Also remove the extra write that sets the WM8711_ACTIVE register to 0x00 in the suspend handler since this write is already done when transitioning to SND_SOC_BIAS_OFF. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 32187e7..121e46d 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -350,19 +350,6 @@ static struct snd_soc_dai_driver wm8711_dai = { .ops = &wm8711_ops, }; -static int wm8711_suspend(struct snd_soc_codec *codec) -{ - snd_soc_write(codec, WM8711_ACTIVE, 0x0); - wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8711_resume(struct snd_soc_codec *codec) -{ - wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int wm8711_probe(struct snd_soc_codec *codec) { int ret; @@ -373,8 +360,6 @@ static int wm8711_probe(struct snd_soc_codec *codec) return ret; } - wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Latch the update bits */ snd_soc_update_bits(codec, WM8711_LOUT1V, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8711_ROUT1V, 0x0100, 0x0100); @@ -383,19 +368,11 @@ static int wm8711_probe(struct snd_soc_codec *codec) } -/* power down chip */ -static int wm8711_remove(struct snd_soc_codec *codec) -{ - wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8711 = { .probe = wm8711_probe, - .remove = wm8711_remove, - .suspend = wm8711_suspend, - .resume = wm8711_resume, .set_bias_level = wm8711_set_bias_level, + .suspend_bias_off = true, + .controls = wm8711_snd_controls, .num_controls = ARRAY_SIZE(wm8711_snd_controls), .dapm_widgets = wm8711_dapm_widgets, -- cgit v0.10.2 From d4d41436ff3b1fddf2f8feafa6772647eac6b61d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:23 +0100 Subject: ASoC: wm8728: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 38ff826..55c7fb4 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -212,40 +212,10 @@ static struct snd_soc_dai_driver wm8728_dai = { .ops = &wm8728_dai_ops, }; -static int wm8728_suspend(struct snd_soc_codec *codec) -{ - wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int wm8728_resume(struct snd_soc_codec *codec) -{ - wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int wm8728_probe(struct snd_soc_codec *codec) -{ - /* power on device */ - wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int wm8728_remove(struct snd_soc_codec *codec) -{ - wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8728 = { - .probe = wm8728_probe, - .remove = wm8728_remove, - .suspend = wm8728_suspend, - .resume = wm8728_resume, .set_bias_level = wm8728_set_bias_level, + .suspend_bias_off = true, + .controls = wm8728_snd_controls, .num_controls = ARRAY_SIZE(wm8728_snd_controls), .dapm_widgets = wm8728_dapm_widgets, -- cgit v0.10.2 From 2081b2cf05d022ac6245334e8baa25f589e5635a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:24 +0100 Subject: ASoC: wm8731: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 2c9f2a7..3b3786e 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -559,25 +559,6 @@ static struct snd_soc_dai_driver wm8731_dai = { .symmetric_rates = 1, }; -#ifdef CONFIG_PM -static int wm8731_suspend(struct snd_soc_codec *codec) -{ - wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int wm8731_resume(struct snd_soc_codec *codec) -{ - wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define wm8731_suspend NULL -#define wm8731_resume NULL -#endif - static int wm8731_probe(struct snd_soc_codec *codec) { struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); @@ -633,8 +614,6 @@ static int wm8731_remove(struct snd_soc_codec *codec) { struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); - regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); return 0; @@ -643,9 +622,9 @@ static int wm8731_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { .probe = wm8731_probe, .remove = wm8731_remove, - .suspend = wm8731_suspend, - .resume = wm8731_resume, .set_bias_level = wm8731_set_bias_level, + .suspend_bias_off = true, + .dapm_widgets = wm8731_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), .dapm_routes = wm8731_intercon, -- cgit v0.10.2 From 67cac3a351b9d411f8736a180767f0e898b50423 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:25 +0100 Subject: ASoC: wm8737: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index fe41dd2..ada9ac1 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -537,23 +537,6 @@ static struct snd_soc_dai_driver wm8737_dai = { .ops = &wm8737_dai_ops, }; -#ifdef CONFIG_PM -static int wm8737_suspend(struct snd_soc_codec *codec) -{ - wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8737_resume(struct snd_soc_codec *codec) -{ - wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define wm8737_suspend NULL -#define wm8737_resume NULL -#endif - static int wm8737_probe(struct snd_soc_codec *codec) { struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); @@ -590,18 +573,10 @@ err_get: return ret; } -static int wm8737_remove(struct snd_soc_codec *codec) -{ - wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8737 = { .probe = wm8737_probe, - .remove = wm8737_remove, - .suspend = wm8737_suspend, - .resume = wm8737_resume, .set_bias_level = wm8737_set_bias_level, + .suspend_bias_off = true, .controls = wm8737_snd_controls, .num_controls = ARRAY_SIZE(wm8737_snd_controls), -- cgit v0.10.2 From d12cbf956f428229bb29fb58dee8729e16873ca7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:26 +0100 Subject: ASoC: wm8750: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 67653a2..f6847fd 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -686,18 +686,6 @@ static struct snd_soc_dai_driver wm8750_dai = { .ops = &wm8750_dai_ops, }; -static int wm8750_suspend(struct snd_soc_codec *codec) -{ - wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8750_resume(struct snd_soc_codec *codec) -{ - wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int wm8750_probe(struct snd_soc_codec *codec) { int ret; @@ -708,9 +696,6 @@ static int wm8750_probe(struct snd_soc_codec *codec) return ret; } - /* charge output caps */ - wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* set the update bits */ snd_soc_update_bits(codec, WM8750_LDAC, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8750_RDAC, 0x0100, 0x0100); @@ -724,18 +709,10 @@ static int wm8750_probe(struct snd_soc_codec *codec) return ret; } -static int wm8750_remove(struct snd_soc_codec *codec) -{ - wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8750 = { .probe = wm8750_probe, - .remove = wm8750_remove, - .suspend = wm8750_suspend, - .resume = wm8750_resume, .set_bias_level = wm8750_set_bias_level, + .suspend_bias_off = true, .controls = wm8750_snd_controls, .num_controls = ARRAY_SIZE(wm8750_snd_controls), -- cgit v0.10.2 From 6c286afb01cc641e2a78e485467e4a90aedfbd75 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:27 +0100 Subject: ASoC: wm8776: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 70952ce..c13050b 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -408,24 +408,6 @@ static struct snd_soc_dai_driver wm8776_dai[] = { }, }; -#ifdef CONFIG_PM -static int wm8776_suspend(struct snd_soc_codec *codec) -{ - wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int wm8776_resume(struct snd_soc_codec *codec) -{ - wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define wm8776_suspend NULL -#define wm8776_resume NULL -#endif - static int wm8776_probe(struct snd_soc_codec *codec) { int ret = 0; @@ -436,8 +418,6 @@ static int wm8776_probe(struct snd_soc_codec *codec) return ret; } - wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Latch the update bits; right channel only since we always * update both. */ snd_soc_update_bits(codec, WM8776_HPRVOL, 0x100, 0x100); @@ -446,19 +426,10 @@ static int wm8776_probe(struct snd_soc_codec *codec) return ret; } -/* power down chip */ -static int wm8776_remove(struct snd_soc_codec *codec) -{ - wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8776 = { .probe = wm8776_probe, - .remove = wm8776_remove, - .suspend = wm8776_suspend, - .resume = wm8776_resume, .set_bias_level = wm8776_set_bias_level, + .suspend_bias_off = true, .controls = wm8776_snd_controls, .num_controls = ARRAY_SIZE(wm8776_snd_controls), -- cgit v0.10.2 From a4235a14bef979752fb2ddb4dafdb696f622beb0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:28 +0100 Subject: ASoC: wm8804: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 3addc5f..1315f76 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -524,7 +524,6 @@ static int wm8804_remove(struct snd_soc_codec *codec) int i; wm8804 = snd_soc_codec_get_drvdata(codec); - wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF); for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) regulator_unregister_notifier(wm8804->supplies[i].consumer, @@ -606,8 +605,6 @@ static int wm8804_probe(struct snd_soc_codec *codec) goto err_reg_enable; } - wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; err_reg_enable: -- cgit v0.10.2 From d2a9bc68512696a896abf7a94852c5fb5e6733a1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:29 +0100 Subject: ASoC: wm8900: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 44a5f15..3a0d4b7 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1209,16 +1209,8 @@ static int wm8900_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int wm8900_remove(struct snd_soc_codec *codec) -{ - wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8900 = { .probe = wm8900_probe, - .remove = wm8900_remove, .suspend = wm8900_suspend, .resume = wm8900_resume, .set_bias_level = wm8900_set_bias_level, -- cgit v0.10.2 From b0d55b1a63ea3c3d694c58694d93f74bea61215f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:30 +0100 Subject: ASoC: wm8903: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Also remove the unused codec field from the wm8903_priv struct so we can remove the whole probe callback. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index c038b3e..9758d2e 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -117,7 +117,6 @@ static const struct reg_default wm8903_reg_defaults[] = { struct wm8903_priv { struct wm8903_platform_data *pdata; struct device *dev; - struct snd_soc_codec *codec; struct regmap *regmap; int sysclk; @@ -1757,21 +1756,12 @@ static struct snd_soc_dai_driver wm8903_dai = { .symmetric_rates = 1, }; -static int wm8903_suspend(struct snd_soc_codec *codec) -{ - wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static int wm8903_resume(struct snd_soc_codec *codec) { struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); regcache_sync(wm8903->regmap); - wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; } @@ -1889,33 +1879,12 @@ static void wm8903_free_gpio(struct wm8903_priv *wm8903) } #endif -static int wm8903_probe(struct snd_soc_codec *codec) -{ - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - wm8903->codec = codec; - - /* power on device */ - wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -/* power down chip */ -static int wm8903_remove(struct snd_soc_codec *codec) -{ - wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8903 = { - .probe = wm8903_probe, - .remove = wm8903_remove, - .suspend = wm8903_suspend, .resume = wm8903_resume, .set_bias_level = wm8903_set_bias_level, .seq_notifier = wm8903_seq_notifier, + .suspend_bias_off = true, + .controls = wm8903_snd_controls, .num_controls = ARRAY_SIZE(wm8903_snd_controls), .dapm_widgets = wm8903_dapm_widgets, -- cgit v0.10.2 From 5fdf082b43995ae31d746d3d9e3b616afa24c542 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:31 +0100 Subject: ASoC: wm8940: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 5201104..e4142b4 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -695,17 +695,6 @@ static struct snd_soc_dai_driver wm8940_dai = { .symmetric_rates = 1, }; -static int wm8940_suspend(struct snd_soc_codec *codec) -{ - return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF); -} - -static int wm8940_resume(struct snd_soc_codec *codec) -{ - wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int wm8940_probe(struct snd_soc_codec *codec) { struct wm8940_setup_data *pdata = codec->dev->platform_data; @@ -736,18 +725,11 @@ static int wm8940_probe(struct snd_soc_codec *codec) return ret; } -static int wm8940_remove(struct snd_soc_codec *codec) -{ - wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8940 = { .probe = wm8940_probe, - .remove = wm8940_remove, - .suspend = wm8940_suspend, - .resume = wm8940_resume, .set_bias_level = wm8940_set_bias_level, + .suspend_bias_off = true, + .controls = wm8940_snd_controls, .num_controls = ARRAY_SIZE(wm8940_snd_controls), .dapm_widgets = wm8940_dapm_widgets, -- cgit v0.10.2 From bf68a0470876b5bf43758c50a7585eb5f6e177ea Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:32 +0100 Subject: ASoC: wm8955: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Also remove the regcache_mark_dirty() from the suspend handler since this is already done by the ASoC core. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 09d91d9..1173f7f 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -866,29 +866,6 @@ static struct snd_soc_dai_driver wm8955_dai = { .ops = &wm8955_dai_ops, }; -#ifdef CONFIG_PM -static int wm8955_suspend(struct snd_soc_codec *codec) -{ - struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - - wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF); - - regcache_mark_dirty(wm8955->regmap); - - return 0; -} - -static int wm8955_resume(struct snd_soc_codec *codec) -{ - wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define wm8955_suspend NULL -#define wm8955_resume NULL -#endif - static int wm8955_probe(struct snd_soc_codec *codec) { struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); @@ -964,18 +941,10 @@ err_enable: return ret; } -static int wm8955_remove(struct snd_soc_codec *codec) -{ - wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8955 = { .probe = wm8955_probe, - .remove = wm8955_remove, - .suspend = wm8955_suspend, - .resume = wm8955_resume, .set_bias_level = wm8955_set_bias_level, + .suspend_bias_off = true, .controls = wm8955_snd_controls, .num_controls = ARRAY_SIZE(wm8955_snd_controls), -- cgit v0.10.2 From 0a87a6e1c09c3b93d91bf65809e79cf6cf358785 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:33 +0100 Subject: ASoC: wm8960: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 99d6457..bc8793cd 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -943,22 +943,6 @@ static struct snd_soc_dai_driver wm8960_dai = { .symmetric_rates = 1, }; -static int wm8960_suspend(struct snd_soc_codec *codec) -{ - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - - wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8960_resume(struct snd_soc_codec *codec) -{ - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - - wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int wm8960_probe(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); @@ -976,8 +960,6 @@ static int wm8960_probe(struct snd_soc_codec *codec) return ret; } - wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Latch the update bits */ snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100); snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100); @@ -997,21 +979,10 @@ static int wm8960_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int wm8960_remove(struct snd_soc_codec *codec) -{ - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - - wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8960 = { .probe = wm8960_probe, - .remove = wm8960_remove, - .suspend = wm8960_suspend, - .resume = wm8960_resume, .set_bias_level = wm8960_set_bias_level, + .suspend_bias_off = true, }; static const struct regmap_config wm8960_regmap = { -- cgit v0.10.2 From 7bea32c5b2493044d31a2116328c71c7048de0e3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:34 +0100 Subject: ASoC: wm8961: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index e077bb2..eeffd05 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -870,44 +870,26 @@ static int wm8961_probe(struct snd_soc_codec *codec) reg &= ~WM8961_MANUAL_MODE; snd_soc_write(codec, WM8961_CLOCKING_3, reg); - wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int wm8961_remove(struct snd_soc_codec *codec) -{ - wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } #ifdef CONFIG_PM -static int wm8961_suspend(struct snd_soc_codec *codec) -{ - wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} static int wm8961_resume(struct snd_soc_codec *codec) { snd_soc_cache_sync(codec); - wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; } #else -#define wm8961_suspend NULL #define wm8961_resume NULL #endif static struct snd_soc_codec_driver soc_codec_dev_wm8961 = { .probe = wm8961_probe, - .remove = wm8961_remove, - .suspend = wm8961_suspend, .resume = wm8961_resume, .set_bias_level = wm8961_set_bias_level, + .suspend_bias_off = true, .controls = wm8961_snd_controls, .num_controls = ARRAY_SIZE(wm8961_snd_controls), -- cgit v0.10.2 From 387fe80fb13ed9f5f3741a661f96e409a2c959b5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:37 +0100 Subject: ASoC: wm8983: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index ac5defd..5d1cf08 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -967,29 +967,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec, return 0; } -#ifdef CONFIG_PM -static int wm8983_suspend(struct snd_soc_codec *codec) -{ - wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8983_resume(struct snd_soc_codec *codec) -{ - wm8983_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define wm8983_suspend NULL -#define wm8983_resume NULL -#endif - -static int wm8983_remove(struct snd_soc_codec *codec) -{ - wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int wm8983_probe(struct snd_soc_codec *codec) { int ret; @@ -1055,10 +1032,8 @@ static struct snd_soc_dai_driver wm8983_dai = { static struct snd_soc_codec_driver soc_codec_dev_wm8983 = { .probe = wm8983_probe, - .remove = wm8983_remove, - .suspend = wm8983_suspend, - .resume = wm8983_resume, .set_bias_level = wm8983_set_bias_level, + .suspend_bias_off = true, .controls = wm8983_snd_controls, .num_controls = ARRAY_SIZE(wm8983_snd_controls), .dapm_widgets = wm8983_dapm_widgets, -- cgit v0.10.2 From d02486fd42a3295edbec4db8f7f81c1432fa60a4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:36 +0100 Subject: ASoC: wm8978: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index ee2ba57..cf70329 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -991,21 +991,11 @@ static int wm8978_probe(struct snd_soc_codec *codec) for (i = 0; i < ARRAY_SIZE(update_reg); i++) snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100); - wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -/* power down chip */ -static int wm8978_remove(struct snd_soc_codec *codec) -{ - wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } static struct snd_soc_codec_driver soc_codec_dev_wm8978 = { .probe = wm8978_probe, - .remove = wm8978_remove, .suspend = wm8978_suspend, .resume = wm8978_resume, .set_bias_level = wm8978_set_bias_level, -- cgit v0.10.2 From ed1358f508e1ebcb01e1e545c5330599098b7687 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:35 +0100 Subject: ASoC: wm8974: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 682e9ed..ff0e464 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -568,18 +568,6 @@ static struct snd_soc_dai_driver wm8974_dai = { .symmetric_rates = 1, }; -static int wm8974_suspend(struct snd_soc_codec *codec) -{ - wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8974_resume(struct snd_soc_codec *codec) -{ - wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static const struct regmap_config wm8974_regmap = { .reg_bits = 7, .val_bits = 9, @@ -599,24 +587,13 @@ static int wm8974_probe(struct snd_soc_codec *codec) return ret; } - wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return ret; -} - -/* power down chip */ -static int wm8974_remove(struct snd_soc_codec *codec) -{ - wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { .probe = wm8974_probe, - .remove = wm8974_remove, - .suspend = wm8974_suspend, - .resume = wm8974_resume, .set_bias_level = wm8974_set_bias_level, + .suspend_bias_off = true, .controls = wm8974_snd_controls, .num_controls = ARRAY_SIZE(wm8974_snd_controls), -- cgit v0.10.2 From a5dde8c42ef6b3cb47c69905ee51520e18ac6515 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:38 +0100 Subject: ASoC: wm8985: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index ee38019..0b3b54c 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -961,29 +961,6 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec, return 0; } -#ifdef CONFIG_PM -static int wm8985_suspend(struct snd_soc_codec *codec) -{ - wm8985_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8985_resume(struct snd_soc_codec *codec) -{ - wm8985_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define wm8985_suspend NULL -#define wm8985_resume NULL -#endif - -static int wm8985_remove(struct snd_soc_codec *codec) -{ - wm8985_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int wm8985_probe(struct snd_soc_codec *codec) { size_t i; @@ -1023,7 +1000,6 @@ static int wm8985_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT, WM8985_BIASCUT); - wm8985_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; err_reg_enable: @@ -1064,10 +1040,8 @@ static struct snd_soc_dai_driver wm8985_dai = { static struct snd_soc_codec_driver soc_codec_dev_wm8985 = { .probe = wm8985_probe, - .remove = wm8985_remove, - .suspend = wm8985_suspend, - .resume = wm8985_resume, .set_bias_level = wm8985_set_bias_level, + .suspend_bias_off = true, .controls = wm8985_snd_controls, .num_controls = ARRAY_SIZE(wm8985_snd_controls), -- cgit v0.10.2 From 1f07b8de451f5f4f6a268a95a34183e528cda711 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:39 +0100 Subject: ASoC: wm8988: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Also remove the regcache_mark_dirty() from the suspend handler since it is already called by the ASoC core. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index a5130d9..e418199 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -793,21 +793,6 @@ static struct snd_soc_dai_driver wm8988_dai = { .symmetric_rates = 1, }; -static int wm8988_suspend(struct snd_soc_codec *codec) -{ - struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); - - wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); - regcache_mark_dirty(wm8988->regmap); - return 0; -} - -static int wm8988_resume(struct snd_soc_codec *codec) -{ - wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int wm8988_probe(struct snd_soc_codec *codec) { int ret = 0; @@ -825,23 +810,13 @@ static int wm8988_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8988_ROUT2V, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8988_RINVOL, 0x0100, 0x0100); - wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int wm8988_remove(struct snd_soc_codec *codec) -{ - wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } static struct snd_soc_codec_driver soc_codec_dev_wm8988 = { .probe = wm8988_probe, - .remove = wm8988_remove, - .suspend = wm8988_suspend, - .resume = wm8988_resume, .set_bias_level = wm8988_set_bias_level, + .suspend_bias_off = true, .controls = wm8988_snd_controls, .num_controls = ARRAY_SIZE(wm8988_snd_controls), -- cgit v0.10.2 From 955efc8f50eb11d1c85daca6db7943c63dc5c2e7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:40 +0100 Subject: ASoC: wm8990: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 03e43e3..8a58422 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1271,18 +1271,6 @@ static struct snd_soc_dai_driver wm8990_dai = { .ops = &wm8990_dai_ops, }; -static int wm8990_suspend(struct snd_soc_codec *codec) -{ - wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8990_resume(struct snd_soc_codec *codec) -{ - wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - /* * initialise the WM8990 driver * register the mixer and dsp interfaces with the kernel @@ -1309,19 +1297,11 @@ static int wm8990_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int wm8990_remove(struct snd_soc_codec *codec) -{ - wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8990 = { .probe = wm8990_probe, - .remove = wm8990_remove, - .suspend = wm8990_suspend, - .resume = wm8990_resume, .set_bias_level = wm8990_set_bias_level, + .suspend_bias_off = true, + .controls = wm8990_snd_controls, .num_controls = ARRAY_SIZE(wm8990_snd_controls), .dapm_widgets = wm8990_dapm_widgets, -- cgit v0.10.2 From 497b900f83c56d513794ccf56b7a87c50a34a454 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:41 +0100 Subject: ASoC: wm8991: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index d0be897..b0ac2c3 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1227,32 +1227,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int wm8991_suspend(struct snd_soc_codec *codec) -{ - wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8991_resume(struct snd_soc_codec *codec) -{ - wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - -/* power down chip */ -static int wm8991_remove(struct snd_soc_codec *codec) -{ - wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8991_probe(struct snd_soc_codec *codec) -{ - wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - #define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) @@ -1293,11 +1267,9 @@ static struct snd_soc_dai_driver wm8991_dai = { }; static struct snd_soc_codec_driver soc_codec_dev_wm8991 = { - .probe = wm8991_probe, - .remove = wm8991_remove, - .suspend = wm8991_suspend, - .resume = wm8991_resume, .set_bias_level = wm8991_set_bias_level, + .suspend_bias_off = true, + .controls = wm8991_snd_controls, .num_controls = ARRAY_SIZE(wm8991_snd_controls), .dapm_widgets = wm8991_dapm_widgets, -- cgit v0.10.2 From 77d05e7f81da95eb2b6c7ae24ae0fb3272c49282 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:42 +0100 Subject: ASoC: wm8993: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 93b14ed..53c6fe3 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1486,7 +1486,6 @@ static int wm8993_probe(struct snd_soc_codec *codec) { struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; wm8993->hubs_data.hp_startup_mode = 1; wm8993->hubs_data.dcs_codes_l = -2; @@ -1518,10 +1517,6 @@ static int wm8993_probe(struct snd_soc_codec *codec) wm8993->pdata.micbias1_lvl, wm8993->pdata.micbias2_lvl); - ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (ret != 0) - return ret; - snd_soc_add_codec_controls(codec, wm8993_snd_controls, ARRAY_SIZE(wm8993_snd_controls)); if (wm8993->pdata.num_retune_configs != 0) { @@ -1550,12 +1545,6 @@ static int wm8993_probe(struct snd_soc_codec *codec) } -static int wm8993_remove(struct snd_soc_codec *codec) -{ - wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - #ifdef CONFIG_PM static int wm8993_suspend(struct snd_soc_codec *codec) { @@ -1629,7 +1618,6 @@ static const struct regmap_config wm8993_regmap = { static struct snd_soc_codec_driver soc_codec_dev_wm8993 = { .probe = wm8993_probe, - .remove = wm8993_remove, .suspend = wm8993_suspend, .resume = wm8993_resume, .set_bias_level = wm8993_set_bias_level, -- cgit v0.10.2 From 49d9ac383cddc3e8d4cae8bc7a8f4da9dc071121 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:43 +0100 Subject: ASoC: wm8994: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1fcb9f3..c3a2e75 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4391,8 +4391,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) struct wm8994 *control = wm8994->wm8994; int i; - wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); - for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, &wm8994->fll_locked[i]); -- cgit v0.10.2 From aee9ffabec81d96d68d8537ccc6fedfbb0e6c468 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:44 +0100 Subject: ASoC: wm8995: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index e40c8a6..c280f0a 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2004,7 +2004,6 @@ static int wm8995_remove(struct snd_soc_codec *codec) int i; wm8995 = snd_soc_codec_get_drvdata(codec); - wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF); for (i = 0; i < ARRAY_SIZE(wm8995->supplies); ++i) regulator_unregister_notifier(wm8995->supplies[i].consumer, @@ -2078,8 +2077,6 @@ static int wm8995_probe(struct snd_soc_codec *codec) goto err_reg_enable; } - wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Latch volume updates (right only; we always do left then right). */ snd_soc_update_bits(codec, WM8995_AIF1_DAC1_RIGHT_VOLUME, WM8995_AIF1DAC1_VU_MASK, WM8995_AIF1DAC1_VU); -- cgit v0.10.2 From 68201d6998a0d7701f7c3009130c5d8cd6ad7562 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:45 +0100 Subject: ASoC: wm9081: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 0cdc9e2..b1d946f 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1277,15 +1277,8 @@ static int wm9081_probe(struct snd_soc_codec *codec) return 0; } -static int wm9081_remove(struct snd_soc_codec *codec) -{ - wm9081_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm9081 = { .probe = wm9081_probe, - .remove = wm9081_remove, .set_sysclk = wm9081_set_sysclk, .set_bias_level = wm9081_set_bias_level, -- cgit v0.10.2 From a70cf928ca396447416422c2ec1697a530534ac9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:46 +0100 Subject: ASoC: wm9090: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index a13f072..6ffe8dc 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -550,45 +550,15 @@ static int wm9090_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM9090_CLOCKING_1, WM9090_TOCLK_ENA, WM9090_TOCLK_ENA); - wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - wm9090_add_controls(codec); return 0; } -#ifdef CONFIG_PM -static int wm9090_suspend(struct snd_soc_codec *codec) -{ - wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int wm9090_resume(struct snd_soc_codec *codec) -{ - wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define wm9090_suspend NULL -#define wm9090_resume NULL -#endif - -static int wm9090_remove(struct snd_soc_codec *codec) -{ - wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm9090 = { .probe = wm9090_probe, - .remove = wm9090_remove, - .suspend = wm9090_suspend, - .resume = wm9090_resume, .set_bias_level = wm9090_set_bias_level, + .suspend_bias_off = true, }; static const struct regmap_config wm9090_regmap = { -- cgit v0.10.2 From ab492b86b89be6c98bdca71cfc97b411ca42e140 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:47 +0100 Subject: ASoC: wm9712: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend. This makes the code a bit shorter and cleaner. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index f3aab6e..30f4b17 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -599,12 +599,6 @@ err: return -EIO; } -static int wm9712_soc_suspend(struct snd_soc_codec *codec) -{ - wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int wm9712_soc_resume(struct snd_soc_codec *codec) { int i, ret; @@ -646,8 +640,6 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); - wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; reset_err: @@ -664,11 +656,11 @@ static int wm9712_soc_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { .probe = wm9712_soc_probe, .remove = wm9712_soc_remove, - .suspend = wm9712_soc_suspend, .resume = wm9712_soc_resume, .read = ac97_read, .write = ac97_write, .set_bias_level = wm9712_set_bias_level, + .suspend_bias_off = true, .reg_cache_size = ARRAY_SIZE(wm9712_reg), .reg_word_size = sizeof(u16), .reg_cache_step = 2, -- cgit v0.10.2 From 0cb6b1419ec864996835991a62788c588693f27d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:48 +0100 Subject: ASoC: wm9713: Cleanup manual bias level transitions The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index ac13fc8..e977b13 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1203,8 +1203,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) if (ret < 0) goto reset_err; - wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* unmute the adc - move to kcontrol */ reg = ac97_read(codec, AC97_CD) & 0x7fff; ac97_write(codec, AC97_CD, reg); -- cgit v0.10.2 From bbc686b34650b0f54affe9d9a637ccbe02b03760 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 24 Nov 2014 20:37:12 +0200 Subject: ASoC: tlv320aic31xx: Fix off by one error in the loop stucture. Fix off by one read beyond the end of a table. Reported-by: David Binderman Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 145fe5b..93de5dd 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -911,12 +911,13 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, } aic31xx->p_div = i; - for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) { - if (i == ARRAY_SIZE(aic31xx_divs)) { - dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n", - __func__, freq); - return -EINVAL; - } + for (i = 0; i < ARRAY_SIZE(aic31xx_divs) && + aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) + ; + if (i == ARRAY_SIZE(aic31xx_divs)) { + dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n", + __func__, freq); + return -EINVAL; } /* set clock on MCLK, BCLK, or GPIO1 as PLL input */ -- cgit v0.10.2 From 8d213de7ffff614e3aa751a31a8980e1be3036e1 Mon Sep 17 00:00:00 2001 From: Andreas Ruprecht Date: Fri, 21 Nov 2014 20:50:46 +0100 Subject: ASoC: rockchip: i2s: Fix Kconfig for I2S device driver Currently, CONFIG_SND_SOC_ROCKCHIP_I2S could also be selected without having CONFIG_SND_SOC_ROCKCHIP enabled. As this makes no sense, a Kconfig dependency is added to CONFIG_SND_SOC_ROCKCHIP_I2S. This will make the item visible only if CONFIG_SND_SOC_ROCKCHIP is enabled. Additionally, as the code connected to CONFIG_SND_SOC_ROCKCHIP_I2S depends on CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM, the dependency is moved to reflect this more clearly. Signed-off-by: Andreas Ruprecht Reported-by: Jim Davis Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index b1fc0ca..e181826 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -1,7 +1,6 @@ config SND_SOC_ROCKCHIP tristate "ASoC support for Rockchip" depends on COMPILE_TEST || ARCH_ROCKCHIP - select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the Rockchip SoCs' Audio interfaces. You will also need to @@ -9,7 +8,8 @@ config SND_SOC_ROCKCHIP config SND_SOC_ROCKCHIP_I2S tristate "Rockchip I2S Device Driver" - depends on CLKDEV_LOOKUP + depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for Rockchip I2S device. The device supports upto maximum of -- cgit v0.10.2 From 141f87d4d6ab36bfcd4c5683cf90abf83b306d90 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 25 Nov 2014 04:49:44 +0800 Subject: ASoC: sigmadsp: fix simple_return.cocci warnings sound/soc/codecs/sigmadsp.c:656:1-4: WARNING: end returns can be simpified and declaration on line 636 can be dropped Simplify a trivial if-return sequence. Possibly combine with a preceding function call. Generated by: scripts/coccinelle/misc/simple_return.cocci Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 6abefd2..34fdc40 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -633,7 +633,6 @@ static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp, { struct snd_kcontrol_new template; struct snd_kcontrol *kcontrol; - int ret; memset(&template, 0, sizeof(template)); template.iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -653,11 +652,7 @@ static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp, kcontrol->private_free = sigmadsp_control_free; ctrl->kcontrol = kcontrol; - ret = snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol); - if (ret) - return ret; - - return 0; + return snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol); } static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, -- cgit v0.10.2 From 2d4e2d020516632288e8c8d1f8be2f3042d6b8de Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 18 Nov 2014 16:50:18 +0800 Subject: ASoC: rt5645: multiple JD mode support There are 3 JD modes in RT5645. This patch configure register values according to platform data. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h index 937f421..120d961 100644 --- a/include/sound/rt5645.h +++ b/include/sound/rt5645.h @@ -26,6 +26,7 @@ struct rt5645_platform_data { /* true if codec's jd function is used */ bool en_jd_func; + unsigned int jd_mode; }; #endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index ef88b50..6e9cd8e 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2239,7 +2239,8 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec) snd_soc_dapm_disable_pin(&codec->dapm, "micbias1"); snd_soc_dapm_disable_pin(&codec->dapm, "micbias2"); - snd_soc_dapm_disable_pin(&codec->dapm, "LDO2"); + if (rt5645->pdata.jd_mode == 0) + snd_soc_dapm_disable_pin(&codec->dapm, "LDO2"); snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); snd_soc_dapm_sync(&codec->dapm); } @@ -2543,6 +2544,38 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT); } + if (rt5645->pdata.jd_mode) { + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN); + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, + RT5645_JD_PSV_MODE, RT5645_JD_PSV_MODE); + regmap_update_bits(rt5645->regmap, RT5645_HPO_MIXER, + RT5645_IRQ_PSV_MODE, RT5645_IRQ_PSV_MODE); + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_MIC2_OVCD_EN, RT5645_MIC2_OVCD_EN); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ); + switch (rt5645->pdata.jd_mode) { + case 1: + regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1, + RT5645_JD1_MODE_MASK, + RT5645_JD1_MODE_0); + break; + case 2: + regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1, + RT5645_JD1_MODE_MASK, + RT5645_JD1_MODE_1); + break; + case 3: + regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1, + RT5645_JD1_MODE_MASK, + RT5645_JD1_MODE_2); + break; + default: + break; + } + } + if (rt5645->i2c->irq) { ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index c72220a..a815e36 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -594,6 +594,7 @@ #define RT5645_M_DAC1_HM_SFT 14 #define RT5645_M_HPVOL_HM (0x1 << 13) #define RT5645_M_HPVOL_HM_SFT 13 +#define RT5645_IRQ_PSV_MODE (0x1 << 12) /* SPK Left Mixer Control (0x46) */ #define RT5645_G_RM_L_SM_L_MASK (0x3 << 14) @@ -1350,6 +1351,10 @@ #define RT5645_PWR_CLK25M_PU (0x1 << 4) #define RT5645_IRQ_CLK_MCLK (0x0 << 3) #define RT5645_IRQ_CLK_INT (0x1 << 3) +#define RT5645_JD1_MODE_MASK (0x3 << 0) +#define RT5645_JD1_MODE_0 (0x0 << 0) +#define RT5645_JD1_MODE_1 (0x1 << 0) +#define RT5645_JD1_MODE_2 (0x2 << 0) /* VAD Control 4 (0x9d) */ #define RT5645_VAD_SEL_MASK (0x3 << 8) @@ -1638,6 +1643,7 @@ #define RT5645_OT_P_SFT 10 #define RT5645_OT_P_NOR (0x0 << 10) #define RT5645_OT_P_INV (0x1 << 10) +#define RT5645_IRQ_JD_1_1_EN (0x1 << 9) /* IRQ Control 2 (0xbe) */ #define RT5645_IRQ_MB1_OC_MASK (0x1 << 15) @@ -2120,6 +2126,7 @@ enum { #define RT5645_RXDP2_SEL_SFT (3) /* General Control3 (0xfc) */ +#define RT5645_JD_PSV_MODE (0x1 << 12) #define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11) #define RT5645_MICINDET_MANU (0x1 << 7) -- cgit v0.10.2 From e50334d4e1c3bacfeb3bb1530f73a419d4ec6832 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 17 Nov 2014 15:27:21 +0800 Subject: ASoC: rt5670: check if asrc is useable To use ASRC, the sysclk should be faster than 384 times sample rate of I2S1. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 5e54ac9..3ddb34e 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -576,6 +576,18 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source, } +static int can_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384) + return 1; + + return 0; +} + /* Digital Mixer */ static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER, @@ -1639,8 +1651,8 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc }, { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc }, - { "I2S1", NULL, "I2S1 ASRC" }, - { "I2S2", NULL, "I2S2 ASRC" }, + { "I2S1", NULL, "I2S1 ASRC", can_use_asrc}, + { "I2S2", NULL, "I2S2 ASRC", can_use_asrc}, { "DMIC1", NULL, "DMIC L1" }, { "DMIC1", NULL, "DMIC R1" }, -- cgit v0.10.2 From ff4541c3f48781f84e1cc162d73013aa32a09a41 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 17 Nov 2014 15:27:22 +0800 Subject: ASoC: rt5670: add DMIC ASRC support This patch will enable ASRC for DMIC if ASRC is useable. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 3ddb34e..8bf3a56 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -1294,6 +1294,14 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = { 9, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5670_ASRC_1, + 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5670_ASRC_1, + 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5670_ASRC_1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5670_ASRC_1, + 4, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1, 3, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1, @@ -1650,6 +1658,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc }, { "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc }, { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc }, + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc }, + { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc }, { "I2S1", NULL, "I2S1 ASRC", can_use_asrc}, { "I2S2", NULL, "I2S2 ASRC", can_use_asrc}, -- cgit v0.10.2 From 6fe17da00ba7046db2d3a952a930e127dcd7f06e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 25 Nov 2014 09:51:41 +0800 Subject: ASoC: rt5677: Fix the issue that the regmap_range "rt5677_ranges" cannot be accessed After the patch "ASoC: rt5677: Use specific r/w function for DSP mode", the regmap_range "rt5677_ranges" was not registered in rt5677_regmap_physical, and it caused that the regmap_range "rt5677_ranges" cannot be accessed by the specific r/w function. The patch fixes this issue. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index f2211f1..133010d 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4287,6 +4287,7 @@ static int rt5677_probe(struct snd_soc_codec *codec) } mutex_init(&rt5677->dsp_cmd_lock); + mutex_init(&rt5677->dsp_pri_lock); return 0; } @@ -4344,10 +4345,19 @@ static int rt5677_read(void *context, unsigned int reg, unsigned int *val) struct i2c_client *client = context; struct rt5677_priv *rt5677 = i2c_get_clientdata(client); - if (rt5677->is_dsp_mode) - rt5677_dsp_mode_i2c_read(rt5677, reg, val); - else + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { + mutex_lock(&rt5677->dsp_pri_lock); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); + mutex_unlock(&rt5677->dsp_pri_lock); + } else { + rt5677_dsp_mode_i2c_read(rt5677, reg, val); + } + } else { regmap_read(rt5677->regmap_physical, reg, val); + } return 0; } @@ -4357,10 +4367,20 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) struct i2c_client *client = context; struct rt5677_priv *rt5677 = i2c_get_clientdata(client); - if (rt5677->is_dsp_mode) - rt5677_dsp_mode_i2c_write(rt5677, reg, val); - else + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { + mutex_lock(&rt5677->dsp_pri_lock); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, + val); + mutex_unlock(&rt5677->dsp_pri_lock); + } else { + rt5677_dsp_mode_i2c_write(rt5677, reg, val); + } + } else { regmap_write(rt5677->regmap_physical, reg, val); + } return 0; } @@ -4495,10 +4515,13 @@ static const struct regmap_config rt5677_regmap_physical = { .reg_bits = 8, .val_bits = 16, - .max_register = RT5677_VENDOR_ID2 + 1, + .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) * + RT5677_PR_SPACING), .readable_reg = rt5677_readable_register, .cache_type = REGCACHE_NONE, + .ranges = rt5677_ranges, + .num_ranges = ARRAY_SIZE(rt5677_ranges), }; static const struct regmap_config rt5677_regmap = { diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index a02f64c..dbd9ffd 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1670,7 +1670,7 @@ struct rt5677_priv { struct rt5677_platform_data pdata; struct regmap *regmap, *regmap_physical; const struct firmware *fw1, *fw2; - struct mutex dsp_cmd_lock; + struct mutex dsp_cmd_lock, dsp_pri_lock; int sysclk; int sysclk_src; -- cgit v0.10.2 From 86ea522b369abbbe92b4d66a238e79319ca46ba5 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 24 Oct 2014 16:48:12 -0700 Subject: ASoC: fsl_esai: Use dynamic slot width as default The driver previously used 32-bit fixed slot width as default. In result, ESAI might use 32-bit length to capture 16-bit width audio slot from CODEC side when ESAI is running as DAI slave. So this patch just removes the default slot_width so as to use dynamic slot width. If there comes a specific situation that needs a fixed width, the machine driver shall set slot_width via set_tdm_slot() so as to let the ESAI driver replace the dynamic width policy with the fixed value. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index a645e29..ca319d5 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -513,10 +513,15 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, u32 width = snd_pcm_format_width(params_format(params)); u32 channels = params_channels(params); u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); + u32 slot_width = width; u32 bclk, mask, val; int ret; - bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots; + /* Override slot_width if being specifially set */ + if (esai_priv->slot_width) + slot_width = esai_priv->slot_width; + + bclk = params_rate(params) * slot_width * esai_priv->slots; ret = fsl_esai_set_bclk(dai, tx, bclk); if (ret) @@ -538,7 +543,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, 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); + val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0); regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); @@ -780,9 +785,6 @@ static int fsl_esai_probe(struct platform_device *pdev) return ret; } - /* Set a default slot size */ - esai_priv->slot_width = 32; - /* Set a default slot number */ esai_priv->slots = 2; -- cgit v0.10.2 From cb3fc1ff46667ee4ab23ef22c9e391aa7d014cdf Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 24 Oct 2014 16:48:13 -0700 Subject: ASoC: fsl-asoc-card: Add slot_width setting for cpu-dai ESAI may need to use fixed slot width to comply with external CODEC. So this set_tdm_slot() call will allow the ESAI driver to override its default dynamic slot width policy. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 14572e6..3f6959c 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -51,6 +51,7 @@ struct codec_priv { * @sysclk_freq[2]: SYSCLK rates for set_sysclk() * @sysclk_dir[2]: SYSCLK directions for set_sysclk() * @sysclk_id[2]: SYSCLK ids for set_sysclk() + * @slot_width: Slot width of each frame * * Note: [1] for tx and [0] for rx */ @@ -58,6 +59,7 @@ struct cpu_priv { unsigned long sysclk_freq[2]; u32 sysclk_dir[2]; u32 sysclk_id[2]; + u32 slot_width; }; /** @@ -142,6 +144,15 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, return ret; } + if (cpu_priv->slot_width) { + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, + cpu_priv->slot_width); + if (ret) { + dev_err(dev, "failed to set TDM slot for cpu dai\n"); + return ret; + } + } + return 0; } @@ -453,6 +464,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; + priv->cpu_priv.slot_width = 32; priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { priv->codec_priv.mclk_id = SGTL5000_SYSCLK; -- cgit v0.10.2 From f1e5982546edf96e4537ba689cfad83b90b70143 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 25 Nov 2014 21:00:53 +0800 Subject: ASoC: Intel: Fix stream volume set no effect issue on Broadwell The volume setting control for capture stream doesn't take effect on intel Broadwell platform. Root cause it at 2 points: 1. set stream volume with channel=2 is wrongly bapassed; 2. the saved stream volume should be restored after stream is commit. Here correct these 2 items to fix the stream volume set issue. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index ffd5728..3f8c482 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1042,14 +1042,9 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw, trace_ipc_request("set stream volume", stream->reply.stream_hw_id); - if (channel > 1) + if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL) return -EINVAL; - if (stream->mute[channel]) { - stream->mute_volume[channel] = volume; - return 0; - } - header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT); @@ -1057,9 +1052,28 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw, header |= (stage_id << IPC_STG_ID_SHIFT); req = &stream->vol_req; - req->channel = channel; req->target_volume = volume; + /* set both at same time ? */ + if (channel == SST_HSW_CHANNELS_ALL) { + if (hsw->mute[0] && hsw->mute[1]) { + hsw->mute_volume[0] = hsw->mute_volume[1] = volume; + return 0; + } else if (hsw->mute[0]) + req->channel = 1; + else if (hsw->mute[1]) + req->channel = 0; + else + req->channel = SST_HSW_CHANNELS_ALL; + } else { + /* set only 1 channel */ + if (hsw->mute[channel]) { + hsw->mute_volume[channel] = volume; + return 0; + } + req->channel = channel; + } + ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0); if (ret < 0) { dev_err(hsw->dev, "error: set stream volume failed\n"); @@ -1138,8 +1152,11 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, trace_ipc_request("set mixer volume", volume); + if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL) + return -EINVAL; + /* set both at same time ? */ - if (channel == 2) { + if (channel == SST_HSW_CHANNELS_ALL) { if (hsw->mute[0] && hsw->mute[1]) { hsw->mute_volume[0] = hsw->mute_volume[1] = volume; return 0; @@ -1148,7 +1165,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, else if (hsw->mute[1]) req.channel = 0; else - req.channel = 0xffffffff; + req.channel = SST_HSW_CHANNELS_ALL; } else { /* set only 1 channel */ if (hsw->mute[channel]) { diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 387511f..138e894 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -24,6 +24,7 @@ #define SST_HSW_NO_CHANNELS 4 #define SST_HSW_MAX_DX_REGIONS 14 #define SST_HSW_DX_CONTEXT_SIZE (640 * 1024) +#define SST_HSW_CHANNELS_ALL 0xffffffff #define SST_HSW_FW_LOG_CONFIG_DWORDS 12 #define SST_HSW_GLOBAL_LOG 15 diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index e7a3b6a..13c0100 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -189,7 +189,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, if (ucontrol->value.integer.value[0] == ucontrol->value.integer.value[1]) { volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume); + /* apply volume value to all channels */ + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume); } else { volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume); @@ -255,7 +256,7 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[1]) { volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); - sst_hsw_mixer_set_volume(hsw, 0, 2, volume); + sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume); } else { volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); @@ -525,7 +526,15 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, dev_err(rtd->dev, "error: failed to commit stream %d\n", ret); return ret; } - pcm_data->allocated = true; + + if (!pcm_data->allocated) { + /* Set previous saved volume */ + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, + 0, pcm_data->volume[0]); + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, + 1, pcm_data->volume[1]); + pcm_data->allocated = true; + } ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1); if (ret < 0) @@ -632,12 +641,6 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) return -EINVAL; } - /* Set previous saved volume */ - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, - 0, pcm_data->volume[0]); - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, - 1, pcm_data->volume[1]); - mutex_unlock(&pcm_data->mutex); return 0; } -- cgit v0.10.2 From 93b0f3eeebdce6f32417187b5d24ea218a3e7fb4 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 25 Nov 2014 13:16:12 +0100 Subject: ASoC: core: add multi-codec support in DT This patch exports a core function which handles the DT description of multi-codec links (as: "sound-dai = <&hdmi 0>, <&spdif_codec>;") and creates a CODEC component array in the DAI link. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ba7130..2750e6a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1451,6 +1451,9 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, struct device_node **framemaster); int snd_soc_of_get_dai_name(struct device_node *of_node, const char **dai_name); +int snd_soc_of_get_dai_link_codecs(struct device *dev, + struct device_node *of_node, + struct snd_soc_dai_link *dai_link); #include diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a2..72a3ad0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4750,36 +4750,30 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); -int snd_soc_of_get_dai_name(struct device_node *of_node, - const char **dai_name) +static int snd_soc_get_dai_name(struct of_phandle_args *args, + const char **dai_name) { struct snd_soc_component *pos; - struct of_phandle_args args; - int ret; - - ret = of_parse_phandle_with_args(of_node, "sound-dai", - "#sound-dai-cells", 0, &args); - if (ret) - return ret; - - ret = -EPROBE_DEFER; + int ret = -EPROBE_DEFER; mutex_lock(&client_mutex); list_for_each_entry(pos, &component_list, list) { - if (pos->dev->of_node != args.np) + if (pos->dev->of_node != args->np) continue; if (pos->driver->of_xlate_dai_name) { - ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name); + ret = pos->driver->of_xlate_dai_name(pos, + args, + dai_name); } else { int id = -1; - switch (args.args_count) { + switch (args->args_count) { case 0: id = 0; /* same as dai_drv[0] */ break; case 1: - id = args.args[0]; + id = args->args[0]; break; default: /* not supported */ @@ -4801,6 +4795,21 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, break; } mutex_unlock(&client_mutex); + return ret; +} + +int snd_soc_of_get_dai_name(struct device_node *of_node, + const char **dai_name) +{ + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_args(of_node, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) + return ret; + + ret = snd_soc_get_dai_name(&args, dai_name); of_node_put(args.np); @@ -4808,6 +4817,77 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); +/* + * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree + * @dev: Card device + * @of_node: Device node + * @dai_link: DAI link + * + * Builds an array of CODEC DAI components from the DAI link property + * 'sound-dai'. + * The array is set in the DAI link and the number of DAIs is set accordingly. + * The device nodes in the array (of_node) must be dereferenced by the caller. + * + * Returns 0 for success + */ +int snd_soc_of_get_dai_link_codecs(struct device *dev, + struct device_node *of_node, + struct snd_soc_dai_link *dai_link) +{ + struct of_phandle_args args; + struct snd_soc_dai_link_component *component; + char *name; + int index, num_codecs, ret; + + /* Count the number of CODECs */ + name = "sound-dai"; + num_codecs = of_count_phandle_with_args(of_node, name, + "#sound-dai-cells"); + if (num_codecs <= 0) { + if (num_codecs == -ENOENT) + dev_err(dev, "No 'sound-dai' property\n"); + else + dev_err(dev, "Bad phandle in 'sound-dai'\n"); + return num_codecs; + } + component = devm_kzalloc(dev, + sizeof *component * num_codecs, + GFP_KERNEL); + if (!component) + return -ENOMEM; + dai_link->codecs = component; + dai_link->num_codecs = num_codecs; + + /* Parse the list */ + for (index = 0, component = dai_link->codecs; + index < dai_link->num_codecs; + index++, component++) { + ret = of_parse_phandle_with_args(of_node, name, + "#sound-dai-cells", + index, &args); + if (ret) + goto err; + component->of_node = args.np; + ret = snd_soc_get_dai_name(&args, &component->dai_name); + if (ret < 0) + goto err; + } + return 0; +err: + for (index = 0, component = dai_link->codecs; + index < dai_link->num_codecs; + index++, component++) { + if (!component->of_node) + break; + of_node_put(component->of_node); + component->of_node = NULL; + } + dai_link->codecs = NULL; + dai_link->num_codecs = 0; + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); + static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS -- cgit v0.10.2 From 7195d920bd6094f6810e20a903027cd438bc6d8e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 25 Nov 2014 13:22:45 +0100 Subject: ASoC: simple-card: Remove useless function argument The device node pointer 'cpu' is not used in the function asoc_simple_card_parse_daifmt(). Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3e3fec4..ece22d5 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -232,7 +232,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np, static int asoc_simple_card_parse_daifmt(struct device_node *node, struct simple_card_data *priv, - struct device_node *cpu, struct device_node *codec, char *prefix, int idx) { @@ -309,7 +308,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, } ret = asoc_simple_card_parse_daifmt(node, priv, - cpu, codec, prefix, idx); + codec, prefix, idx); if (ret < 0) goto dai_link_of_err; -- cgit v0.10.2 From 69b93607a9862b1db2f0f2e078e1396f8e20fa9b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 25 Nov 2014 20:29:40 +0100 Subject: ASoC: qi_lb60: Pass flags to gpiod_get() Pass flags to gpiod_get() to automatically configure the GPIOs. This is shorter and not passing any flags to gpiod_get() will eventually no longer be supported. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c index 5cb91f9..0fb7d2a 100644 --- a/sound/soc/jz4740/qi_lb60.c +++ b/sound/soc/jz4740/qi_lb60.c @@ -77,25 +77,18 @@ static int qi_lb60_probe(struct platform_device *pdev) { struct qi_lb60 *qi_lb60; struct snd_soc_card *card = &qi_lb60_card; - int ret; qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL); if (!qi_lb60) return -ENOMEM; - qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd"); + qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd", GPIOD_OUT_LOW); if (IS_ERR(qi_lb60->snd_gpio)) return PTR_ERR(qi_lb60->snd_gpio); - ret = gpiod_direction_output(qi_lb60->snd_gpio, 0); - if (ret) - return ret; - qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp"); + qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp", GPIOD_OUT_LOW); if (IS_ERR(qi_lb60->amp_gpio)) return PTR_ERR(qi_lb60->amp_gpio); - ret = gpiod_direction_output(qi_lb60->amp_gpio, 0); - if (ret) - return ret; card->dev = &pdev->dev; -- cgit v0.10.2 From e874bf5f7647a9fdf14d72dbb376ec95327e3a81 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 25 Nov 2014 21:41:03 +0100 Subject: ASoC: Disable regmap helpers if regmap is disabled If regmap is disabled there will be no users of the ASoC regmap helpers. Furthermore regmap_exit() will no be defined causing the following compile error: sound/soc/soc-core.c: In function 'snd_soc_component_exit_regmap': sound/soc/soc-core.c:2645:2: error: implicit declaration of function 'regmap_exit' [-Werror=implicit-function-declaration] So disable the helpers if regmap is disabled. Reported-by: kbuild test robot Fixes: 20feb881988c ASoC: Add helper functions for deferred regmap setup") Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 80ca937..b532348 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1286,6 +1286,8 @@ void snd_soc_component_async_complete(struct snd_soc_component *component); int snd_soc_component_test_bits(struct snd_soc_component *component, unsigned int reg, unsigned int mask, unsigned int value); +#ifdef CONFIG_REGMAP + void snd_soc_component_init_regmap(struct snd_soc_component *component, struct regmap *regmap); void snd_soc_component_exit_regmap(struct snd_soc_component *component); @@ -1321,6 +1323,8 @@ static inline void snd_soc_codec_exit_regmap(struct snd_soc_codec *codec) snd_soc_component_exit_regmap(&codec->component); } +#endif + /* device driver data */ static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index db74c06..1edc519 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3661,6 +3661,8 @@ static void snd_soc_component_setup_regmap(struct snd_soc_component *component) component->val_bytes = val_bytes; } +#ifdef CONFIG_REGMAP + /** * snd_soc_component_init_regmap() - Initialize regmap instance for the component * @component: The component for which to initialize the regmap instance @@ -3696,6 +3698,8 @@ void snd_soc_component_exit_regmap(struct snd_soc_component *component) } EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); +#endif + static void snd_soc_component_add_unlocked(struct snd_soc_component *component) { if (!component->write && !component->read) { -- cgit v0.10.2 From c362effe5cda4df02aa7670d58636ea73979e304 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 25 Nov 2014 12:14:48 +0100 Subject: ASoC: Remove 'const' from the device_node pointers As Russell King's explained it, there should not be pointers to struct device_node: "struct device_node is a ref-counted structure. That means if you store a reference to it, you should "get" it, and you should "put" it once you've done. The act of "put"ing the pointed-to structure involves writing to that structure, so it is totally unappropriate to store a device_node structure as a const pointer. It forces you to have to cast it back to a non-const pointer at various points in time to use various OF function calls." [This isn't quite the application here, we're not geting or putting the pointer though we did add some other users who call non-const OF functions -- broonie] Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index edbb07b..d1f3dac 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -881,7 +881,7 @@ struct snd_soc_platform_driver { struct snd_soc_dai_link_component { const char *name; - const struct device_node *of_node; + struct device_node *of_node; const char *dai_name; }; @@ -983,7 +983,7 @@ struct snd_soc_codec_conf { * DT/OF node, but not both. */ const char *dev_name; - const struct device_node *of_node; + struct device_node *of_node; /* * optional map of kcontrol, widget and path name prefixes that are @@ -1000,7 +1000,7 @@ struct snd_soc_aux_dev { * DT/OF node, but not both. */ const char *codec_name; - const struct device_node *codec_of_node; + struct device_node *codec_of_node; /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_component *component); -- cgit v0.10.2 From 094cb98179f19b75acf9ff471daabf3948ce98e6 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 25 Nov 2014 15:05:13 +0000 Subject: of/fdt: memblock_reserve /memreserve/ regions in the case of partial overlap memblock_is_region_reserved() returns true in the case of a partial overlap, meaning that the current code fails to reserve the non-overlapping portion. This call was introduced as part of d1552ce449eb "of/fdt: move memreserve and dtb memory reservations into core" which went into v3.16. I observed this causing a Midway system with a buggy fdt (the header declares itself to be larger than it really is) failing to boot because the over-inflated size of the fdt was causing it to seem to run into the swapper_pg_dir region, meaning the DT wasn't reserved. The symptoms were failing to find an disks or network and failing to boot. However given the ambiguity of whether things like the initrd are covered by /memreserve/ and similar I think it is best to also register the region rather than just ignoring it. Since memblock_reserve() handles overlaps just fine lets just warn and carry on. Signed-off-by: Ian Campbell Signed-off-by: Grant Likely Cc: Rob Herring Cc: stable@vger.kernel.org # v3.16+ diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 30e97bc..d134710 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -964,8 +964,6 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, bool nomap) { - if (memblock_is_region_reserved(base, size)) - return -EBUSY; if (nomap) return memblock_remove(base, size); return memblock_reserve(base, size); -- cgit v0.10.2 From d683d0b690c13437d752ccce47963ac64119b07a Mon Sep 17 00:00:00 2001 From: Krishna Mohan Dani Date: Wed, 26 Nov 2014 14:53:04 +0530 Subject: ASoC: Samsung: Add arndale_rt5631 machine driver and binding Adding machine driver to instantiate I2S based realtek's ALC5631 sound card on Arndale board. There are other variants of Audio Daughter Cards for Arndale Board for which support already exists but there is no support for Realtek's alc5631 codec hence support for ALC5631 based machine driver is being added. This patch also documents the device tree binding for the Arndale board based machine driver. Signed-off-by: Claude Youn Signed-off-by: Krishna Mohan Dani Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt new file mode 100644 index 0000000..0e76946 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/arndale.txt @@ -0,0 +1,24 @@ +Audio Binding for Arndale boards + +Required properties: +- compatible : Can be the following, + "samsung,arndale-rt5631" + +- samsung,audio-cpu: The phandle of the Samsung I2S controller +- samsung,audio-codec: The phandle of the audio codec + +Optional: +- samsung,model: The name of the sound-card + +Arndale Boards has many audio daughter cards, one of them is +rt5631/alc5631. Below example shows audio bindings for rt5631/ +alc5631 based codec. + +Example: + +sound { + compatible = "samsung,arndale-rt5631"; + + samsung,audio-cpu = <&i2s0> + samsung,audio-codec = <&rt5631>; +}; diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index e0e737f..fc67f97 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -239,3 +239,9 @@ config SND_SOC_ODROIDX2 select SND_SAMSUNG_I2S help Say Y here to enable audio support for the Odroid-X2/U3. + +config SND_SOC_ARNDALE_RT5631_ALC5631 + tristate "Audio support for RT5631(ALC5631) on Arndale Board" + depends on SND_SOC_SAMSUNG + select SND_SAMSUNG_I2S + select SND_SOC_RT5631 diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 91505dd..31e3dba 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -45,6 +45,7 @@ snd-soc-lowland-objs := lowland.o snd-soc-littlemill-objs := littlemill.o snd-soc-bells-objs := bells.o snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o +snd-soc-arndale-rt5631-objs := arndale_rt5631.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o +obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c new file mode 100644 index 0000000..1e2b61c --- /dev/null +++ b/sound/soc/samsung/arndale_rt5631.c @@ -0,0 +1,150 @@ +/* + * arndale_rt5631.c + * + * Copyright (c) 2014, Insignal Co., Ltd. + * + * Author: Claude + * + * 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 +#include +#include + +#include +#include +#include +#include + +#include "i2s.h" + +static int arndale_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; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int rfs, ret; + unsigned long rclk; + + rfs = 256; + + rclk = params_rate(params) * rfs; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, + 0, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, + 0, SND_SOC_CLOCK_OUT); + + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops arndale_ops = { + .hw_params = arndale_hw_params, +}; + +static struct snd_soc_dai_link arndale_rt5631_dai[] = { + { + .name = "RT5631 HiFi", + .stream_name = "Primary", + .codec_dai_name = "rt5631-hifi", + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .ops = &arndale_ops, + }, +}; + +static struct snd_soc_card arndale_rt5631 = { + .name = "Arndale RT5631", + .dai_link = arndale_rt5631_dai, + .num_links = ARRAY_SIZE(arndale_rt5631_dai), +}; + +static int arndale_audio_probe(struct platform_device *pdev) +{ + int n, ret; + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &arndale_rt5631; + + card->dev = &pdev->dev; + + for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) { + if (!arndale_rt5631_dai[n].cpu_dai_name) { + arndale_rt5631_dai[n].cpu_of_node = of_parse_phandle(np, + "samsung,audio-cpu", n); + + if (!arndale_rt5631_dai[n].cpu_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,audio-cpu' missing or invalid\n"); + return -EINVAL; + } + } + if (!arndale_rt5631_dai[n].platform_name) + arndale_rt5631_dai[n].platform_of_node = + arndale_rt5631_dai[n].cpu_of_node; + + arndale_rt5631_dai[n].codec_name = NULL; + arndale_rt5631_dai[n].codec_of_node = of_parse_phandle(np, + "samsung,audio-codec", n); + if (!arndale_rt5631_dai[0].codec_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,audio-codec' missing or invalid\n"); + return -EINVAL; + } + } + + ret = devm_snd_soc_register_card(card->dev, card); + + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + + return ret; +} + +static int arndale_audio_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = { + { .compatible = "samsung,arndale-rt5631", }, + { .compatible = "samsung,arndale-alc5631", }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match); + +static struct platform_driver arndale_audio_driver = { + .driver = { + .name = "arndale-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match), + }, + .probe = arndale_audio_probe, + .remove = arndale_audio_remove, +}; + +module_platform_driver(arndale_audio_driver); + +MODULE_AUTHOR("Claude "); +MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 3ad5e861a715cbe932cd145d4612c11e5912a72f Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Thu, 27 Nov 2014 16:53:08 +0800 Subject: ASoC: wm8960: Move register initialisation to I2C driver probe() We must ensure that the clocking configuration is valid as rapidly as possible. And do software reset before the others registers updates, or the registers will be reset to the default state. Signed-off-by: Zidan Wang Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index bc8793cd..031a1ae 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -128,7 +128,7 @@ struct wm8960_priv { struct wm8960_data pdata; }; -#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) +#define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) /* enumerated controls */ static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", @@ -947,31 +947,12 @@ static int wm8960_probe(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); struct wm8960_data *pdata = &wm8960->pdata; - int ret; if (pdata->capless) wm8960->set_bias_level = wm8960_set_bias_level_capless; else wm8960->set_bias_level = wm8960_set_bias_level_out3; - ret = wm8960_reset(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset\n"); - return ret; - } - - /* Latch the update bits */ - snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100); - snd_soc_add_codec_controls(codec, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls)); wm8960_add_widgets(codec); @@ -1030,7 +1011,13 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, else if (i2c->dev.of_node) wm8960_set_pdata_from_of(i2c, &wm8960->pdata); - if (pdata && pdata->shared_lrclk) { + ret = wm8960_reset(wm8960->regmap); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset\n"); + return ret; + } + + if (wm8960->pdata.shared_lrclk) { ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 0x4, 0x4); if (ret != 0) { @@ -1040,6 +1027,18 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, } } + /* Latch the update bits */ + regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100); + i2c_set_clientdata(i2c, wm8960); ret = snd_soc_register_codec(&i2c->dev, -- cgit v0.10.2 From d98123a76be53d570d72e04aac3e195a560ef149 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 27 Nov 2014 01:35:16 +0300 Subject: ASoC: sigmadsp: uninitialized variable in sigmadsp_activate_ctrl() The "changed" variable should be set to false at the start. Fixes: a35daac77a03 ('ASoC: sigmadsp: Add support for fw v2') Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 34fdc40..d53680a 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -661,7 +661,8 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, struct snd_card *card = sigmadsp->component->card->snd_card; struct snd_kcontrol_volatile *vd; struct snd_ctl_elem_id id; - bool active, changed; + bool active; + bool changed = false; active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask); -- cgit v0.10.2 From 525b8634d8dee0e3a8409a73ef5f22ac8676a8c4 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 27 Nov 2014 14:04:23 +0800 Subject: ASoC: Intel: Remove useless loopback volume control for Broadwell On Broadwell, the ADSP FW don't support loopback record volume tuning, so here remove this control. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 13c0100..7aa348d 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -308,10 +308,6 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8, ARRAY_SIZE(volume_map), 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), - /* Loopback volume */ - SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8, - ARRAY_SIZE(volume_map), 0, - hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), /* Mic Capture volume */ SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, ARRAY_SIZE(volume_map), 0, -- cgit v0.10.2 From 002fe7c831404d179266cfe0dad00a67333256f1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:50 +0100 Subject: ASoC: cq93vc: Remove unused state struct While two of the fields in the cq93vc driver state struct are initialized none of them are ever acutally read again. So remove the whole struct. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/linux/mfd/davinci_voicecodec.h b/include/linux/mfd/davinci_voicecodec.h index cb01496..8e1cdbe 100644 --- a/include/linux/mfd/davinci_voicecodec.h +++ b/include/linux/mfd/davinci_voicecodec.h @@ -99,12 +99,6 @@ struct davinci_vcif { dma_addr_t dma_rx_addr; }; -struct cq93vc { - struct platform_device *pdev; - struct snd_soc_codec *codec; - u32 sysclk; -}; - struct davinci_vc; struct davinci_vc { @@ -122,7 +116,6 @@ struct davinci_vc { /* Client devices */ struct davinci_vcif davinci_vcif; - struct cq93vc cq93vc; }; #endif diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 537327c..036a877 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -62,14 +62,10 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute) static int cq93vc_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 davinci_vc *davinci_vc = codec->dev->platform_data; - switch (freq) { case 22579200: case 27000000: case 33868800: - davinci_vc->cq93vc.sysclk = freq; return 0; } @@ -135,10 +131,6 @@ static int cq93vc_resume(struct snd_soc_codec *codec) static int cq93vc_probe(struct snd_soc_codec *codec) { - struct davinci_vc *davinci_vc = codec->dev->platform_data; - - davinci_vc->cq93vc.codec = codec; - /* Off, with power on */ cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -- cgit v0.10.2 From 6200b75a8bbc16e434bf3d8ca54538ea678ccbd7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:51 +0100 Subject: ASoC: cq93vc: Cleanup manual bias level transitions Remove the manual transition back to SND_SOC_BIAS_STANDBY in resume. This is already be automatically handled by the ASoC core. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. While we are at it also remove the unused codec field from the cq93vc struct so the whole probe function can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 036a877..8d638e8 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -122,28 +122,6 @@ static struct snd_soc_dai_driver cq93vc_dai = { .ops = &cq93vc_dai_ops, }; -static int cq93vc_resume(struct snd_soc_codec *codec) -{ - cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int cq93vc_probe(struct snd_soc_codec *codec) -{ - /* Off, with power on */ - cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int cq93vc_remove(struct snd_soc_codec *codec) -{ - cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct regmap *cq93vc_get_regmap(struct device *dev) { struct davinci_vc *davinci_vc = dev->platform_data; @@ -153,9 +131,6 @@ static struct regmap *cq93vc_get_regmap(struct device *dev) static struct snd_soc_codec_driver soc_codec_dev_cq93vc = { .set_bias_level = cq93vc_set_bias_level, - .probe = cq93vc_probe, - .remove = cq93vc_remove, - .resume = cq93vc_resume, .get_regmap = cq93vc_get_regmap, .controls = cq93vc_snd_controls, .num_controls = ARRAY_SIZE(cq93vc_snd_controls), -- cgit v0.10.2 From 68d27bc63c4f331c912dfb92168f5fe4753c61c9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:52 +0100 Subject: ASoC: lm49453: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index c1ae576..c4dfde9 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1395,15 +1395,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = { }, }; -/* power down chip */ -static int lm49453_remove(struct snd_soc_codec *codec) -{ - lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_lm49453 = { - .remove = lm49453_remove, .set_bias_level = lm49453_set_bias_level, .controls = lm49453_snd_controls, .num_controls = ARRAY_SIZE(lm49453_snd_controls), -- cgit v0.10.2 From 0eef4ed5970a736bf2449b389fb44f6fe3635765 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:53 +0100 Subject: ASoC: sn95031: Cleanup bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 6167c59..31d97cd 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -870,17 +870,8 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec) return 0; } -static int sn95031_codec_remove(struct snd_soc_codec *codec) -{ - pr_debug("codec_remove called\n"); - sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct snd_soc_codec_driver sn95031_codec = { .probe = sn95031_codec_probe, - .remove = sn95031_codec_remove, .set_bias_level = sn95031_set_vaud_bias, .idle_bias_off = true, -- cgit v0.10.2 From aabb87f00304764dffe097e3b65f6a1862c2c2b5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:54 +0100 Subject: ASoC: tlv320aic23: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index d671679..cc17e7e 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -540,19 +540,11 @@ static struct snd_soc_dai_driver tlv320aic23_dai = { .ops = &tlv320aic23_dai_ops, }; -static int tlv320aic23_suspend(struct snd_soc_codec *codec) -{ - tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static int tlv320aic23_resume(struct snd_soc_codec *codec) { struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); regcache_mark_dirty(aic23->regmap); regcache_sync(aic23->regmap); - tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } @@ -562,9 +554,6 @@ static int tlv320aic23_codec_probe(struct snd_soc_codec *codec) /* Reset codec */ snd_soc_write(codec, TLV320AIC23_RESET, 0); - /* power on device */ - tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K); /* Unmute input */ @@ -589,18 +578,12 @@ static int tlv320aic23_codec_probe(struct snd_soc_codec *codec) return 0; } -static int tlv320aic23_remove(struct snd_soc_codec *codec) -{ - tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { .probe = tlv320aic23_codec_probe, - .remove = tlv320aic23_remove, - .suspend = tlv320aic23_suspend, .resume = tlv320aic23_resume, .set_bias_level = tlv320aic23_set_bias_level, + .suspend_bias_off = true, + .controls = tlv320aic23_snd_controls, .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls), .dapm_widgets = tlv320aic23_dapm_widgets, -- cgit v0.10.2 From a43a262901363ea412c288e5ebc3a3c0a8ff6591 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:55 +0100 Subject: ASoC: tlv320aix31xx: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Acked-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 145fe5b..6cd5f50 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1056,18 +1056,6 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int aic31xx_suspend(struct snd_soc_codec *codec) -{ - aic31xx_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int aic31xx_resume(struct snd_soc_codec *codec) -{ - aic31xx_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int aic31xx_codec_probe(struct snd_soc_codec *codec) { int ret = 0; @@ -1110,8 +1098,6 @@ static int aic31xx_codec_remove(struct snd_soc_codec *codec) { struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int i; - /* power down chip */ - aic31xx_set_bias_level(codec, SND_SOC_BIAS_OFF); for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) regulator_unregister_notifier(aic31xx->supplies[i].consumer, @@ -1123,9 +1109,9 @@ static int aic31xx_codec_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_driver_aic31xx = { .probe = aic31xx_codec_probe, .remove = aic31xx_codec_remove, - .suspend = aic31xx_suspend, - .resume = aic31xx_resume, .set_bias_level = aic31xx_set_bias_level, + .suspend_bias_off = true, + .controls = aic31xx_snd_controls, .num_controls = ARRAY_SIZE(aic31xx_snd_controls), .dapm_widgets = aic31xx_dapm_widgets, -- cgit v0.10.2 From f10c0a71e6efc7c8cbc3bfcfd0ecf822607f0b3d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:56 +0100 Subject: ASoC: tlv320aic32x4: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 6ea662d..015467e 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -597,18 +597,6 @@ static struct snd_soc_dai_driver aic32x4_dai = { .symmetric_rates = 1, }; -static int aic32x4_suspend(struct snd_soc_codec *codec) -{ - aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int aic32x4_resume(struct snd_soc_codec *codec) -{ - aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int aic32x4_probe(struct snd_soc_codec *codec) { struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); @@ -654,8 +642,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec) snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_CM1R_10K); - aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* * Workaround: for an unknown reason, the ADC needs to be powered up * and down for the first capture to work properly. It seems related to @@ -669,18 +655,10 @@ static int aic32x4_probe(struct snd_soc_codec *codec) return 0; } -static int aic32x4_remove(struct snd_soc_codec *codec) -{ - aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { .probe = aic32x4_probe, - .remove = aic32x4_remove, - .suspend = aic32x4_suspend, - .resume = aic32x4_resume, .set_bias_level = aic32x4_set_bias_level, + .suspend_bias_off = true, .controls = aic32x4_snd_controls, .num_controls = ARRAY_SIZE(aic32x4_snd_controls), -- cgit v0.10.2 From 68f438378cde79e29f71c7e043b10d76001d8892 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:57 +0100 Subject: ASoC: tlv320aic3x: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index f0a8281..b7ebce0 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1491,7 +1491,6 @@ static int aic3x_remove(struct snd_soc_codec *codec) struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); int i; - aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); list_del(&aic3x->list); for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) regulator_unregister_notifier(aic3x->supplies[i].consumer, -- cgit v0.10.2 From 90db15e17e46e2841843bf20f258fed963228bed Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:58 +0100 Subject: ASoC: tlv320dac33: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index e21ed93..0fe2ced 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1436,8 +1436,6 @@ static int dac33_soc_remove(struct snd_soc_codec *codec) { struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - dac33_set_bias_level(codec, SND_SOC_BIAS_OFF); - if (dac33->irq >= 0) { free_irq(dac33->irq, dac33->codec); destroy_workqueue(dac33->dac33_wq); -- cgit v0.10.2 From 3ec8d2036464d961a6314281ae68d80a5c071d07 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:57:59 +0100 Subject: ASoC: twl4030: Cleanup manual bias level transitions Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index b6b0cb3..27f3b21 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -2177,8 +2177,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; - twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); - if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio)) gpio_free(pdata->hs_extmute_gpio); -- cgit v0.10.2 From c4ee42a050e82855aa06d7217937b1549c95bef3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 26 Nov 2014 20:58:00 +0100 Subject: ASoC: twl6040: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 0f6067f..5ff2b1e 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1095,25 +1095,6 @@ static struct snd_soc_dai_driver twl6040_dai[] = { }, }; -#ifdef CONFIG_PM -static int twl6040_suspend(struct snd_soc_codec *codec) -{ - twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int twl6040_resume(struct snd_soc_codec *codec) -{ - twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define twl6040_suspend NULL -#define twl6040_resume NULL -#endif - static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; @@ -1160,7 +1141,6 @@ static int twl6040_remove(struct snd_soc_codec *codec) struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); free_irq(priv->plug_irq, codec); - twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1168,11 +1148,10 @@ static int twl6040_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { .probe = twl6040_probe, .remove = twl6040_remove, - .suspend = twl6040_suspend, - .resume = twl6040_resume, .read = twl6040_read, .write = twl6040_write, .set_bias_level = twl6040_set_bias_level, + .suspend_bias_off = true, .ignore_pmdown_time = true, .controls = twl6040_snd_controls, -- cgit v0.10.2 From d819ce965d451aac08e46c9f8e2119fe3a845786 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 27 Nov 2014 13:02:00 -0200 Subject: ASoC: sgtl5000: Remove MCLK restriction According to the sgtl5000 datasheet the MCLK frequency range restriction of 8 to 27 MHz only applies when the PLL is used - synchronous SYS_MCLK input mode. When running the codec as slave, the master should generate MCLK in the range of 256*fs, 384*fs or 512*fs, which is called asynchronous SYS_MCLK input mode. In asynchronous SYS_MCLK we cannot have the 8 to 27 MHz check because if we want to play a 8KHz sample rate track, with a MCLK of 8k * 512 = 4.096MHz the current check would return -EINVAL, which is not correct. Remove the 8 to 27MHz frequency check, since this only applies to the synchronous SYS_MCLK input case. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 490404c..4735791 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1435,7 +1435,6 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, { struct sgtl5000_priv *sgtl5000; int ret, reg, rev; - unsigned int mclk; struct device_node *np = client->dev.of_node; u32 value; @@ -1460,14 +1459,6 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, return ret; } - /* SGTL5000 SYS_MCLK should be between 8 and 27 MHz */ - mclk = clk_get_rate(sgtl5000->mclk); - if (mclk < 8000000 || mclk > 27000000) { - dev_err(&client->dev, "Invalid SYS_CLK frequency: %u.%03uMHz\n", - mclk / 1000000, mclk / 1000 % 1000); - return -EINVAL; - } - ret = clk_prepare_enable(sgtl5000->mclk); if (ret) return ret; -- cgit v0.10.2 From 2a4cfd10229dc93507aa5ddbc1ba0162140f4951 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 27 Nov 2014 13:02:01 -0200 Subject: ASoC: sgtl5000: Allow 8kHz playback in codec slave mode When trying to play a 8kHz file with codec in slave mode we get the following error on a mx28evk: $ aplay -Dhw:0,0 stereo_8k.wav Playing WAVE 'stereo_8k.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo [ 21.218647] sgtl5000 0-000a: PLL not supported in slave mode [ 21.224559] sgtl5000 0-000a: 128 ratio is not supported. SYS_MCLK needs to be 256, 384 or 512 * fs [ 21.233687] sgtl5000 0-000a: ASoC: can't set sgtl5000 hw params: -22 aplay: set_params:1123: Unable to install hw params: This error happens because we are using 'sys_fs' instead of 'frame_rate' in the valid ratio check. Use the real'frame_rate' so that the ratio is correctly calculated and the playback can run. sgtl5000 codec manual states that in 'Synchronous SYS_MCLK input' mode that the following SYS_CLK frequencies are allowed: 256*fs, 384*fs, 512*fs. , where fs is the sampling frequency, which can be in the range of: 8, 11.025, 16, 22.5, 32, 44.1, 48, 96 kHz. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 4735791..47d6ca0 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -618,7 +618,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) * factor of freq = 96 kHz can only be 256, since mclk is in the range * of 8 MHz - 27 MHz */ - switch (sgtl5000->sysclk / sys_fs) { + switch (sgtl5000->sysclk / frame_rate) { case 256: clk_ctl |= SGTL5000_MCLK_FREQ_256FS << SGTL5000_MCLK_FREQ_SHIFT; @@ -641,7 +641,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) "PLL not supported in slave mode\n"); dev_err(codec->dev, "%d ratio is not supported. " "SYS_MCLK needs to be 256, 384 or 512 * fs\n", - sgtl5000->sysclk / sys_fs); + sgtl5000->sysclk / frame_rate); return -EINVAL; } } -- cgit v0.10.2 From d206f66177ab7cd69d79c7e01b43f45d935f43dd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 27 Nov 2014 13:01:59 -0200 Subject: ASoC: mxs-sgtl5000: Remove MCLK restriction According to the sgtl5000 datasheet the MCLK frequency range restriction of 8 to 27 MHz only applies when the PLL is used - synchronous SYS_MCLK input mode. mxs-sgtl5000 machine sets the codec as slave, and mx28 generates MCLK in the range of 256*fs, 384*fs or 512*fs, which is called asynchronous SYS_MCLK input. In asynchronous SYS_MCLK we cannot have the 8 to 27 MHz check because if we want to play a 8KHz sample rate track, with a MCLK of 8k * 512 = 4.096MHz the current check would return -EINVAL, which is not correct. Remove the 8 to 27MHz frequency check, since this only applies to the synchronous SYS_MCLK input case. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 61822cc..3bba6cf 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -49,13 +49,6 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, break; } - /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */ - if (mclk < 8000000 || mclk > 27000000) { - dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n", - mclk / 1000000, mclk / 1000 % 1000); - return -EINVAL; - } - /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */ ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0); if (ret) { -- cgit v0.10.2 From c5f0406bdce6eebc9147a54f4be0250618d3a423 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Fri, 28 Nov 2014 10:41:28 +0800 Subject: ASoC: Intel: Correct the xmax volume The xmax volume should be corrected to ARRAY_SIZE(volume_map)-1, otherwise, the xmax value will be mapped to 0 wrongly. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 7aa348d..f993079 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -298,19 +298,19 @@ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1); static const struct snd_kcontrol_new hsw_volume_controls[] = { /* Global DSP volume */ SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8, - ARRAY_SIZE(volume_map) -1, 0, + ARRAY_SIZE(volume_map) - 1, 0, hsw_volume_get, hsw_volume_put, hsw_vol_tlv), /* Offload 0 volume */ SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8, - ARRAY_SIZE(volume_map), 0, + ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), /* Offload 1 volume */ SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8, - ARRAY_SIZE(volume_map), 0, + ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), /* Mic Capture volume */ SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, - ARRAY_SIZE(volume_map), 0, + ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), }; -- cgit v0.10.2 From 7bb73cbd073c495b5aed9ec446faa17a262f18be Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Fri, 28 Nov 2014 21:52:11 +0800 Subject: ASoC: Intel: Move capture PCM pin to PCM0 for Broadwell/Haswell Move capture PCM pin from PCM4 to PCM0 for Broadwell/Haswell. This will allow us to integrate with pulseaudio better for usually default device is set to 0. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/broadwell.c index 52cb764..c256764 100644 --- a/sound/soc/intel/broadwell.c +++ b/sound/soc/intel/broadwell.c @@ -171,7 +171,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { /* Front End DAI links */ { .name = "System PCM", - .stream_name = "System Playback", + .stream_name = "System Playback/Capture", .cpu_dai_name = "System Pin", .platform_name = "haswell-pcm-audio", .dynamic = 1, @@ -180,6 +180,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .init = broadwell_rtd_init, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, + .dpcm_capture = 1, }, { .name = "Offload0", @@ -214,18 +215,6 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_capture = 1, }, - { - .name = "Capture PCM", - .stream_name = "Capture", - .cpu_dai_name = "Capture Pin", - .platform_name = "haswell-pcm-audio", - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_capture = 1, - }, - /* Back End DAI links */ { /* SSP0 - Codec */ diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/haswell.c index 3981982..cb8a482 100644 --- a/sound/soc/intel/haswell.c +++ b/sound/soc/intel/haswell.c @@ -109,7 +109,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { /* Front End DAI links */ { .name = "System", - .stream_name = "System Playback", + .stream_name = "System Playback/Capture", .cpu_dai_name = "System Pin", .platform_name = "haswell-pcm-audio", .dynamic = 1, @@ -118,6 +118,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { .init = haswell_rtd_init, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, + .dpcm_capture = 1, }, { .name = "Offload0", @@ -152,17 +153,6 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_capture = 1, }, - { - .name = "Capture", - .stream_name = "Capture", - .cpu_dai_name = "Capture Pin", - .platform_name = "haswell-pcm-audio", - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_capture = 1, - }, /* Back End DAI links */ { diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index f993079..f6a9acf 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -309,7 +309,7 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), /* Mic Capture volume */ - SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, + SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8, ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), }; @@ -396,8 +396,14 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, /* DSP stream type depends on DAI ID */ switch (rtd->cpu_dai->id) { case 0: - stream_type = SST_HSW_STREAM_TYPE_SYSTEM; - module_id = SST_HSW_MODULE_PCM_SYSTEM; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + stream_type = SST_HSW_STREAM_TYPE_SYSTEM; + module_id = SST_HSW_MODULE_PCM_SYSTEM; + } + else { + stream_type = SST_HSW_STREAM_TYPE_CAPTURE; + module_id = SST_HSW_MODULE_PCM_CAPTURE; + } break; case 1: case 2: @@ -410,10 +416,6 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, path_id = SST_HSW_STREAM_PATH_SSP0_OUT; module_id = SST_HSW_MODULE_PCM_REFERENCE; break; - case 4: - stream_type = SST_HSW_STREAM_TYPE_CAPTURE; - module_id = SST_HSW_MODULE_PCM_CAPTURE; - break; default: dev_err(rtd->dev, "error: invalid DAI ID %d\n", rtd->cpu_dai->id); @@ -781,6 +783,13 @@ static struct snd_soc_dai_driver hsw_dais[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, }, + .capture = { + .stream_name = "Analog Capture", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, + }, }, { /* PCM */ @@ -817,17 +826,6 @@ static struct snd_soc_dai_driver hsw_dais[] = { .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, }, }, - { - .name = "Capture Pin", - .id = HSW_PCM_DAI_ID_CAPTURE, - .capture = { - .stream_name = "Analog Capture", - .channels_min = 2, - .channels_max = 4, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, - }, - }, }; static const struct snd_soc_dapm_widget widgets[] = { -- cgit v0.10.2 From f6c6fda4c9e17940b0a2ba206b0408babfdc930c Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Thu, 27 Nov 2014 00:22:33 +0100 Subject: bond: Check length of IFLA_BOND_ARP_IP_TARGET attributes Fixes: 7f28fa10 ("bonding: add arp_ip_target netlink support") Reported-by: John Fastabend Cc: Scott Feldman Signed-off-by: Thomas Graf Acked-by: John Fastabend Signed-off-by: David S. Miller diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index c13d83e..45f09a6 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -225,7 +225,12 @@ static int bond_changelink(struct net_device *bond_dev, bond_option_arp_ip_targets_clear(bond); nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) { - __be32 target = nla_get_be32(attr); + __be32 target; + + if (nla_len(attr) < sizeof(target)) + return -EINVAL; + + target = nla_get_be32(attr); bond_opt_initval(&newval, (__force u64)target); err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS, -- cgit v0.10.2 From e0ebde0e131b529fd721b24f62872def5ec3718c Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 27 Nov 2014 10:16:15 +0100 Subject: rtnetlink: release net refcnt on error in do_setlink() rtnl_link_get_net() holds a reference on the 'struct net', we need to release it in case of error. CC: Eric W. Biederman Fixes: b51642f6d77b ("net: Enable a userns root rtnl calls that are safe for unprivilged users") Signed-off-by: Nicolas Dichtel Reviewed-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b9b7dfa..76321ea 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1498,6 +1498,7 @@ static int do_setlink(const struct sk_buff *skb, goto errout; } if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { + put_net(net); err = -EPERM; goto errout; } -- cgit v0.10.2 From 4d6a949c62f123569fb355b6ec7f314b76f93735 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Kimura Date: Thu, 27 Nov 2014 20:34:00 +0900 Subject: sh_eth: Fix skb alloc size and alignment adjust rule. In the current driver, allocation size of skb does not care the alignment adjust after allocation. And also, in the current implementation, buffer alignment method by sh_eth_set_receive_align function has a bug that this function displace buffer start address forcedly when the alignment is corrected. In the result, tail of the skb will exceed allocated area and kernel panic will be occurred. This patch fix this issue. Signed-off-by: Mitsuhiro Kimura Signed-off-by: Yoshihiro Kaneko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 60e9c2c..f9e30b8 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -917,21 +917,13 @@ static int sh_eth_reset(struct net_device *ndev) return ret; } -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) static void sh_eth_set_receive_align(struct sk_buff *skb) { - int reserve; + uintptr_t reserve = (uintptr_t)skb->data & (SH_ETH_RX_ALIGN - 1); - reserve = SH4_SKB_RX_ALIGN - ((u32)skb->data & (SH4_SKB_RX_ALIGN - 1)); if (reserve) - skb_reserve(skb, reserve); + skb_reserve(skb, SH_ETH_RX_ALIGN - reserve); } -#else -static void sh_eth_set_receive_align(struct sk_buff *skb) -{ - skb_reserve(skb, SH2_SH3_SKB_RX_ALIGN); -} -#endif /* CPU <-> EDMAC endian convert */ @@ -1119,6 +1111,7 @@ static void sh_eth_ring_format(struct net_device *ndev) struct sh_eth_txdesc *txdesc = NULL; int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring; int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring; + int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; mdp->cur_rx = 0; mdp->cur_tx = 0; @@ -1131,21 +1124,21 @@ static void sh_eth_ring_format(struct net_device *ndev) for (i = 0; i < mdp->num_rx_ring; i++) { /* skb */ mdp->rx_skbuff[i] = NULL; - skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz); + skb = netdev_alloc_skb(ndev, skbuff_size); mdp->rx_skbuff[i] = skb; if (skb == NULL) break; - dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz, - DMA_FROM_DEVICE); sh_eth_set_receive_align(skb); /* RX descriptor */ rxdesc = &mdp->rx_ring[i]; + /* The size of the buffer is a multiple of 16 bytes. */ + rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); + dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length, + DMA_FROM_DEVICE); rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4)); rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP); - /* The size of the buffer is 16 byte boundary. */ - rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); /* Rx descriptor address set */ if (i == 0) { sh_eth_write(ndev, mdp->rx_desc_dma, RDLAR); @@ -1397,6 +1390,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) struct sk_buff *skb; u16 pkt_len = 0; u32 desc_status; + int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; rxdesc = &mdp->rx_ring[entry]; while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) { @@ -1448,7 +1442,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) if (mdp->cd->rpadir) skb_reserve(skb, NET_IP_ALIGN); dma_sync_single_for_cpu(&ndev->dev, rxdesc->addr, - mdp->rx_buf_sz, + ALIGN(mdp->rx_buf_sz, 16), DMA_FROM_DEVICE); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, ndev); @@ -1468,13 +1462,13 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); if (mdp->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz); + skb = netdev_alloc_skb(ndev, skbuff_size); mdp->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ - dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz, - DMA_FROM_DEVICE); sh_eth_set_receive_align(skb); + dma_map_single(&ndev->dev, skb->data, + rxdesc->buffer_length, DMA_FROM_DEVICE); skb_checksum_none_assert(skb); rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4)); diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index b37c427..9fa9332 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -162,9 +162,9 @@ enum { /* Driver's parameters */ #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) -#define SH4_SKB_RX_ALIGN 32 +#define SH_ETH_RX_ALIGN 32 #else -#define SH2_SH3_SKB_RX_ALIGN 2 +#define SH_ETH_RX_ALIGN 2 #endif /* Register's bits -- cgit v0.10.2 From 28603d13997e2ef47f18589cc9a44553aad49c86 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 27 Nov 2014 21:05:34 +0800 Subject: stmmac: platform: Move plat_dat checking earlier Original code only check/alloc plat_dat for the CONFIG_OF case, this patch check/alloc it earlier and unconditionally to avoid kernel build warnings: drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c:275 stmmac_pltfr_probe() warn: variable dereferenced before check 'plat_dat' V2: Fix coding style. Signed-off-by: Huacai Chen Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 5b0da39..58a1a0a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -265,6 +265,15 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) plat_dat = dev_get_platdata(&pdev->dev); + if (!plat_dat) + plat_dat = devm_kzalloc(&pdev->dev, + sizeof(struct plat_stmmacenet_data), + GFP_KERNEL); + if (!plat_dat) { + pr_err("%s: ERROR: no memory", __func__); + return -ENOMEM; + } + /* Set default value for multicast hash bins */ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; @@ -272,15 +281,6 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) plat_dat->unicast_filter_entries = 1; if (pdev->dev.of_node) { - if (!plat_dat) - plat_dat = devm_kzalloc(&pdev->dev, - sizeof(struct plat_stmmacenet_data), - GFP_KERNEL); - if (!plat_dat) { - pr_err("%s: ERROR: no memory", __func__); - return -ENOMEM; - } - ret = stmmac_probe_config_dt(pdev, plat_dat, &mac); if (ret) { pr_err("%s: main dt probe failed", __func__); -- cgit v0.10.2 From 7fa2955ff70ce4532f144d26b8a087095f9c9ffc Mon Sep 17 00:00:00 2001 From: Mitsuhiro Kimura Date: Fri, 28 Nov 2014 10:04:15 +0900 Subject: sh_eth: Fix sleeping function called from invalid context This resolves the following bug which can be reproduced by building the kernel with CONFIG_DEBUG_ATOMIC_SLEEP=y and reading network statistics while the network interface is down. e.g.: ifconfig eth0 down cat /sys/class/net/eth0/statistics/tx_errors ---- [ 1238.161349] BUG: sleeping function called from invalid context at drivers/base/power/runtime.c:952 [ 1238.188279] in_atomic(): 1, irqs_disabled(): 0, pid: 1388, name: cat [ 1238.207425] CPU: 0 PID: 1388 Comm: cat Not tainted 3.10.31-ltsi-00046-gefa0b46 #1087 [ 1238.230737] Backtrace: [ 1238.238123] [] (dump_backtrace+0x0/0x10c) from [] (show_stack+0x18/0x1c) [ 1238.263499] r6:000003b8 r5:c06160c0 r4:c0669e00 r3:00404000 [ 1238.280583] [] (show_stack+0x0/0x1c) from [] (dump_stack+0x20/0x28) [ 1238.304631] [] (dump_stack+0x0/0x28) from [] (__might_sleep+0xf8/0x118) [ 1238.329734] [] (__might_sleep+0x0/0x118) from [] (__pm_runtime_resume+0x38/0x90) [ 1238.357170] r7:d616f000 r6:c049c458 r5:00000004 r4:d6a17210 [ 1238.374251] [] (__pm_runtime_resume+0x0/0x90) from [] (sh_eth_get_stats+0x44/0x280) [ 1238.402468] r7:d616f000 r6:c049c458 r5:d5c21000 r4:d5c21000 [ 1238.419552] [] (sh_eth_get_stats+0x0/0x280) from [] (dev_get_stats+0x54/0x88) [ 1238.446204] r5:d5c21000 r4:d5ed7e08 [ 1238.456980] [] (dev_get_stats+0x0/0x88) from [] (netstat_show.isra.15+0x54/0x9c) [ 1238.484413] r6:d5c21000 r5:d5c21238 r4:00000028 r3:00000001 [ 1238.501495] [] (netstat_show.isra.15+0x0/0x9c) from [] (show_tx_errors+0x18/0x1c) [ 1238.529196] r7:d5f945d8 r6:d5f945c0 r5:c049716c r4:c0650e7c [ 1238.546279] [] (show_tx_errors+0x0/0x1c) from [] (dev_attr_show+0x24/0x50) [ 1238.572157] [] (dev_attr_show+0x0/0x50) from [] (sysfs_read_file+0xb0/0x140) [ 1238.598554] r5:c049716c r4:d5c21240 [ 1238.609326] [] (sysfs_read_file+0x0/0x140) from [] (vfs_read+0xb0/0x13c) [ 1238.634679] [] (vfs_read+0x0/0x13c) from [] (SyS_read+0x44/0x74) [ 1238.657944] r8:bef45bf0 r7:00000000 r6:d6ac0600 r5:00000000 r4:00000000 [ 1238.678172] [] (SyS_read+0x0/0x74) from [] (ret_fast_syscall+0x0/0x30) ---- Signed-off-by: Mitsuhiro Kimura Signed-off-by: Yoshihiro Kaneko Signed-off-by: Simon Horman Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index f9e30b8..b5db6b3 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2036,6 +2036,8 @@ static int sh_eth_open(struct net_device *ndev) if (ret) goto out_free_irq; + mdp->is_opened = 1; + return ret; out_free_irq: @@ -2125,6 +2127,36 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) return NETDEV_TX_OK; } +static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + + if (sh_eth_is_rz_fast_ether(mdp)) + return &ndev->stats; + + if (!mdp->is_opened) + return &ndev->stats; + + ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR); + sh_eth_write(ndev, 0, TROCR); /* (write clear) */ + ndev->stats.collisions += sh_eth_read(ndev, CDCR); + sh_eth_write(ndev, 0, CDCR); /* (write clear) */ + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR); + sh_eth_write(ndev, 0, LCCR); /* (write clear) */ + + if (sh_eth_is_gether(mdp)) { + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR); + sh_eth_write(ndev, 0, CERCR); /* (write clear) */ + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR); + sh_eth_write(ndev, 0, CEECR); /* (write clear) */ + } else { + ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR); + sh_eth_write(ndev, 0, CNDCR); /* (write clear) */ + } + + return &ndev->stats; +} + /* device close function */ static int sh_eth_close(struct net_device *ndev) { @@ -2139,6 +2171,7 @@ static int sh_eth_close(struct net_device *ndev) sh_eth_write(ndev, 0, EDTRR); sh_eth_write(ndev, 0, EDRRR); + sh_eth_get_stats(ndev); /* PHY Disconnect */ if (mdp->phydev) { phy_stop(mdp->phydev); @@ -2157,36 +2190,9 @@ static int sh_eth_close(struct net_device *ndev) pm_runtime_put_sync(&mdp->pdev->dev); - return 0; -} - -static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (sh_eth_is_rz_fast_ether(mdp)) - return &ndev->stats; - - pm_runtime_get_sync(&mdp->pdev->dev); - - ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR); - sh_eth_write(ndev, 0, TROCR); /* (write clear) */ - ndev->stats.collisions += sh_eth_read(ndev, CDCR); - sh_eth_write(ndev, 0, CDCR); /* (write clear) */ - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR); - sh_eth_write(ndev, 0, LCCR); /* (write clear) */ - if (sh_eth_is_gether(mdp)) { - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR); - sh_eth_write(ndev, 0, CERCR); /* (write clear) */ - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR); - sh_eth_write(ndev, 0, CEECR); /* (write clear) */ - } else { - ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR); - sh_eth_write(ndev, 0, CNDCR); /* (write clear) */ - } - pm_runtime_put_sync(&mdp->pdev->dev); + mdp->is_opened = 0; - return &ndev->stats; + return 0; } /* ioctl to device function */ diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 9fa9332..22301bf 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -522,6 +522,7 @@ struct sh_eth_private { unsigned no_ether_link:1; unsigned ether_link_active_low:1; + unsigned is_opened:1; }; static inline void sh_eth_soft_swap(char *src, int len) -- cgit v0.10.2 From 35d0565b95547ec12d025dc9b1394f22968d113d Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Mon, 1 Dec 2014 02:09:18 +0100 Subject: ACPI / video: update condition to check if device is in _DOD list Commit 0b8db271f159 ("ACPI / video: check _DOD list when creating backlight devices") checks if the video device is in the bind devices list to decide if we should create backlight device for it, that causes problem for one Dell Latitude E6410, where none of the video output devices are properly bound due to the way how we did the comparing between its _ADR and the _DOD's values. Solve this problem by comparing the lower 12 bits of both the device's _ADR and the _DOD's values instead of relying on bind result. Fixes: 0b8db271f159 ("ACPI / video: check _DOD list when creating backlight devices") Reported-and-tested-by: Brian Norris Signed-off-by: Aaron Lu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 807a88a..9d75ead 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1164,7 +1164,8 @@ static bool acpi_video_device_in_dod(struct acpi_video_device *device) return true; for (i = 0; i < video->attached_count; i++) { - if (video->attached_array[i].bind_info == device) + if ((video->attached_array[i].value.int_val & 0xfff) == + (device->device_id & 0xfff)) return true; } -- cgit v0.10.2 From 02ecc41abcea4ff9291d548f6f846b29b354ddd2 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sun, 30 Nov 2014 14:26:39 +0100 Subject: x86, microcode: Limit the microcode reloading to 64-bit for now First, there was this: https://bugzilla.kernel.org/show_bug.cgi?id=88001 The problem there was that microcode patches are not being reapplied after suspend-to-ram. It was important to reapply them, though, because of for example Haswell's TSX erratum which disabled TSX instructions with a microcode patch. A simple fix was fb86b97300d9 ("x86, microcode: Update BSPs microcode on resume") but, as it is often the case, simple fixes are too simple. This one causes 32-bit resume to fail: https://bugzilla.kernel.org/show_bug.cgi?id=88391 Properly fixing this would require more involved changes for which it is too late now, right before the merge window. Thus, limit this to 64-bit only temporarily. Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/1417353999-32236-1-git-send-email-bp@alien8.de Signed-off-by: Thomas Gleixner diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index 2ce9051..08fe6e8 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -465,6 +465,7 @@ static void mc_bp_resume(void) if (uci->valid && uci->mc) microcode_ops->apply_microcode(cpu); +#ifdef CONFIG_X86_64 else if (!uci->mc) /* * We might resume and not have applied late microcode but still @@ -473,6 +474,7 @@ static void mc_bp_resume(void) * applying patches early on the APs. */ load_ucode_ap(); +#endif } static struct syscore_ops mc_syscore_ops = { -- cgit v0.10.2 From 2cb4a18262fd0108cb8abd875710c59d0aa66f1d Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Fri, 28 Nov 2014 15:40:57 +0100 Subject: s390: fix machine check handling Commit eb7e7d76 "s390: Replace __get_cpu_var uses" broke machine check handling. We copy machine check information from per-cpu to a stack variable for local processing. Next we should zap the per-cpu variable, not the stack variable. Signed-off-by: Sebastian Ott Reviewed-by: Heiko Carstens Acked-by: Christoph Lameter Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index dd1c24c..3f51cf4 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -54,12 +54,8 @@ void s390_handle_mcck(void) */ local_irq_save(flags); local_mcck_disable(); - /* - * Ummm... Does this make sense at all? Copying the percpu struct - * and then zapping it one statement later? - */ - memcpy(&mcck, this_cpu_ptr(&cpu_mcck), sizeof(mcck)); - memset(&mcck, 0, sizeof(struct mcck_struct)); + mcck = *this_cpu_ptr(&cpu_mcck); + memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck)); clear_cpu_flag(CIF_MCCK_PENDING); local_mcck_enable(); local_irq_restore(flags); -- cgit v0.10.2 From 1689092da0815997eeded3f4c7b35be3df444df5 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 1 Dec 2014 16:57:52 +0100 Subject: ASoC: snd-soc-afeb9260: delete driver as board has just been removed During the removal of all AT91 !DT boards, we removed the AFEB9260. This driver is !DT and was only used by this board, so we delete it as well. Reported-by: Paul Bolle Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 5baabc8..466a821 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -17,4 +17,3 @@ snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o -obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c deleted file mode 100644 index 9579799..0000000 --- a/sound/soc/atmel/snd-soc-afeb9260.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * afeb9260.c -- SoC audio for AFEB9260 - * - * Copyright (C) 2009 Sergey Lapin - * - * 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. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../codecs/tlv320aic23.h" -#include "atmel-pcm.h" -#include "atmel_ssc_dai.h" - -#define CODEC_CLOCK 12000000 - -static int afeb9260_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int err; - - /* Set the codec system clock for DAC and ADC */ - err = - snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); - - if (err < 0) { - printk(KERN_ERR "can't set codec system clock\n"); - return err; - } - - return err; -} - -static struct snd_soc_ops afeb9260_ops = { - .hw_params = afeb9260_hw_params, -}; - -static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), - SND_SOC_DAPM_MIC("Mic Jack", NULL), -}; - -static const struct snd_soc_dapm_route afeb9260_audio_map[] = { - {"Headphone Jack", NULL, "LHPOUT"}, - {"Headphone Jack", NULL, "RHPOUT"}, - - {"LLINEIN", NULL, "Line In"}, - {"RLINEIN", NULL, "Line In"}, - - {"MICIN", NULL, "Mic Jack"}, -}; - - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link afeb9260_dai = { - .name = "TLV320AIC23", - .stream_name = "AIC23", - .cpu_dai_name = "atmel-ssc-dai.0", - .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "atmel_pcm-audio", - .codec_name = "tlv320aic23-codec.0-001a", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &afeb9260_ops, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_machine_afeb9260 = { - .name = "AFEB9260", - .owner = THIS_MODULE, - .dai_link = &afeb9260_dai, - .num_links = 1, - - .dapm_widgets = tlv320aic23_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), - .dapm_routes = afeb9260_audio_map, - .num_dapm_routes = ARRAY_SIZE(afeb9260_audio_map), -}; - -static struct platform_device *afeb9260_snd_device; - -static int __init afeb9260_soc_init(void) -{ - int err; - struct device *dev; - - if (!(machine_is_afeb9260())) - return -ENODEV; - - - afeb9260_snd_device = platform_device_alloc("soc-audio", -1); - if (!afeb9260_snd_device) { - printk(KERN_ERR "ASoC: Platform device allocation failed\n"); - return -ENOMEM; - } - - platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260); - err = platform_device_add(afeb9260_snd_device); - if (err) - goto err1; - - dev = &afeb9260_snd_device->dev; - - return 0; -err1: - platform_device_put(afeb9260_snd_device); - return err; -} - -static void __exit afeb9260_soc_exit(void) -{ - platform_device_unregister(afeb9260_snd_device); -} - -module_init(afeb9260_soc_init); -module_exit(afeb9260_soc_exit); - -MODULE_AUTHOR("Sergey Lapin "); -MODULE_DESCRIPTION("ALSA SoC for AFEB9260"); -MODULE_LICENSE("GPL"); - -- cgit v0.10.2 From aa9d4437893f7e015ce5b6d6c443a9ba92c8a2e7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 1 Dec 2014 22:52:45 +0000 Subject: KEYS: Fix the size of the key description passed to/from userspace When a key description argument is imported into the kernel from userspace, as happens in add_key(), request_key(), KEYCTL_JOIN_SESSION_KEYRING, KEYCTL_SEARCH, the description is copied into a buffer up to PAGE_SIZE in size. PAGE_SIZE, however, is a variable quantity, depending on the arch. Fix this at 4096 instead (ie. 4095 plus a NUL termination) and define a constant (KEY_MAX_DESC_SIZE) to this end. When reading the description back with KEYCTL_DESCRIBE, a PAGE_SIZE internal buffer is allocated into which the information and description will be rendered. This means that the description will get truncated if an extremely long description it has to be crammed into the buffer with the stringified information. There is no particular need to copy the description into the buffer, so just copy it directly to userspace in a separate operation. Reported-by: Christian Kastner Signed-off-by: David Howells Tested-by: Christian Kastner diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index eff88a5..4743d71 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -26,6 +26,8 @@ #include #include "internal.h" +#define KEY_MAX_DESC_SIZE 4096 + static int key_get_type_from_user(char *type, const char __user *_type, unsigned len) @@ -78,7 +80,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, description = NULL; if (_description) { - description = strndup_user(_description, PAGE_SIZE); + description = strndup_user(_description, KEY_MAX_DESC_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; @@ -177,7 +179,7 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, goto error; /* pull the description into kernel space */ - description = strndup_user(_description, PAGE_SIZE); + description = strndup_user(_description, KEY_MAX_DESC_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; @@ -287,7 +289,7 @@ long keyctl_join_session_keyring(const char __user *_name) /* fetch the name from userspace */ name = NULL; if (_name) { - name = strndup_user(_name, PAGE_SIZE); + name = strndup_user(_name, KEY_MAX_DESC_SIZE); if (IS_ERR(name)) { ret = PTR_ERR(name); goto error; @@ -562,8 +564,9 @@ long keyctl_describe_key(key_serial_t keyid, { struct key *key, *instkey; key_ref_t key_ref; - char *tmpbuf; + char *infobuf; long ret; + int desclen, infolen; key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW); if (IS_ERR(key_ref)) { @@ -586,38 +589,31 @@ long keyctl_describe_key(key_serial_t keyid, } okay: - /* calculate how much description we're going to return */ - ret = -ENOMEM; - tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!tmpbuf) - goto error2; - key = key_ref_to_ptr(key_ref); + desclen = strlen(key->description); - ret = snprintf(tmpbuf, PAGE_SIZE - 1, - "%s;%d;%d;%08x;%s", - key->type->name, - from_kuid_munged(current_user_ns(), key->uid), - from_kgid_munged(current_user_ns(), key->gid), - key->perm, - key->description ?: ""); - - /* include a NUL char at the end of the data */ - if (ret > PAGE_SIZE - 1) - ret = PAGE_SIZE - 1; - tmpbuf[ret] = 0; - ret++; + /* calculate how much information we're going to return */ + ret = -ENOMEM; + infobuf = kasprintf(GFP_KERNEL, + "%s;%d;%d;%08x;", + key->type->name, + from_kuid_munged(current_user_ns(), key->uid), + from_kgid_munged(current_user_ns(), key->gid), + key->perm); + if (!infobuf) + goto error2; + infolen = strlen(infobuf); + ret = infolen + desclen + 1; /* consider returning the data */ - if (buffer && buflen > 0) { - if (buflen > ret) - buflen = ret; - - if (copy_to_user(buffer, tmpbuf, buflen) != 0) + if (buffer && buflen >= ret) { + if (copy_to_user(buffer, infobuf, infolen) != 0 || + copy_to_user(buffer + infolen, key->description, + desclen + 1) != 0) ret = -EFAULT; } - kfree(tmpbuf); + kfree(infobuf); error2: key_ref_put(key_ref); error: @@ -649,7 +645,7 @@ long keyctl_keyring_search(key_serial_t ringid, if (ret < 0) goto error; - description = strndup_user(_description, PAGE_SIZE); + description = strndup_user(_description, KEY_MAX_DESC_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; -- cgit v0.10.2 From 054f6180d8b5602b431b5924976c956e760488b1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 1 Dec 2014 22:52:50 +0000 Subject: KEYS: Simplify KEYRING_SEARCH_{NO,DO}_STATE_CHECK flags Simplify KEYRING_SEARCH_{NO,DO}_STATE_CHECK flags to be two variations of the same flag. They are effectively mutually exclusive and one or the other should be provided, but not both. Keyring cycle detection and key possession determination are the only things that set NO_STATE_CHECK, except that neither flag really does anything there because neither purpose makes use of the keyring_search_iterator() function, but rather provides their own. For cycle detection we definitely want to check inside of expired keyrings, just so that we don't create a cycle we can't get rid of. Revoked keyrings are cleared at revocation time and can't then be reused, so shouldn't be a problem either way. For possession determination, we *might* want to validate each keyring before searching it: do you possess a key that's hidden behind an expired or just plain inaccessible keyring? Currently, the answer is yes. Note that you cannot, however, possess a key behind a revoked keyring because they are cleared on revocation. keyring_search() sets DO_STATE_CHECK, which is correct. request_key_and_link() currently doesn't specify whether to check the key state or not - but it should set DO_STATE_CHECK. key_get_instantiation_authkey() also currently doesn't specify whether to check the key state or not - but it probably should also set DO_STATE_CHECK. Signed-off-by: David Howells Tested-by: Chuck Lever diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 8177010..238aa17 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -628,6 +628,10 @@ static bool search_nested_keyrings(struct key *keyring, ctx->index_key.type->name, ctx->index_key.description); +#define STATE_CHECKS (KEYRING_SEARCH_NO_STATE_CHECK | KEYRING_SEARCH_DO_STATE_CHECK) + BUG_ON((ctx->flags & STATE_CHECKS) == 0 || + (ctx->flags & STATE_CHECKS) == STATE_CHECKS); + if (ctx->index_key.description) ctx->index_key.desc_len = strlen(ctx->index_key.description); @@ -637,7 +641,6 @@ static bool search_nested_keyrings(struct key *keyring, if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE || keyring_compare_object(keyring, &ctx->index_key)) { ctx->skipped_ret = 2; - ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK; switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) { case 1: goto found; @@ -649,8 +652,6 @@ static bool search_nested_keyrings(struct key *keyring, } ctx->skipped_ret = 0; - if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK) - ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK; /* Start processing a new keyring */ descend_to_keyring: diff --git a/security/keys/request_key.c b/security/keys/request_key.c index bb4337c..0bb23f9 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -516,6 +516,7 @@ struct key *request_key_and_link(struct key_type *type, .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_DO_STATE_CHECK, }; struct key *key; key_ref_t key_ref; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 6639e2c..5d672f7 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -249,6 +249,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_DO_STATE_CHECK, }; struct key *authkey; key_ref_t authkey_ref; -- cgit v0.10.2 From 0b0a84154eff56913e91df29de5c3a03a0029e38 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 1 Dec 2014 22:52:53 +0000 Subject: KEYS: request_key() should reget expired keys rather than give EKEYEXPIRED Since the keyring facility can be viewed as a cache (at least in some applications), the local expiration time on the key should probably be viewed as a 'needs updating after this time' property rather than an absolute 'anyone now wanting to use this object is out of luck' property. Since request_key() is the main interface for the usage of keys, this should update or replace an expired key rather than issuing EKEYEXPIRED if the local expiration has been reached (ie. it should refresh the cache). For absolute conditions where refreshing the cache probably doesn't help, the key can be negatively instantiated using KEYCTL_REJECT_KEY with EKEYEXPIRED given as the error to issue. This will still cause request_key() to return EKEYEXPIRED as that was explicitly set. In the future, if the key type has an update op available, we might want to upcall with the expired key and allow the upcall to update it. We would pass a different operation name (the first column in /etc/request-key.conf) to the request-key program. request_key() returning EKEYEXPIRED is causing an NFS problem which Chuck Lever describes thusly: After about 10 minutes, my NFSv4 functional tests fail because the ownership of the test files goes to "-2". Looking at /proc/keys shows that the id_resolv keys that map to my test user ID have expired. The ownership problem persists until the expired keys are purged from the keyring, and fresh keys are obtained. I bisected the problem to 3.13 commit b2a4df200d57 ("KEYS: Expand the capacity of a keyring"). This commit inadvertantly changes the API contract of the internal function keyring_search_aux(). The root cause appears to be that b2a4df200d57 made "no state check" the default behavior. "No state check" means the keyring search iterator function skips checking the key's expiry timeout, and returns expired keys. request_key_and_link() depends on getting an -EAGAIN result code to know when to perform an upcall to refresh an expired key. This patch can be tested directly by: keyctl request2 user debug:fred a @s keyctl timeout %user:debug:fred 3 sleep 4 keyctl request2 user debug:fred a @s Without the patch, the last command gives error EKEYEXPIRED, but with the command it gives a new key. Reported-by: Carl Hetherington Reported-by: Chuck Lever Signed-off-by: David Howells Tested-by: Chuck Lever diff --git a/security/keys/internal.h b/security/keys/internal.h index b8960c4..200e378 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -117,6 +117,7 @@ struct keyring_search_context { #define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */ #define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */ #define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */ +#define KEYRING_SEARCH_SKIP_EXPIRED 0x0020 /* Ignore expired keys (intention to replace) */ int (*iterator)(const void *object, void *iterator_data); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 238aa17..e72548b 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -546,7 +546,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) } if (key->expiry && ctx->now.tv_sec >= key->expiry) { - ctx->result = ERR_PTR(-EKEYEXPIRED); + if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED)) + ctx->result = ERR_PTR(-EKEYEXPIRED); kleave(" = %d [expire]", ctx->skipped_ret); goto skipped; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 0bb23f9..0c7aea4 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -516,7 +516,8 @@ struct key *request_key_and_link(struct key_type *type, .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .flags = KEYRING_SEARCH_DO_STATE_CHECK, + .flags = (KEYRING_SEARCH_DO_STATE_CHECK | + KEYRING_SEARCH_SKIP_EXPIRED), }; struct key *key; key_ref_t key_ref; -- cgit v0.10.2 From 5106787a9e08dc2901d6b2513ed8f377671befa8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 27 Nov 2014 09:54:09 +0100 Subject: PCI: tegra: Use physical range for I/O mapping Commit 0b0b0893d49b ("of/pci: Fix the conversion of IO ranges into IO resources") changed how I/O resources are parsed from DT. Rather than containing the physical address of the I/O region, the addresses will now be in I/O address space. On Tegra the union of all ranges is used to expose a top-level memory- mapped resource for the PCI host bridge. This helps to make /proc/iomem more readable. Combining both of the above, the union would now include the I/O space region. This causes a regression on Tegra20, where the physical base address of the PCIe controller (and therefore of the union) is located at physical address 0x80000000. Since I/O space starts at 0, the union will now include all of system RAM which starts at 0x00000000. This commit fixes this by keeping two copies of the I/O range: one that represents the range in the CPU's physical address space, the other for the range in the I/O address space. This allows the translation setup within the driver to reuse the physical addresses. The code registering the I/O region with the PCI core uses both ranges to establish the mapping. Fixes: 0b0b0893d49b ("of/pci: Fix the conversion of IO ranges into IO resources") Reported-by: Marc Zyngier Tested-by: Marc Zyngier Suggested-by: Arnd Bergmann Signed-off-by: Thierry Reding Signed-off-by: Bjorn Helgaas Reviewed-by: Arnd Bergmann diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 3d43874..19bb19c 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -276,6 +276,7 @@ struct tegra_pcie { struct resource all; struct resource io; + struct resource pio; struct resource mem; struct resource prefetch; struct resource busn; @@ -658,7 +659,6 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) { struct tegra_pcie *pcie = sys_to_pcie(sys); int err; - phys_addr_t io_start; err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem); if (err < 0) @@ -668,14 +668,12 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) if (err) return err; - io_start = pci_pio_to_address(pcie->io.start); - pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource_offset(&sys->resources, &pcie->prefetch, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); - pci_ioremap_io(nr * SZ_64K, io_start); + pci_ioremap_io(pcie->pio.start, pcie->io.start); return 1; } @@ -786,7 +784,6 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) { u32 fpci_bar, size, axi_address; - phys_addr_t io_start = pci_pio_to_address(pcie->io.start); /* Bar 0: type 1 extended configuration space */ fpci_bar = 0xfe100000; @@ -799,7 +796,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) /* Bar 1: downstream IO bar */ fpci_bar = 0xfdfc0000; size = resource_size(&pcie->io); - axi_address = io_start; + axi_address = pcie->io.start; afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); @@ -1690,8 +1687,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) switch (res.flags & IORESOURCE_TYPE_BITS) { case IORESOURCE_IO: - memcpy(&pcie->io, &res, sizeof(res)); - pcie->io.name = np->full_name; + memcpy(&pcie->pio, &res, sizeof(res)); + pcie->pio.name = np->full_name; + + /* + * The Tegra PCIe host bridge uses this to program the + * mapping of the I/O space to the physical address, + * so we override the .start and .end fields here that + * of_pci_range_to_resource() converted to I/O space. + * We also set the IORESOURCE_MEM type to clarify that + * the resource is in the physical memory space. + */ + pcie->io.start = range.cpu_addr; + pcie->io.end = range.cpu_addr + range.size - 1; + pcie->io.flags = IORESOURCE_MEM; + pcie->io.name = "I/O"; + + memcpy(&res, &pcie->io, sizeof(res)); break; case IORESOURCE_MEM: -- cgit v0.10.2 From 32f3869184d498850d36b7e6aa3b9f5260ea648a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 1 Dec 2014 16:22:23 -0800 Subject: jbd2: fix regression where we fail to initialize checksum seed when loading When we're enabling journal features, we cannot use the predicate jbd2_journal_has_csum_v2or3() because we haven't yet set the sb feature flag fields! Moreover, we just finished loading the shash driver, so the test is unnecessary; calculate the seed always. Without this patch, we fail to initialize the checksum seed the first time we turn on journal_checksum, which means that all journal blocks written during that first mount are corrupt. Transactions written after the second mount will be fine, since the feature flag will be set in the journal superblock. xfstests generic/{034,321,322} are the regression tests. (This is important for 3.18.) Signed-off-by: Darrick J. Wong Reported-by: Eric Whitney Signed-off-by: Theodore Ts'o diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index e4dc747..1df94fa 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1853,13 +1853,12 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, journal->j_chksum_driver = NULL; return 0; } - } - /* Precompute checksum seed for all metadata */ - if (jbd2_journal_has_csum_v2or3(journal)) + /* Precompute checksum seed for all metadata */ journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid, sizeof(sb->s_uuid)); + } } /* If enabling v1 checksums, downgrade superblock */ -- cgit v0.10.2 From 19a10828814aa3ba483301b416b27f94330e0c80 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 1 Dec 2014 11:44:27 +1000 Subject: drm/nouveau/fifo/g84-: ack non-stall interrupt before handling it Closes a very unlikely race that can occur if another NonStallInterrupt method passes between checking fences and acking the previous interrupt. With this change, the interrupt will re-fire under such conditions. Tested-by: Tobias Klausmann Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c index 5ae6a43..1931057 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c @@ -551,8 +551,8 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) } if (status & 0x40000000) { - nouveau_fifo_uevent(&priv->base); nv_wr32(priv, 0x002100, 0x40000000); + nouveau_fifo_uevent(&priv->base); status &= ~0x40000000; } } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c index 1fe1f8f..074d434 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c @@ -740,6 +740,8 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) u32 inte = nv_rd32(priv, 0x002628); u32 unkn; + nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); + for (unkn = 0; unkn < 8; unkn++) { u32 ints = (intr >> (unkn * 0x04)) & inte; if (ints & 0x1) { @@ -751,8 +753,6 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) nv_mask(priv, 0x002628, ints, 0); } } - - nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); } static void diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index d2f0fd3..f8734eb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -952,8 +952,8 @@ nve0_fifo_intr(struct nouveau_subdev *subdev) } if (stat & 0x80000000) { - nve0_fifo_intr_engine(priv); nv_wr32(priv, 0x002100, 0x80000000); + nve0_fifo_intr_engine(priv); stat &= ~0x80000000; } -- cgit v0.10.2 From 0ec5f02f0e2c6fe88ba5817790e11fe33ee298a7 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 1 Dec 2014 19:11:06 +1000 Subject: drm/nouveau: prevent stale fence->channel pointers, and protect with rcu Tested-by: Alexandre Courbot Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 515cd9a..f32a434 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -52,20 +52,24 @@ nouveau_fctx(struct nouveau_fence *fence) return container_of(fence->base.lock, struct nouveau_fence_chan, lock); } -static void +static int nouveau_fence_signal(struct nouveau_fence *fence) { + int drop = 0; + fence_signal_locked(&fence->base); list_del(&fence->head); + rcu_assign_pointer(fence->channel, NULL); if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { struct nouveau_fence_chan *fctx = nouveau_fctx(fence); if (!--fctx->notify_ref) - nvif_notify_put(&fctx->notify); + drop = 1; } fence_put(&fence->base); + return drop; } static struct nouveau_fence * @@ -88,16 +92,23 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence; - nvif_notify_fini(&fctx->notify); - spin_lock_irq(&fctx->lock); while (!list_empty(&fctx->pending)) { fence = list_entry(fctx->pending.next, typeof(*fence), head); - nouveau_fence_signal(fence); - fence->channel = NULL; + if (nouveau_fence_signal(fence)) + nvif_notify_put(&fctx->notify); } spin_unlock_irq(&fctx->lock); + + nvif_notify_fini(&fctx->notify); + fctx->dead = 1; + + /* + * Ensure that all accesses to fence->channel complete before freeing + * the channel. + */ + synchronize_rcu(); } static void @@ -112,21 +123,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) kref_put(&fctx->fence_ref, nouveau_fence_context_put); } -static void +static int nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence; - + int drop = 0; u32 seq = fctx->read(chan); while (!list_empty(&fctx->pending)) { fence = list_entry(fctx->pending.next, typeof(*fence), head); if ((int)(seq - fence->base.seqno) < 0) - return; + break; - nouveau_fence_signal(fence); + drop |= nouveau_fence_signal(fence); } + + return drop; } static int @@ -135,18 +148,21 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) struct nouveau_fence_chan *fctx = container_of(notify, typeof(*fctx), notify); unsigned long flags; + int ret = NVIF_NOTIFY_KEEP; spin_lock_irqsave(&fctx->lock, flags); if (!list_empty(&fctx->pending)) { struct nouveau_fence *fence; + struct nouveau_channel *chan; fence = list_entry(fctx->pending.next, typeof(*fence), head); - nouveau_fence_update(fence->channel, fctx); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (nouveau_fence_update(fence->channel, fctx)) + ret = NVIF_NOTIFY_DROP; } spin_unlock_irqrestore(&fctx->lock, flags); - /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */ - return NVIF_NOTIFY_KEEP; + return ret; } void @@ -262,7 +278,10 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) if (!ret) { fence_get(&fence->base); spin_lock_irq(&fctx->lock); - nouveau_fence_update(chan, fctx); + + if (nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); + list_add_tail(&fence->head, &fctx->pending); spin_unlock_irq(&fctx->lock); } @@ -276,13 +295,16 @@ nouveau_fence_done(struct nouveau_fence *fence) if (fence->base.ops == &nouveau_fence_ops_legacy || fence->base.ops == &nouveau_fence_ops_uevent) { struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan; unsigned long flags; if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) return true; spin_lock_irqsave(&fctx->lock, flags); - nouveau_fence_update(fence->channel, fctx); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (chan && nouveau_fence_update(chan, fctx)) + nvif_notify_put(&fctx->notify); spin_unlock_irqrestore(&fctx->lock, flags); } return fence_is_signaled(&fence->base); @@ -387,12 +409,18 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e if (fence && (!exclusive || !fobj || !fobj->shared_count)) { struct nouveau_channel *prev = NULL; + bool must_wait = true; f = nouveau_local_fence(fence, chan->drm); - if (f) - prev = f->channel; + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } - if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + if (must_wait) ret = fence_wait(fence, intr); return ret; @@ -403,19 +431,22 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e for (i = 0; i < fobj->shared_count && !ret; ++i) { struct nouveau_channel *prev = NULL; + bool must_wait = true; fence = rcu_dereference_protected(fobj->shared[i], reservation_object_held(resv)); f = nouveau_local_fence(fence, chan->drm); - if (f) - prev = f->channel; + if (f) { + rcu_read_lock(); + prev = rcu_dereference(f->channel); + if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + must_wait = false; + rcu_read_unlock(); + } - if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + if (must_wait) ret = fence_wait(fence, intr); - - if (ret) - break; } return ret; @@ -463,7 +494,7 @@ static const char *nouveau_fence_get_timeline_name(struct fence *f) struct nouveau_fence *fence = from_fence(f); struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - return fence->channel ? fctx->name : "dead channel"; + return !fctx->dead ? fctx->name : "dead channel"; } /* @@ -476,9 +507,16 @@ static bool nouveau_fence_is_signaled(struct fence *f) { struct nouveau_fence *fence = from_fence(f); struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - struct nouveau_channel *chan = fence->channel; + struct nouveau_channel *chan; + bool ret = false; + + rcu_read_lock(); + chan = rcu_dereference(fence->channel); + if (chan) + ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0; + rcu_read_unlock(); - return (int)(fctx->read(chan) - fence->base.seqno) >= 0; + return ret; } static bool nouveau_fence_no_signaling(struct fence *f) diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index 943b0b1..96e461c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -14,7 +14,7 @@ struct nouveau_fence { bool sysmem; - struct nouveau_channel *channel; + struct nouveau_channel __rcu *channel; unsigned long timeout; }; @@ -47,7 +47,7 @@ struct nouveau_fence_chan { char name[32]; struct nvif_notify notify; - int notify_ref; + int notify_ref, dead; }; struct nouveau_fence_priv { -- cgit v0.10.2 From 226d63a1addea8cbe8fc671978e62dc84927b046 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Sun, 30 Nov 2014 12:56:18 -0500 Subject: drm/nouveau/gf116: remove copy1 engine Indications are that no GF116's actually have a copy engine there, but actually have the decompression engine. This engine can be made to do copies, but that should be done separately. Unclear why this didn't turn up on all GF116's, but perhaps the non-mobile ones came with enough VRAM to not trigger ttm migrations in test scenarios. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=85465 Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=59168 Cc: stable@vger.kernel.org Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index cd05677..72a40f9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -218,7 +218,6 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; - device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass; device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass; break; -- cgit v0.10.2 From 8b62c8c6df08ca567c78afa51aa7bbc554cede06 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 2 Dec 2014 16:27:25 +1000 Subject: nouveau: move the hotplug ignore to correct place. Introduced in b440bde74f, however it was added to the wrong function in nouveau. https://bugzilla.kernel.org/show_bug.cgi?id=86011 Cc: Bjorn Helgaas CC: stable@vger.kernel.org # v3.15+ Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 5723807..62b97c4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -629,7 +629,6 @@ int nouveau_pmops_suspend(struct device *dev) pci_save_state(pdev); pci_disable_device(pdev); - pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3hot); return 0; } @@ -933,6 +932,7 @@ static int nouveau_pmops_runtime_suspend(struct device *dev) ret = nouveau_do_suspend(drm_dev, true); pci_save_state(pdev); pci_disable_device(pdev); + pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3cold); drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; return ret; -- cgit v0.10.2 From b68362278af94e1171f5be9d4e44988601fb0439 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 24 Nov 2014 17:02:45 +0100 Subject: drm/i915: More cautious with pch fifo underruns Apparently PCH fifo underruns are tricky, we have plenty reports that we see the occasional underrun (especially at boot-up). So for a change let's see what happens when we don't re-enable pch fifo underrun reporting when the pipe is disabled. This means that the kernel can't catch pch fifo underruns when they happen (except when all pipes are on on the pch). But we'll still catch underruns when disabling the pipe again. So not a terrible reduction in test coverage. Since the DRM_ERROR is new and hence a regression plan B would be to revert it back to a debug output. Which would be a lot worse than this hack for underrun test coverage in the wild. See the referenced discussions for more. References: http://mid.gmane.org/CA+gsUGRfGe3t4NcjdeA=qXysrhLY3r4CEu7z4bjTwxi1uOfy+g@mail.gmail.com Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=85898 References: https://bugs.freedesktop.org/show_bug.cgi?id=85898 References: https://bugs.freedesktop.org/show_bug.cgi?id=86233 References: https://bugs.freedesktop.org/show_bug.cgi?id=86478 Signed-off-by: Daniel Vetter Tested-by: lu hua Reviewed-by: Paulo Zanoni Cc: stable@vger.kernel.org Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8bcdb98..9cb5c95 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4325,7 +4325,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ironlake_fdi_disable(crtc); ironlake_disable_pch_transcoder(dev_priv, pipe); - intel_set_pch_fifo_underrun_reporting(dev, pipe, true); if (HAS_PCH_CPT(dev)) { /* disable TRANS_DP_CTL */ @@ -4389,7 +4388,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) { lpt_disable_pch_transcoder(dev_priv); - intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); intel_ddi_fdi_disable(crtc); } -- cgit v0.10.2 From de82bf6c055e1f426845e7e140c7d48dd70b6d16 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 2 Dec 2014 10:04:35 +0100 Subject: ASoC: Kconfig: remove not used SND_AT91_SOC_AFEB9260 option Now that the driver snd-soc-afeb9260.c is deleted, remove its Kconfig option. Reported-by: Paul Bolle Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 27e3fc4..fb38783 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -52,12 +52,3 @@ config SND_AT91_SOC_SAM9X5_WM8731 help Say Y if you want to add support for audio SoC on an at91sam9x5 based board that is using WM8731 codec. - -config SND_AT91_SOC_AFEB9260 - tristate "SoC Audio support for AFEB9260 board" - depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC - select SND_ATMEL_SOC_PDC - select SND_ATMEL_SOC_SSC - select SND_SOC_TLV320AIC23_I2C - help - Say Y here to support sound on AFEB9260 board. -- cgit v0.10.2 From b0616c5306b342ceca07044dbc4f917d95c4f825 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 1 Dec 2014 17:56:54 +0100 Subject: drm/i915: Unlock panel even when LVDS is disabled Otherwise we'll have backtraces in assert_panel_unlocked because the BIOS locks the register. In the reporter's case this regression was introduced in commit c31407a3672aaebb4acddf90944a114fa5c8af7b Author: Chris Wilson Date: Thu Oct 18 21:07:01 2012 +0100 drm/i915: Add no-lvds quirk for Supermicro X7SPA-H Reported-by: Alexey Orishko Cc: Alexey Orishko Cc: stable@vger.kernel.org Cc: Chris Wilson Cc: Francois Tigeot Signed-off-by: Daniel Vetter Tested-by: Alexey Orishko Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index a6bd142..c0bbf21 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -899,6 +899,17 @@ void intel_lvds_init(struct drm_device *dev) int pipe; u8 pin; + /* + * Unlock registers and just leave them unlocked. Do this before + * checking quirk lists to avoid bogus WARNINGs. + */ + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_PP_CONTROL, + I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); + } else { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); + } if (!intel_lvds_supported(dev)) return; @@ -1097,17 +1108,6 @@ out: lvds_encoder->a3_power = I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK; - /* - * Unlock registers and just - * leave them unlocked - */ - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_PP_CONTROL, - I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); - } else { - I915_WRITE(PP_CONTROL, - I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); - } lvds_connector->lid_notifier.notifier_call = intel_lid_notify; if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) { DRM_DEBUG_KMS("lid notifier registration failed\n"); -- cgit v0.10.2 From 594416a72032684792bb22510c538098db10b750 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 25 Nov 2014 17:40:25 -0800 Subject: block: fix regression where bio_integrity_process uses wrong bio_vec iterator bio integrity handling is broken on a system with LVM layered atop a DIF/DIX SCSI drive because device mapper clones the bio, modifies the clone, and sends the clone to the lower layers for processing. However, the clone bio has bi_vcnt == 0, which means that when the sd driver calls bio_integrity_process to attach DIX data, the for_each_segment_all() call (which uses bi_vcnt) returns immediately and random garbage is sent to the disk on a disk write. The disk of course returns an error. Therefore, teach bio_integrity_process() to use bio_for_each_segment() to iterate the bio_vecs, since the per-bio iterator tracks which bio_vecs are associated with that particular bio. The integrity handling code is effectively part of the "driver" (it's not the bio owner), so it must use the correct iterator function. v2: Fix a compiler warning about abandoned local variables. This patch supersedes "block: bio_integrity_process uses wrong bio_vec iterator". Patch applies against 3.18-rc6. Signed-off-by: Darrick J. Wong Acked-by: Martin K. Petersen Signed-off-by: Jens Axboe diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 0984232..5cbd5d9 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -216,9 +216,10 @@ static int bio_integrity_process(struct bio *bio, { struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); struct blk_integrity_iter iter; - struct bio_vec *bv; + struct bvec_iter bviter; + struct bio_vec bv; struct bio_integrity_payload *bip = bio_integrity(bio); - unsigned int i, ret = 0; + unsigned int ret = 0; void *prot_buf = page_address(bip->bip_vec->bv_page) + bip->bip_vec->bv_offset; @@ -227,11 +228,11 @@ static int bio_integrity_process(struct bio *bio, iter.seed = bip_get_seed(bip); iter.prot_buf = prot_buf; - bio_for_each_segment_all(bv, bio, i) { - void *kaddr = kmap_atomic(bv->bv_page); + bio_for_each_segment(bv, bio, bviter) { + void *kaddr = kmap_atomic(bv.bv_page); - iter.data_buf = kaddr + bv->bv_offset; - iter.data_size = bv->bv_len; + iter.data_buf = kaddr + bv.bv_offset; + iter.data_size = bv.bv_len; ret = proc_fn(&iter); if (ret) { -- cgit v0.10.2 From 249cd0a187ed4ef1d0af7f74362cc2791ec5581b Mon Sep 17 00:00:00 2001 From: Devin Ryles Date: Fri, 7 Nov 2014 17:59:05 -0500 Subject: AHCI: Add DeviceIDs for Sunrise Point-LP SATA controller This patch adds DeviceIDs for Sunrise Point-LP. Signed-off-by: Devin Ryles Signed-off-by: Tejun Heo Cc: stable@vger.kernel.org diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e45f837..0ef0e39 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -321,6 +321,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */ { PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */ { PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */ + { PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */ + { PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */ -- cgit v0.10.2 From aad0b624129709c94c2e19e583b6053520353fa8 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 13:39:05 -0800 Subject: sata_fsl: fix error handling of irq_of_parse_and_map irq_of_parse_and_map() returns 0 on error (the result is unsigned int), so testing for negative result never works. Signed-off-by: Dmitry Torokhov Signed-off-by: Tejun Heo Cc: stable@vger.kernel.org diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 07bc7e4..6507159 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1488,7 +1488,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) host_priv->csr_base = csr_base; irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); - if (irq < 0) { + if (!irq) { dev_err(&ofdev->dev, "invalid irq from platform\n"); goto error_exit_with_cleanup; } -- cgit v0.10.2 From 86b276385c6a986872e4cd144f5940b156053c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 27 Nov 2014 13:12:58 +0100 Subject: drm/radeon: sync all BOs involved in a CS v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not just the userspace relocs, otherwise we won't wait for a swapped out page tables to be swapped in again. v2: rebased on Alex current drm-fixes-3.18 Signed-off-by: Christian König Cc: stable@vger.kernel.org Signed-off-by: Alex Deucher diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index a3e7aed..6f377de 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -251,22 +251,19 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority static int radeon_cs_sync_rings(struct radeon_cs_parser *p) { - int i, r = 0; + struct radeon_cs_reloc *reloc; + int r; - for (i = 0; i < p->nrelocs; i++) { + list_for_each_entry(reloc, &p->validated, tv.head) { struct reservation_object *resv; - if (!p->relocs[i].robj) - continue; - - resv = p->relocs[i].robj->tbo.resv; + resv = reloc->robj->tbo.resv; r = radeon_semaphore_sync_resv(p->rdev, p->ib.semaphore, resv, - p->relocs[i].tv.shared); - + reloc->tv.shared); if (r) - break; + return r; } - return r; + return 0; } /* XXX: note that this is called from the legacy UMS CS ioctl as well */ -- cgit v0.10.2 From a08b588e4199e4200d26027ffcdf3ab2fa906412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 27 Nov 2014 18:00:54 +0900 Subject: drm/radeon: Ignore RADEON_GEM_GTT_WC on 32-bit x86 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=84627 Signed-off-by: Michel Dänzer Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 99a960a..4c0d786 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -213,6 +213,13 @@ int radeon_bo_create(struct radeon_device *rdev, if (!(rdev->flags & RADEON_IS_PCIE)) bo->flags &= ~(RADEON_GEM_GTT_WC | RADEON_GEM_GTT_UC); +#ifdef CONFIG_X86_32 + /* XXX: Write-combined CPU mappings of GTT seem broken on 32-bit + * See https://bugs.freedesktop.org/show_bug.cgi?id=84627 + */ + bo->flags &= ~RADEON_GEM_GTT_WC; +#endif + radeon_ttm_placement_from_domain(bo, domain); /* Kernel allocation are uninterruptible */ down_read(&rdev->pm.mclk_lock); -- cgit v0.10.2 From f5475cc43c899e33098d4db44b7c5e710f16589d Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Thu, 27 Nov 2014 16:57:21 +0100 Subject: drm/radeon: kernel panic in drm_calc_vbltimestamp_from_scanoutpos with 3.18.0-rc6 I was unable too boot 3.18.0-rc6 because of the following kernel panic in drm_calc_vbltimestamp_from_scanoutpos(): [drm] Initialized drm 1.1.0 20060810 [drm] radeon kernel modesetting enabled. [drm] initializing kernel modesetting (RV100 0x1002:0x515E 0x15D9:0x8080). [drm] register mmio base: 0xC8400000 [drm] register mmio size: 65536 radeon 0000:0b:01.0: VRAM: 128M 0x00000000D0000000 - 0x00000000D7FFFFFF (16M used) radeon 0000:0b:01.0: GTT: 512M 0x00000000B0000000 - 0x00000000CFFFFFFF [drm] Detected VRAM RAM=128M, BAR=128M [drm] RAM width 16bits DDR [TTM] Zone kernel: Available graphics memory: 3829346 kiB [TTM] Zone dma32: Available graphics memory: 2097152 kiB [TTM] Initializing pool allocator [TTM] Initializing DMA pool allocator [drm] radeon: 16M of VRAM memory ready [drm] radeon: 512M of GTT memory ready. [drm] GART: num cpu pages 131072, num gpu pages 131072 [drm] PCI GART of 512M enabled (table at 0x0000000037880000). radeon 0000:0b:01.0: WB disabled radeon 0000:0b:01.0: fence driver on ring 0 use gpu addr 0x00000000b0000000 and cpu addr 0xffff8800bbbfa000 [drm] Supports vblank timestamp caching Rev 2 (21.10.2013). [drm] Driver supports precise vblank timestamp query. [drm] radeon: irq initialized. [drm] Loading R100 Microcode radeon 0000:0b:01.0: Direct firmware load for radeon/R100_cp.bin failed with error -2 radeon_cp: Failed to load firmware "radeon/R100_cp.bin" [drm:r100_cp_init] *ERROR* Failed to load firmware! radeon 0000:0b:01.0: failed initializing CP (-2). radeon 0000:0b:01.0: Disabling GPU acceleration [drm] radeon: cp finalized BUG: unable to handle kernel NULL pointer dereference at 000000000000025c IP: [] drm_calc_vbltimestamp_from_scanoutpos+0x4b/0x320 PGD 0 Oops: 0000 [#1] SMP Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6-4-default #2649 Hardware name: Supermicro X7DB8/X7DB8, BIOS 6.00 07/26/2006 task: ffff880234da2010 ti: ffff880234da4000 task.ti: ffff880234da4000 RIP: 0010:[] [] drm_calc_vbltimestamp_from_scanoutpos+0x4b/0x320 RSP: 0000:ffff880234da7918 EFLAGS: 00010086 RAX: ffffffff81557890 RBX: 0000000000000000 RCX: ffff880234da7a48 RDX: ffff880234da79f4 RSI: 0000000000000000 RDI: ffff880232e15000 RBP: ffff880234da79b8 R08: 0000000000000000 R09: 0000000000000000 R10: 000000000000000a R11: 0000000000000001 R12: ffff880232dda1c0 R13: ffff880232e1518c R14: 0000000000000292 R15: ffff880232e15000 FS: 0000000000000000(0000) GS:ffff88023fc40000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 000000000000025c CR3: 0000000002014000 CR4: 00000000000007e0 Stack: ffff880234da79d8 0000000000000286 ffff880232dcbc00 0000000000002480 ffff880234da7958 0000000000000296 ffff880234da7998 ffffffff8151b51d ffff880234da7a48 0000000032dcbeb0 ffff880232dcbc00 ffff880232dcbc58 Call Trace: [] ? drm_vma_offset_remove+0x1d/0x110 [] radeon_get_vblank_timestamp_kms+0x38/0x60 [] ? ttm_bo_release_list+0xba/0x180 [] drm_get_last_vbltimestamp+0x41/0x70 [] vblank_disable_and_save+0x73/0x1d0 [] ? try_to_del_timer_sync+0x4f/0x70 [] drm_vblank_cleanup+0x65/0xa0 [] radeon_irq_kms_fini+0x1a/0x70 [] r100_init+0x26e/0x410 [] radeon_device_init+0x7ae/0xb50 [] radeon_driver_load_kms+0x8f/0x210 [] drm_dev_register+0xb5/0x110 [] drm_get_pci_dev+0x8f/0x200 [] radeon_pci_probe+0xad/0xe0 [] local_pci_probe+0x45/0xa0 [] pci_device_probe+0xd1/0x130 [] driver_probe_device+0x12d/0x3e0 [] __driver_attach+0x9b/0xa0 [] ? __device_attach+0x40/0x40 [] bus_for_each_dev+0x63/0xa0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x180/0x240 [] driver_register+0x64/0xf0 [] __pci_register_driver+0x4c/0x50 [] drm_pci_init+0xf5/0x120 [] ? ttm_init+0x6a/0x6a [] radeon_init+0x97/0xb5 [] do_one_initcall+0xbc/0x1f0 [] ? __wake_up+0x48/0x60 [] kernel_init_freeable+0x18a/0x215 [] ? initcall_blacklist+0xc0/0xc0 [] ? rest_init+0x80/0x80 [] kernel_init+0xe/0xf0 [] ret_from_fork+0x7c/0xb0 [] ? rest_init+0x80/0x80 Code: 45 ac 0f 88 a8 01 00 00 3b b7 d0 01 00 00 49 89 ff 0f 83 99 01 00 00 48 8b 47 20 48 8b 80 88 00 00 00 48 85 c0 0f 84 cd 01 00 00 <41> 8b b1 5c 02 00 00 41 8b 89 58 02 00 00 89 75 98 41 8b b1 60 RIP [] drm_calc_vbltimestamp_from_scanoutpos+0x4b/0x320 RSP CR2: 000000000000025c ---[ end trace ad2c0aadf48e2032 ]--- Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000009 It has helped me to add a NULL pointer check that was suggested at http://lists.freedesktop.org/archives/dri-devel/2014-October/070663.html I am not familiar with the code. But the change looks sane and we need something fast at this stage of 3.18 development. Suggested-by: Helge Deller Signed-off-by: Petr Mladek Tested-by: Petr Mladek Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 8309b11..0358676 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -795,6 +795,8 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, /* Get associated drm_crtc: */ drmcrtc = &rdev->mode_info.crtcs[crtc]->base; + if (!drmcrtc) + return -EINVAL; /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, -- cgit v0.10.2 From bc127bda37db2792fa7f02a8d258731ecf3e4b8b Mon Sep 17 00:00:00 2001 From: Rafael Aquini Date: Tue, 2 Dec 2014 15:59:22 -0800 Subject: mm: do not overwrite reserved pages counter at show_mem() Minor fixlet to perform the reserved pages counter aggregation for each node, at show_mem() Signed-off-by: Rafael Aquini Acked-by: Rik van Riel Acked-by: Johannes Weiner Acked-by: Yasuaki Ishimatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/lib/show_mem.c b/lib/show_mem.c index 0922579..5e25627 100644 --- a/lib/show_mem.c +++ b/lib/show_mem.c @@ -28,7 +28,7 @@ void show_mem(unsigned int filter) continue; total += zone->present_pages; - reserved = zone->present_pages - zone->managed_pages; + reserved += zone->present_pages - zone->managed_pages; if (is_highmem_idx(zoneid)) highmem += zone->present_pages; -- cgit v0.10.2 From fb993fa1a2f669215fa03a09eed7848f2663e336 Mon Sep 17 00:00:00 2001 From: Weijie Yang Date: Tue, 2 Dec 2014 15:59:25 -0800 Subject: mm: frontswap: invalidate expired data on a dup-store failure If a frontswap dup-store failed, it should invalidate the expired page in the backend, or it could trigger some data corruption issue. Such as: 1. use zswap as the frontswap backend with writeback feature 2. store a swap page(version_1) to entry A, success 3. dup-store a newer page(version_2) to the same entry A, fail 4. use __swap_writepage() write version_2 page to swapfile, success 5. zswap do shrink, writeback version_1 page to swapfile 6. version_2 page is overwrited by version_1, data corrupt. This patch fixes this issue by invalidating expired data immediately when meet a dup-store failure. Signed-off-by: Weijie Yang Cc: Konrad Rzeszutek Wilk Cc: Seth Jennings Cc: Dan Streetman Cc: Minchan Kim Cc: Bob Liu Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/frontswap.c b/mm/frontswap.c index c30eec5..f2a3571 100644 --- a/mm/frontswap.c +++ b/mm/frontswap.c @@ -244,8 +244,10 @@ int __frontswap_store(struct page *page) the (older) page from frontswap */ inc_frontswap_failed_stores(); - if (dup) + if (dup) { __frontswap_clear(sis, offset); + frontswap_ops->invalidate_page(type, offset); + } } if (frontswap_writethrough_enabled) /* report failure so swap also writes to swap device */ -- cgit v0.10.2 From 91b57191cfd152c02ded0745250167d0263084f8 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 2 Dec 2014 15:59:28 -0800 Subject: mm/vmpressure.c: fix race in vmpressure_work_fn() In some android devices, there will be a "divide by zero" exception. vmpr->scanned could be zero before spin_lock(&vmpr->sr_lock). Addresses https://bugzilla.kernel.org/show_bug.cgi?id=88051 [akpm@linux-foundation.org: neaten] Reported-by: ji_ang Cc: Anton Vorontsov Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/vmpressure.c b/mm/vmpressure.c index d4042e7..c5afd57 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -165,6 +165,7 @@ static void vmpressure_work_fn(struct work_struct *work) unsigned long scanned; unsigned long reclaimed; + spin_lock(&vmpr->sr_lock); /* * Several contexts might be calling vmpressure(), so it is * possible that the work was rescheduled again before the old @@ -173,11 +174,12 @@ static void vmpressure_work_fn(struct work_struct *work) * here. No need for any locks here since we don't care if * vmpr->reclaimed is in sync. */ - if (!vmpr->scanned) + scanned = vmpr->scanned; + if (!scanned) { + spin_unlock(&vmpr->sr_lock); return; + } - spin_lock(&vmpr->sr_lock); - scanned = vmpr->scanned; reclaimed = vmpr->reclaimed; vmpr->scanned = 0; vmpr->reclaimed = 0; -- cgit v0.10.2 From 8d609725d4357f499e2103e46011308b32f53513 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 25 Nov 2014 20:28:24 -0600 Subject: xen-netfront: Remove BUGs on paged skb data which crosses a page boundary These BUGs can be erroneously triggered by frags which refer to tail pages within a compound page. The data in these pages may overrun the hardware page while still being contained within the compound page, but since compound_order() evaluates to 0 for tail pages the assertion fails. The code already iterates through subsequent pages correctly in this scenario, so the BUGs are unnecessary and can be removed. Fixes: f36c374782e4 ("xen/netfront: handle compound page fragments on transmit") Cc: # 3.7+ Signed-off-by: Seth Forshee Reviewed-by: David Vrabel Signed-off-by: David S. Miller diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index cca8713..ece8d18 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -496,9 +496,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, len = skb_frag_size(frag); offset = frag->page_offset; - /* Data must not cross a page boundary. */ - BUG_ON(len + offset > PAGE_SIZE<> PAGE_SHIFT; offset &= ~PAGE_MASK; @@ -506,8 +503,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, while (len > 0) { unsigned long bytes; - BUG_ON(offset >= PAGE_SIZE); - bytes = PAGE_SIZE - offset; if (bytes > len) bytes = len; -- cgit v0.10.2 From 4c2d518695338801110bc166eece6aa02822b0b4 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Fri, 28 Nov 2014 18:35:14 +0530 Subject: cxgb4: Fill in supported link mode for SFP modules Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 8520d55..279873c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2442,9 +2442,13 @@ static unsigned int from_fw_linkcaps(unsigned int type, unsigned int caps) SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full | SUPPORTED_10000baseKX4_Full; else if (type == FW_PORT_TYPE_FIBER_XFI || - type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) + type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) { v |= SUPPORTED_FIBRE; - else if (type == FW_PORT_TYPE_BP40_BA) + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseT_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseT_Full; + } else if (type == FW_PORT_TYPE_BP40_BA) v |= SUPPORTED_40000baseSR4_Full; if (caps & FW_PORT_CAP_ANEG) -- cgit v0.10.2 From 92788ac1eb06e69a822de45e2a8a63fa45eb5be2 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 2 Dec 2014 15:59:31 -0800 Subject: drivers/input/evdev.c: don't kfree() a vmalloc address If kzalloc() failed and then evdev_open_device() fails, evdev_open() will pass a vmalloc'ed pointer to kfree. This might fix https://bugzilla.kernel.org/show_bug.cgi?id=88401, where there was a crash in kfree(). Reported-by: Christian Casteyde Belatedly-Acked-by: Dmitry Torokhov Cc: Henrik Rydberg Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index bc20348..8afa28e 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -421,7 +421,7 @@ static int evdev_open(struct inode *inode, struct file *file) err_free_client: evdev_detach_client(evdev, client); - kfree(client); + kvfree(client); return error; } -- cgit v0.10.2 From e8577d1f0329d4842e8302e289fb2c22156abef4 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Tue, 2 Dec 2014 15:59:34 -0800 Subject: ipc/sem.c: fully initialize sem_array before making it visible ipc_addid() makes a new ipc identifier visible to everyone. New objects start as locked, so that the caller can complete the initialization after the call. Within struct sem_array, at least sma->sem_base and sma->sem_nsems are accessed without any locks, therefore this approach doesn't work. Thus: Move the ipc_addid() to the end of the initialization. Signed-off-by: Manfred Spraul Reported-by: Rik van Riel Acked-by: Rik van Riel Acked-by: Davidlohr Bueso Acked-by: Rafael Aquini Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/ipc/sem.c b/ipc/sem.c index 454f6c6..53c3310 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -507,13 +507,6 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) return retval; } - id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); - if (id < 0) { - ipc_rcu_putref(sma, sem_rcu_free); - return id; - } - ns->used_sems += nsems; - sma->sem_base = (struct sem *) &sma[1]; for (i = 0; i < nsems; i++) { @@ -528,6 +521,14 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); + + id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); + if (id < 0) { + ipc_rcu_putref(sma, sem_rcu_free); + return id; + } + ns->used_sems += nsems; + sem_unlock(sma, -1); rcu_read_unlock(); -- cgit v0.10.2 From 1ead0e79bfedd4b563b8ea7c585ca3884b0c89a7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 2 Dec 2014 15:59:37 -0800 Subject: fat: fix oops on corrupted vfat fs a) don't bother with ->d_time for positives - we only check it for negatives anyway. b) make sure to set it at unlink and rmdir time - at *that* point soon-to-be negative dentry matches then-current directory contents c) don't go into renaming of old alias in vfat_lookup() unless it has the same parent (which it will, unless we are seeing corrupted image) [hirofumi@mail.parknet.co.jp: make change minimum, don't call d_move() for dir] Signed-off-by: Al Viro Signed-off-by: OGAWA Hirofumi Cc: [3.17.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 6df8d3d..b8b92c2 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -736,7 +736,12 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, } alias = d_find_alias(inode); - if (alias && !vfat_d_anon_disconn(alias)) { + /* + * Checking "alias->d_parent == dentry->d_parent" to make sure + * FS is not corrupted (especially double linked dir). + */ + if (alias && alias->d_parent == dentry->d_parent && + !vfat_d_anon_disconn(alias)) { /* * This inode has non anonymous-DCACHE_DISCONNECTED * dentry. This means, the user did ->lookup() by an @@ -755,12 +760,9 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, out: mutex_unlock(&MSDOS_SB(sb)->s_lock); - dentry->d_time = dentry->d_parent->d_inode->i_version; - dentry = d_splice_alias(inode, dentry); - if (dentry) - dentry->d_time = dentry->d_parent->d_inode->i_version; - return dentry; - + if (!inode) + dentry->d_time = dir->i_version; + return d_splice_alias(inode, dentry); error: mutex_unlock(&MSDOS_SB(sb)->s_lock); return ERR_PTR(err); @@ -793,7 +795,6 @@ static int vfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry, inode); out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -824,6 +825,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry) clear_nlink(inode); inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; fat_detach(inode); + dentry->d_time = dir->i_version; out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -849,6 +851,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry) clear_nlink(inode); inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; fat_detach(inode); + dentry->d_time = dir->i_version; out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -889,7 +892,6 @@ static int vfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry, inode); mutex_unlock(&MSDOS_SB(sb)->s_lock); -- cgit v0.10.2 From 2022b4d18a491a578218ce7a4eca8666db895a73 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 2 Dec 2014 15:59:39 -0800 Subject: mm: fix swapoff hang after page migration and fork I've been seeing swapoff hangs in recent testing: it's cycling around trying unsuccessfully to find an mm for some remaining pages of swap. I have been exercising swap and page migration more heavily recently, and now notice a long-standing error in copy_one_pte(): it's trying to add dst_mm to swapoff's mmlist when it finds a swap entry, but is doing so even when it's a migration entry or an hwpoison entry. Which wouldn't matter much, except it adds dst_mm next to src_mm, assuming src_mm is already on the mmlist: which may not be so. Then if pages are later swapped out from dst_mm, swapoff won't be able to find where to replace them. There's already a !non_swap_entry() test for stats: move that up before the swap_duplicate() and the addition to mmlist. Signed-off-by: Hugh Dickins Cc: Kelley Nielsen Cc: [2.6.18+] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/memory.c b/mm/memory.c index 3e50383..d5f2ae9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -815,20 +815,20 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm, if (!pte_file(pte)) { swp_entry_t entry = pte_to_swp_entry(pte); - if (swap_duplicate(entry) < 0) - return entry.val; - - /* make sure dst_mm is on swapoff's mmlist. */ - if (unlikely(list_empty(&dst_mm->mmlist))) { - spin_lock(&mmlist_lock); - if (list_empty(&dst_mm->mmlist)) - list_add(&dst_mm->mmlist, - &src_mm->mmlist); - spin_unlock(&mmlist_lock); - } - if (likely(!non_swap_entry(entry))) + if (likely(!non_swap_entry(entry))) { + if (swap_duplicate(entry) < 0) + return entry.val; + + /* make sure dst_mm is on swapoff's mmlist. */ + if (unlikely(list_empty(&dst_mm->mmlist))) { + spin_lock(&mmlist_lock); + if (list_empty(&dst_mm->mmlist)) + list_add(&dst_mm->mmlist, + &src_mm->mmlist); + spin_unlock(&mmlist_lock); + } rss[MM_SWAPENTS]++; - else if (is_migration_entry(entry)) { + } else if (is_migration_entry(entry)) { page = migration_entry_to_page(entry); if (PageAnon(page)) -- cgit v0.10.2 From c4ea95d7cd08d9ffd7fa75e6c5e0332d596dd11e Mon Sep 17 00:00:00 2001 From: Daniel Forrest Date: Tue, 2 Dec 2014 15:59:42 -0800 Subject: mm: fix anon_vma_clone() error treatment Andrew Morton noticed that the error return from anon_vma_clone() was being dropped and replaced with -ENOMEM (which is not itself a bug because the only error return value from anon_vma_clone() is -ENOMEM). I did an audit of callers of anon_vma_clone() and discovered an actual bug where the error return was being lost. In __split_vma(), between Linux 3.11 and 3.12 the code was changed so the err variable is used before the call to anon_vma_clone() and the default initial value of -ENOMEM is overwritten. So a failure of anon_vma_clone() will return success since err at this point is now zero. Below is a patch which fixes this bug and also propagates the error return value from anon_vma_clone() in all cases. Fixes: ef0855d334e1 ("mm: mempolicy: turn vma_set_policy() into vma_dup_policy()") Signed-off-by: Daniel Forrest Reviewed-by: Michal Hocko Cc: Konstantin Khlebnikov Cc: Andrea Arcangeli Cc: Rik van Riel Cc: Tim Hartrick Cc: Hugh Dickins Cc: Michel Lespinasse Cc: Vlastimil Babka Cc: [3.12+] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/mmap.c b/mm/mmap.c index 87e82b3..ae91989 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -776,8 +776,11 @@ again: remove_next = 1 + (end > next->vm_end); * shrinking vma had, to cover any anon pages imported. */ if (exporter && exporter->anon_vma && !importer->anon_vma) { - if (anon_vma_clone(importer, exporter)) - return -ENOMEM; + int error; + + error = anon_vma_clone(importer, exporter); + if (error) + return error; importer->anon_vma = exporter->anon_vma; } } @@ -2469,7 +2472,8 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, if (err) goto out_free_vma; - if (anon_vma_clone(new, vma)) + err = anon_vma_clone(new, vma); + if (err) goto out_free_mpol; if (new->vm_file) diff --git a/mm/rmap.c b/mm/rmap.c index 19886fb..3e4c721 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -274,6 +274,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) { struct anon_vma_chain *avc; struct anon_vma *anon_vma; + int error; /* Don't bother if the parent process has no anon_vma here. */ if (!pvma->anon_vma) @@ -283,8 +284,9 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) * First, attach the new VMA to the parent VMA's anon_vmas, * so rmap can find non-COWed pages in child processes. */ - if (anon_vma_clone(vma, pvma)) - return -ENOMEM; + error = anon_vma_clone(vma, pvma); + if (error) + return error; /* Then add our own anon_vma. */ anon_vma = anon_vma_alloc(); -- cgit v0.10.2 From b724aa213df7aee08d57237ee0bbafffef09aea5 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 2 Dec 2014 15:59:45 -0800 Subject: lib/genalloc.c: export devm_gen_pool_create() for modules Modules can use this function for creating pool. Signed-off-by: Michal Simek Acked-by: Lad, Prabhakar Cc: Laura Abbott Cc: Olof Johansson Cc: Catalin Marinas Cc: Will Deacon Cc: Vladimir Zapolskiy Cc: Philipp Zabel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/lib/genalloc.c b/lib/genalloc.c index cce4dd6..2e65d20 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -598,6 +598,7 @@ struct gen_pool *devm_gen_pool_create(struct device *dev, int min_alloc_order, return pool; } +EXPORT_SYMBOL(devm_gen_pool_create); /** * dev_get_gen_pool - Obtain the gen_pool (if any) for a device -- cgit v0.10.2 From 7c3fbbdd04a681a1992ad6a3d7a36a63ff668753 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 2 Dec 2014 15:59:48 -0800 Subject: slab: fix nodeid bounds check for non-contiguous node IDs The bounds check for nodeid in ____cache_alloc_node gives false positives on machines where the node IDs are not contiguous, leading to a panic at boot time. For example, on a POWER8 machine the node IDs are typically 0, 1, 16 and 17. This means that num_online_nodes() returns 4, so when ____cache_alloc_node is called with nodeid = 16 the VM_BUG_ON triggers, like this: kernel BUG at /home/paulus/kernel/kvm/mm/slab.c:3079! Call Trace: .____cache_alloc_node+0x5c/0x270 (unreliable) .kmem_cache_alloc_node_trace+0xdc/0x360 .init_list+0x3c/0x128 .kmem_cache_init+0x1dc/0x258 .start_kernel+0x2a0/0x568 start_here_common+0x20/0xa8 To fix this, we instead compare the nodeid with MAX_NUMNODES, and additionally make sure it isn't negative (since nodeid is an int). The check is there mainly to protect the array dereference in the get_node() call in the next line, and the array being dereferenced is of size MAX_NUMNODES. If the nodeid is in range but invalid (for example if the node is off-line), the BUG_ON in the next line will catch that. Fixes: 14e50c6a9bc2 ("mm: slab: Verify the nodeid passed to ____cache_alloc_node") Signed-off-by: Paul Mackerras Reviewed-by: Yasuaki Ishimatsu Reviewed-by: Pekka Enberg Acked-by: David Rientjes Cc: Christoph Lameter Cc: Joonsoo Kim Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/slab.c b/mm/slab.c index eb2b2ea..f34e053 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3076,7 +3076,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, void *obj; int x; - VM_BUG_ON(nodeid > num_online_nodes()); + VM_BUG_ON(nodeid < 0 || nodeid >= MAX_NUMNODES); n = get_node(cachep, nodeid); BUG_ON(!n); -- cgit v0.10.2 From b163be4cf4a3bb673038a21e368954f7f88347b7 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Wed, 3 Dec 2014 16:38:46 +0000 Subject: ASoC: dwc: Allocate resources with devm_ioremap_resource Prepare for the introduction of device-tree support by re-ordering some of the allocations and using devm_iomap_resource to simplify IO mapping. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index e961388..08f0229 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -338,31 +338,34 @@ static int dw_i2s_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no i2s resource defined\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "i2s region already claimed\n"); - return -EBUSY; - } - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { dev_warn(&pdev->dev, "kzalloc fail\n"); return -ENOMEM; } - dev->i2s_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!dev->i2s_base) { - dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); + if (!dw_i2s_dai) { + dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); return -ENOMEM; } + dw_i2s_dai->ops = &dw_i2s_dai_ops; + dw_i2s_dai->suspend = dw_i2s_suspend; + dw_i2s_dai->resume = dw_i2s_resume; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no i2s resource defined\n"); + return -ENODEV; + } + + dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->i2s_base)) { + dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + return PTR_ERR(dev->i2s_base); + } + cap = pdata->cap; dev->capability = cap; dev->i2s_clk_cfg = pdata->i2s_clk_cfg; @@ -388,13 +391,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_put; - dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); - if (!dw_i2s_dai) { - dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); - ret = -ENOMEM; - goto err_clk_disable; - } - if (cap & DWC_I2S_PLAY) { dev_dbg(&pdev->dev, " designware: play supported\n"); dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; @@ -411,10 +407,6 @@ static int dw_i2s_probe(struct platform_device *pdev) dw_i2s_dai->capture.rates = pdata->snd_rates; } - dw_i2s_dai->ops = &dw_i2s_dai_ops; - dw_i2s_dai->suspend = dw_i2s_suspend; - dw_i2s_dai->resume = dw_i2s_resume; - dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, -- cgit v0.10.2 From e98c89e05e8b33ad40efff2012c1404e39d12ad8 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 2 Dec 2014 14:34:30 +0100 Subject: ASoC: fsi: Deletion of unnecessary checks before the function call "clk_disable" The clk_disable() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 66fddec..eefb161 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -842,12 +842,9 @@ static int fsi_clk_disable(struct device *dev, return -EINVAL; if (1 == clock->count--) { - if (clock->xck) - clk_disable(clock->xck); - if (clock->ick) - clk_disable(clock->ick); - if (clock->div) - clk_disable(clock->div); + clk_disable(clock->xck); + clk_disable(clock->ick); + clk_disable(clock->div); } return 0; -- cgit v0.10.2 From 1679b532870f565bc184434f545cdbd3fdeff6cf Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 2 Dec 2014 17:15:11 +0100 Subject: ASoC: mop500: Deletion of unnecessary checks before the function call "of_node_put" The of_node_put() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Mark Brown diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index ea9ba284..9f2d045 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -63,10 +63,8 @@ static void mop500_of_node_put(void) int i; for (i = 0; i < 2; i++) { - if (mop500_dai_links[i].cpu_of_node) - of_node_put(mop500_dai_links[i].cpu_of_node); - if (mop500_dai_links[i].codec_of_node) - of_node_put(mop500_dai_links[i].codec_of_node); + of_node_put(mop500_dai_links[i].cpu_of_node); + of_node_put(mop500_dai_links[i].codec_of_node); } } -- cgit v0.10.2 From 97463e193654574e1533f71359d91d9d2fdb3571 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:02:43 +0000 Subject: ASoC: rsnd: add .fallback callback Current R-Car sound has PIO fallback support if it couldn't use DMA. This fallback is done in .remove callback, but, it should have .fallback callback. Otherwise, normal .remove callback will have strange behavior. This patch adds .fallback callback. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 110b99d..0785f84 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -368,7 +368,7 @@ rsnd_dma_channel_err: /* * DMA failed. try to PIO mode * see - * rsnd_ssi_dma_remove() + * rsnd_ssi_fallback() * rsnd_rdai_continuance_probe() */ return -EAGAIN; @@ -1023,7 +1023,7 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, * SSI will be switch to PIO mode if it was DMA mode * see * rsnd_dma_init() - * rsnd_ssi_dma_remove() + * rsnd_ssi_fallback() */ rsnd_dai_call(remove, io, rdai); @@ -1034,6 +1034,11 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, rsnd_path_break(priv, io, dvc); /* + * fallback + */ + rsnd_dai_call(fallback, io, rdai); + + /* * retry to "probe". * DAI has SSI which is PIO mode only now. */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index ed44ca8..83e1066 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -206,6 +206,8 @@ struct rsnd_mod_ops { int (*pcm_new)(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd); + int (*fallback)(struct rsnd_mod *mod, + struct rsnd_dai *rdai); }; struct rsnd_dai_stream; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 346d3dc..e03e70b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -465,11 +465,17 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); + + return 0; +} + +static int rsnd_ssi_fallback(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); - /* * fallback to PIO * @@ -541,6 +547,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_dma_start, .stop = rsnd_ssi_dma_stop, + .fallback = rsnd_ssi_fallback, }; /* -- cgit v0.10.2 From 417f96420a5823485b90ad7ee9fddb67996bbd7f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:02:55 +0000 Subject: ASoC: rsnd: add callback status check method R-Car sound can use SSI/SRC/DVC modules, and these are controlled as rsnd_mod in rsnd driver. These rsnd_mod has each own function as callback. Basically these callback function has pair like probe/remove, start/stop, etc. And, these functions are called by order to each stage like below. 1. src->probe 2. ssi->probe 3. dvc->probe 4. src->start 5. ssi->start 6. dvc->start 7. src->stop 8. ssi->stop 9. dvc->stop 10. src->remove 11. ssi->remove 12. dvc->remove But, current rsnd driver doesn't care about its status which indicates which function is called. For example, if 5) returns error, 6) is not called. In such case, 9) should not be called. This patch care about each modules status. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 0785f84..fce61a0 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -416,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - dev_dbg(dev, "%s[%d] %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ - (mod)->ops->func(mod, rdai); \ + u32 mask = 1 << __rsnd_mod_shift_##func; \ + u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ + int ret = 0; \ + if ((mod->status & mask) == call) { \ + dev_dbg(dev, "%s[%d] %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ + ret = (mod)->ops->func(mod, rdai); \ + mod->status = (mod->status & ~mask) | (~call & mask); \ + } \ + ret; \ }) #define rsnd_mod_call(mod, func, rdai...) \ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 83e1066..c74c239 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -218,7 +218,35 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_dma dma; struct rsnd_dai_stream *io; + u32 status; }; +/* + * status + * + * bit + * 0 0: probe 1: remove + * 1 0: init 1: quit + * 2 0: start 1: stop + * 3 0: pcm_new + * 4 0: fallback + */ +#define __rsnd_mod_shift_probe 0 +#define __rsnd_mod_shift_remove 0 +#define __rsnd_mod_shift_init 1 +#define __rsnd_mod_shift_quit 1 +#define __rsnd_mod_shift_start 2 +#define __rsnd_mod_shift_stop 2 +#define __rsnd_mod_shift_pcm_new 3 +#define __rsnd_mod_shift_fallback 4 + +#define __rsnd_mod_call_probe 0 +#define __rsnd_mod_call_remove 1 +#define __rsnd_mod_call_init 0 +#define __rsnd_mod_call_quit 1 +#define __rsnd_mod_call_start 0 +#define __rsnd_mod_call_stop 1 +#define __rsnd_mod_call_pcm_new 0 +#define __rsnd_mod_call_fallback 0 #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) -- cgit v0.10.2 From 660cdce2fbdcbe48eb143cc394fdb24316232dba Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:03:39 +0000 Subject: ASoC: rsnd: rsnd_src_ssiu_stop() stops SSIU compulsorily rsnd_src_ssiu_stop() is used to stop SSIU, but it shouldn't depend on whether it is using SSIU. This patch stops SSIU compulsorily. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index c74c239..1344c3a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -431,8 +431,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai, int use_busif); int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai, - int use_busif); + struct rsnd_dai *rdai); int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 4679501..384af90 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -175,14 +175,12 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, } int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai, - int use_busif) + struct rsnd_dai *rdai) { /* * DMA settings for SSIU */ - if (use_busif) - rsnd_mod_write(ssi_mod, SSI_CTRL, 0); + rsnd_mod_write(ssi_mod, SSI_CTRL, 0); return 0; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index e03e70b..a200452 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -424,7 +424,7 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, rsnd_ssi_hw_stop(ssi, rdai); - rsnd_src_ssiu_stop(mod, rdai, 0); + rsnd_src_ssiu_stop(mod, rdai); return 0; } @@ -528,7 +528,7 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, rsnd_dma_stop(dma); - rsnd_src_ssiu_stop(mod, rdai, 1); + rsnd_src_ssiu_stop(mod, rdai); return 0; } -- cgit v0.10.2 From 05795411aeda8a86865b595e09f22273d85fce83 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:05:01 +0000 Subject: ASoC: rsnd: tidyup PIO/DMA mode settings method Current ssi.c has .cr_etc which is used for SSICR's etc settings. but, it is used as PIO/DMA switching purpose now. This patch tidyup this method. This is prepare for under/over run issue handling Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 1344c3a..12e8945 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -445,6 +445,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); /* * R-Car DVC diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index a200452..8928913 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -68,7 +68,6 @@ struct rsnd_ssi { struct rsnd_dai *rdai; u32 cr_own; u32 cr_clk; - u32 cr_etc; int err; unsigned int usrcnt; unsigned int rate; @@ -185,6 +184,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, { struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); struct device *dev = rsnd_priv_to_dev(priv); + u32 cr_mode; u32 cr; if (0 == ssi->usrcnt) { @@ -198,9 +198,14 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, } } + cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? + DMEN : /* DMA : use DMA */ + UIEN | OIEN | DIEN; /* PIO : enable interrupt */ + + cr = ssi->cr_own | ssi->cr_clk | - ssi->cr_etc | + cr_mode | EN; rsnd_mod_write(&ssi->mod, SSICR, cr); @@ -403,9 +408,6 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - /* enable PIO IRQ */ - ssi->cr_etc = UIEN | OIEN | DIEN; - rsnd_src_ssiu_start(mod, rdai, 0); rsnd_src_enable_ssi_irq(mod, rdai); @@ -420,8 +422,6 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - ssi->cr_etc = 0; - rsnd_ssi_hw_stop(ssi, rdai); rsnd_src_ssiu_stop(mod, rdai); @@ -498,9 +498,6 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - /* enable DMA transfer */ - ssi->cr_etc = DMEN; - rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); rsnd_dma_start(dma); @@ -520,8 +517,6 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); - ssi->cr_etc = 0; - rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); rsnd_ssi_hw_stop(ssi, rdai); @@ -550,6 +545,12 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .fallback = rsnd_ssi_fallback, }; +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) +{ + return mod->ops == &rsnd_ssi_dma_ops; +} + + /* * Non SSI */ -- cgit v0.10.2 From c17dba8b8e198758a4f61fe89e16106b82af18a9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:05:09 +0000 Subject: ASoC: rsnd: tidyup SSI interrupt enable/disable method Current SSI doesn't care interrupt "disable" method. And, it is used when PIO mode only at this point. SSI interrupt will be used for sound R/L issue workaround when DMA mode too. This patch tidyup SSI interrupt enable/disable method. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 12e8945..48999b1 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -432,8 +432,10 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, int use_busif); int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai); -int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, +int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai); +int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai); #define rsnd_src_nr(priv) ((priv)->src_nr) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 384af90..c301195 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -185,18 +185,37 @@ int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, return 0; } -int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, +int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - /* enable PIO interrupt if Gen2 */ - if (rsnd_is_gen2(priv)) + if (rsnd_is_gen1(priv)) + return 0; + + /* enable SSI interrupt if Gen2 */ + if (rsnd_ssi_is_dma_mode(ssi_mod)) + rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000); + else rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); return 0; } +int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + + if (rsnd_is_gen1(priv)) + return 0; + + /* disable SSI interrupt if Gen2 */ + rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000); + + return 0; +} + unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 8928913..42d7c7e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -410,10 +410,10 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, rsnd_src_ssiu_start(mod, rdai, 0); - rsnd_src_enable_ssi_irq(mod, rdai); - rsnd_ssi_hw_start(ssi, rdai, io); + rsnd_src_ssi_irq_enable(mod, rdai); + return 0; } @@ -422,6 +422,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + rsnd_src_ssi_irq_disable(mod, rdai); + rsnd_ssi_hw_stop(ssi, rdai); rsnd_src_ssiu_stop(mod, rdai); -- cgit v0.10.2 From d1b64056d479a115e02c88ab63bc2406a0c46468 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:05:34 +0000 Subject: ASoC: rsnd: care SSIWSR register in rsnd_ssi_hw_start() Current SSI is careing SSIWSR which controls WS continue mode when DMA mode, but, it should be cared when PIO mode too. This patch fixup it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 42d7c7e..d67bca9 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -210,6 +210,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, rsnd_mod_write(&ssi->mod, SSICR, cr); + /* enable WS continue */ + if (rsnd_dai_is_clk_master(rdai)) + rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + ssi->usrcnt++; dev_dbg(dev, "%s[%d] hw started\n", @@ -506,10 +510,6 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, rsnd_ssi_hw_start(ssi, ssi->rdai, io); - /* enable WS continue */ - if (rsnd_dai_is_clk_master(rdai)) - rsnd_mod_write(&ssi->mod, SSIWSR, CONT); - return 0; } -- cgit v0.10.2 From 3ba84f45231c19af2ca281273e4fca8df65de341 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:05:44 +0000 Subject: ASoC: rsnd: clear status register when HW start Let's clear SSI status when HW start Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d67bca9..3c0d31d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -214,6 +214,9 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, if (rsnd_dai_is_clk_master(rdai)) rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + /* clear error status */ + rsnd_mod_write(&ssi->mod, SSISR, 0); + ssi->usrcnt++; dev_dbg(dev, "%s[%d] hw started\n", -- cgit v0.10.2 From 7b466fc6130a4183b505ebbd8220b55335f69d88 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:05:54 +0000 Subject: ASoC: rsnd: synchronize SSI start/stop sequence between PIO/DMA mode Current SSI start/stop sequence is different between PIO/DMA mode, but, almost all are same. this patch synchronize it. It will be shared in the future. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 3c0d31d..292e98b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -95,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod) struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int use_busif = 0; + if (!rsnd_ssi_is_dma_mode(mod)) + return 0; + if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF)) use_busif = 1; if (rsnd_io_to_mod_src(io)) @@ -415,7 +418,7 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - rsnd_src_ssiu_start(mod, rdai, 0); + rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); rsnd_ssi_hw_start(ssi, rdai, io); @@ -431,6 +434,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, rsnd_src_ssi_irq_disable(mod, rdai); + rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); + rsnd_ssi_hw_stop(ssi, rdai); rsnd_src_ssiu_stop(mod, rdai); @@ -509,10 +514,10 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); - rsnd_dma_start(dma); - rsnd_ssi_hw_start(ssi, ssi->rdai, io); + rsnd_dma_start(dma); + return 0; } @@ -522,12 +527,12 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + rsnd_dma_stop(dma); + rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); rsnd_ssi_hw_stop(ssi, rdai); - rsnd_dma_stop(dma); - rsnd_src_ssiu_stop(mod, rdai); return 0; -- cgit v0.10.2 From 4d24d44e4d368314f23159ad833fc6bd15868c1a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:06:03 +0000 Subject: ASoC: rsnd: show master clock rate when ADG probe master clock rate is useful information for debug. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index fc41a0e..14d1a71 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev, adg->clk[CLKI] = devm_clk_get(dev, "clk_i"); for_each_rsnd_clk(clk, adg, i) - dev_dbg(dev, "clk %d : %p\n", i, clk); + dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); rsnd_adg_ssi_clk_init(priv, adg); -- cgit v0.10.2 From 170a2497a25688f4c6bbf011208fc5fe144ef59c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:06:14 +0000 Subject: ASoC: rsnd: move snd_kcontrol_new fucntions to core.c Current DVC is using snd_kcontrol_new functions, but, SRC will need same method. Move common functions to core.c Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index fce61a0..77af008 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -970,6 +970,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = { }; /* + * snd_kcontrol + */ +#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value) +static int rsnd_kctrl_info(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_info *uinfo) +{ + struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); + + if (cfg->texts) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = cfg->size; + uinfo->value.enumerated.items = cfg->max; + if (uinfo->value.enumerated.item >= cfg->max) + uinfo->value.enumerated.item = cfg->max - 1; + strlcpy(uinfo->value.enumerated.name, + cfg->texts[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + } else { + uinfo->count = cfg->size; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = cfg->max; + uinfo->type = (cfg->max == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : + SNDRV_CTL_ELEM_TYPE_INTEGER; + } + + return 0; +} + +static int rsnd_kctrl_get(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uc) +{ + struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); + int i; + + for (i = 0; i < cfg->size; i++) + if (cfg->texts) + uc->value.enumerated.item[i] = cfg->val[i]; + else + uc->value.integer.value[i] = cfg->val[i]; + + return 0; +} + +static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uc) +{ + struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); + struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); + int i, change = 0; + + for (i = 0; i < cfg->size; i++) { + if (cfg->texts) { + change |= (uc->value.enumerated.item[i] != cfg->val[i]); + cfg->val[i] = uc->value.enumerated.item[i]; + } else { + change |= (uc->value.integer.value[i] != cfg->val[i]); + cfg->val[i] = uc->value.integer.value[i]; + } + } + + if (change) + cfg->update(mod); + + return change; +} + +static int __rsnd_kctrl_new(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_kctrl_cfg *cfg, + void (*update)(struct rsnd_mod *mod)) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_kcontrol *kctrl; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = name, + .info = rsnd_kctrl_info, + .get = rsnd_kctrl_get, + .put = rsnd_kctrl_put, + .private_value = (unsigned long)cfg, + }; + int ret; + + kctrl = snd_ctl_new1(&knew, mod); + if (!kctrl) + return -ENOMEM; + + ret = snd_ctl_add(card, kctrl); + if (ret < 0) + return ret; + + cfg->update = update; + + return 0; +} + +int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_m *_cfg, + u32 max) +{ + _cfg->cfg.max = max; + _cfg->cfg.size = RSND_DVC_CHANNELS; + _cfg->cfg.val = _cfg->val; + return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update); +} + +int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_s *_cfg, + u32 max) +{ + _cfg->cfg.max = max; + _cfg->cfg.size = 1; + _cfg->cfg.val = &_cfg->val; + return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update); +} + +int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_kctrl_cfg_s *_cfg, + void (*update)(struct rsnd_mod *mod), + const char * const *texts, + u32 max) +{ + _cfg->cfg.max = max; + _cfg->cfg.size = 1; + _cfg->cfg.val = &_cfg->val; + _cfg->cfg.texts = texts; + return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update); +} + +/* * snd_soc_platform */ diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index e2c8473..5380a48 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -11,36 +11,18 @@ #include "rsnd.h" #define RSND_DVC_NAME_SIZE 16 -#define RSND_DVC_CHANNELS 2 #define DVC_NAME "dvc" -struct rsnd_dvc_cfg { - unsigned int max; - unsigned int size; - u32 *val; - const char * const *texts; -}; - -struct rsnd_dvc_cfg_m { - struct rsnd_dvc_cfg cfg; - u32 val[RSND_DVC_CHANNELS]; -}; - -struct rsnd_dvc_cfg_s { - struct rsnd_dvc_cfg cfg; - u32 val; -}; - struct rsnd_dvc { struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; - struct rsnd_dvc_cfg_m volume; - struct rsnd_dvc_cfg_m mute; - struct rsnd_dvc_cfg_s ren; /* Ramp Enable */ - struct rsnd_dvc_cfg_s rup; /* Ramp Rate Up */ - struct rsnd_dvc_cfg_s rdown; /* Ramp Rate Down */ + struct rsnd_kctrl_cfg_m volume; + struct rsnd_kctrl_cfg_m mute; + struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ + struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ + struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ }; #define rsnd_mod_to_dvc(_mod) \ @@ -222,140 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod, return 0; } -static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_info *uinfo) -{ - struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - - if (cfg->texts) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = cfg->size; - uinfo->value.enumerated.items = cfg->max; - if (uinfo->value.enumerated.item >= cfg->max) - uinfo->value.enumerated.item = cfg->max - 1; - strlcpy(uinfo->value.enumerated.name, - cfg->texts[uinfo->value.enumerated.item], - sizeof(uinfo->value.enumerated.name)); - } else { - uinfo->count = cfg->size; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = cfg->max; - uinfo->type = (cfg->max == 1) ? - SNDRV_CTL_ELEM_TYPE_BOOLEAN : - SNDRV_CTL_ELEM_TYPE_INTEGER; - } - - return 0; -} - -static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *ucontrol) -{ - struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - int i; - - for (i = 0; i < cfg->size; i++) - if (cfg->texts) - ucontrol->value.enumerated.item[i] = cfg->val[i]; - else - ucontrol->value.integer.value[i] = cfg->val[i]; - - return 0; -} - -static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *ucontrol) -{ - struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); - struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - int i, change = 0; - - for (i = 0; i < cfg->size; i++) { - if (cfg->texts) { - change |= (ucontrol->value.enumerated.item[i] != cfg->val[i]); - cfg->val[i] = ucontrol->value.enumerated.item[i]; - } else { - change |= (ucontrol->value.integer.value[i] != cfg->val[i]); - cfg->val[i] = ucontrol->value.integer.value[i]; - } - } - - if (change) - rsnd_dvc_volume_update(mod); - - return change; -} - -static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg *private) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_kcontrol *kctrl; - struct snd_kcontrol_new knew = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = name, - .info = rsnd_dvc_volume_info, - .get = rsnd_dvc_volume_get, - .put = rsnd_dvc_volume_put, - .private_value = (unsigned long)private, - }; - int ret; - - kctrl = snd_ctl_new1(&knew, mod); - if (!kctrl) - return -ENOMEM; - - ret = snd_ctl_add(card, kctrl); - if (ret < 0) - return ret; - - return 0; -} - -static int _rsnd_dvc_pcm_new_m(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg_m *private, - u32 max) -{ - private->cfg.max = max; - private->cfg.size = RSND_DVC_CHANNELS; - private->cfg.val = private->val; - return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); -} - -static int _rsnd_dvc_pcm_new_s(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg_s *private, - u32 max) -{ - private->cfg.max = max; - private->cfg.size = 1; - private->cfg.val = &private->val; - return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); -} - -static int _rsnd_dvc_pcm_new_e(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg_s *private, - const char * const *texts, - u32 max) -{ - private->cfg.max = max; - private->cfg.size = 1; - private->cfg.val = &private->val; - private->cfg.texts = texts; - return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); -} - static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd) @@ -365,41 +213,46 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, int ret; /* Volume */ - ret = _rsnd_dvc_pcm_new_m(mod, rdai, rtd, + ret = rsnd_kctrl_new_m(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Playback Volume" : "DVC In Capture Volume", + rsnd_dvc_volume_update, &dvc->volume, 0x00800000 - 1); if (ret < 0) return ret; /* Mute */ - ret = _rsnd_dvc_pcm_new_m(mod, rdai, rtd, + ret = rsnd_kctrl_new_m(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Mute Switch" : "DVC In Mute Switch", + rsnd_dvc_volume_update, &dvc->mute, 1); if (ret < 0) return ret; /* Ramp */ - ret = _rsnd_dvc_pcm_new_s(mod, rdai, rtd, + ret = rsnd_kctrl_new_s(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Ramp Switch" : "DVC In Ramp Switch", + rsnd_dvc_volume_update, &dvc->ren, 1); if (ret < 0) return ret; - ret = _rsnd_dvc_pcm_new_e(mod, rdai, rtd, + ret = rsnd_kctrl_new_e(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", &dvc->rup, + rsnd_dvc_volume_update, dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate)); if (ret < 0) return ret; - ret = _rsnd_dvc_pcm_new_e(mod, rdai, rtd, + ret = rsnd_kctrl_new_e(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", &dvc->rdown, + rsnd_dvc_volume_update, dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate)); if (ret < 0) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 48999b1..133ba1f 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -418,6 +418,51 @@ struct rsnd_priv { }) /* + * rsnd_kctrl + */ +struct rsnd_kctrl_cfg { + unsigned int max; + unsigned int size; + u32 *val; + const char * const *texts; + void (*update)(struct rsnd_mod *mod); +}; + +#define RSND_DVC_CHANNELS 2 +struct rsnd_kctrl_cfg_m { + struct rsnd_kctrl_cfg cfg; + u32 val[RSND_DVC_CHANNELS]; +}; + +struct rsnd_kctrl_cfg_s { + struct rsnd_kctrl_cfg cfg; + u32 val; +}; + +int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_m *_cfg, + u32 max); +int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_s *_cfg, + u32 max); +int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_kctrl_cfg_s *_cfg, + void (*update)(struct rsnd_mod *mod), + const char * const *texts, + u32 max); + +/* * R-Car SRC */ int rsnd_src_probe(struct platform_device *pdev, -- cgit v0.10.2 From 0cf7718520dcd673d385105d92a6b1ab923ee373 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:06:25 +0000 Subject: ASoC: rsnd: tidyup rsnd_io_to_runtime() macro Avoid NULL pointer access Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 133ba1f..5826c8a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -303,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) -#define rsnd_io_to_runtime(io) ((io)->substream->runtime) +#define rsnd_io_to_runtime(io) ((io)->substream ? \ + (io)->substream->runtime : NULL) void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); -- cgit v0.10.2 From b167a5780cacb602dfbd3d6f853d7ce916df3fb0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:06:34 +0000 Subject: ASoC: rsnd: use rsnd_src_convert_rate() once on rsnd_src_set_convert_rate_gen2() using many rsnd_src_convert_rate() is not readable. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index c301195..0a56ccd 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -525,16 +525,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); uint ratio; int ret; /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (!rsnd_src_convert_rate(src)) + if (!convert_rate) ratio = 0; - else if (rsnd_src_convert_rate(src) > runtime->rate) - ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate; + else if (convert_rate > runtime->rate) + ratio = 100 * convert_rate / runtime->rate; else - ratio = 100 * runtime->rate / rsnd_src_convert_rate(src); + ratio = 100 * runtime->rate / convert_rate; if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n"); -- cgit v0.10.2 From 603cefa59b1aed460f5fdcb1b6af32efc7e6d82f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:06:43 +0000 Subject: ASoC: rsnd: initialize SRC on rsnd_src_init() Current src initialize SRC on rsnd_src_set_convert_rate() but, it should be done on rsnd_src_init(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 0a56ccd..6a63f8f 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -256,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 1); - /* - * Initialize the operation of the SRC internal circuits - * see rsnd_src_start() - */ - rsnd_mod_write(mod, SRC_SRCIR, 1); - /* Set channel number and output bit length */ rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod)); @@ -286,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod, clk_prepare_enable(src->clk); + /* + * Initialize the operation of the SRC internal circuits + * see rsnd_src_start() + */ + rsnd_mod_write(mod, SRC_SRCIR, 1); + return 0; } @@ -306,7 +306,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, /* * Cancel the initialization and operate the SRC function - * see rsnd_src_set_convert_rate() + * see rsnd_src_init() */ rsnd_mod_write(mod, SRC_SRCIR, 0); -- cgit v0.10.2 From 933cc8cb08d867459689662cf67017ea6f0c6b53 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:07:07 +0000 Subject: ASoC: rsnd: set SRC_ROUTE_MODE0 on each rsnd_src_set_convert_rate() Current src.c sets SRC_ROUTE_MODE0 on rsnd_src_start(), but, set it in rsnd_src_set_convert_rate() is natural. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 6a63f8f..0b6b230 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -302,17 +302,12 @@ static int rsnd_src_quit(struct rsnd_mod *mod, static int rsnd_src_start(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_src *src = rsnd_mod_to_src(mod); - /* * Cancel the initialization and operate the SRC function * see rsnd_src_init() */ rsnd_mod_write(mod, SRC_SRCIR, 0); - if (rsnd_src_convert_rate(src)) - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); - return 0; } @@ -320,11 +315,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_src *src = rsnd_mod_to_src(mod); - - if (rsnd_src_convert_rate(src)) - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); - + /* nothing to do */ return 0; } @@ -431,6 +422,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod, static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; ret = rsnd_src_set_convert_rate(mod, rdai); @@ -444,6 +436,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_MNFSR, rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); + /* Gen1/Gen2 are not compatible */ + if (rsnd_src_convert_rate(src)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ return 0; @@ -548,6 +544,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); + if (convert_rate) { + /* Gen1/Gen2 are not compatible */ + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + } + switch (rsnd_mod_id(mod)) { case 5: case 6: -- cgit v0.10.2 From 49229850bee539e94f0c085a0f89cbbda7f6074b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:07:17 +0000 Subject: ASoC: rsnd: share SSI starting method between PIO/DMA mode Basically, SSI starting method is same between PIO/DMA mode. Let's share it Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 292e98b..5af016e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -412,8 +412,8 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, return ret; } -static int rsnd_ssi_pio_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); @@ -427,8 +427,8 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -448,8 +448,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .probe = rsnd_ssi_pio_probe, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, - .start = rsnd_ssi_pio_start, - .stop = rsnd_ssi_pio_stop, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, }; static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, @@ -508,13 +508,9 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, static int rsnd_ssi_dma_start(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - - rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - rsnd_ssi_hw_start(ssi, ssi->rdai, io); + rsnd_ssi_start(mod, rdai); rsnd_dma_start(dma); @@ -524,16 +520,11 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); rsnd_dma_stop(dma); - rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - - rsnd_ssi_hw_stop(ssi, rdai); - - rsnd_src_ssiu_stop(mod, rdai); + rsnd_ssi_stop(mod, rdai); return 0; } -- cgit v0.10.2 From f0ef0cb84b4351601e696705c0be9cd54e264d81 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:07:28 +0000 Subject: ASoC: rsnd: remove un-necessary parameter from rsnd_src_start/stop() rsnd_src_start/stop() requests struct rsnd_dai as parameter. but, it is not used, and become more complex in L/R error handling. Let's remove it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 0b6b230..eede3ac 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -299,8 +299,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, return 0; } -static int rsnd_src_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_src_start(struct rsnd_mod *mod) { /* * Cancel the initialization and operate the SRC function @@ -311,9 +310,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, return 0; } - -static int rsnd_src_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_src_stop(struct rsnd_mod *mod) { /* nothing to do */ return 0; @@ -488,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod, rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id)); - return rsnd_src_start(mod, rdai); + return rsnd_src_start(mod); } static int rsnd_src_stop_gen1(struct rsnd_mod *mod, @@ -498,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod, rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); - return rsnd_src_stop(mod, rdai); + return rsnd_src_stop(mod); } static struct rsnd_mod_ops rsnd_src_gen1_ops = { @@ -646,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_CTRL, val); - return rsnd_src_start(mod, rdai); + return rsnd_src_start(mod); } static int rsnd_src_stop_gen2(struct rsnd_mod *mod, @@ -658,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod, rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); - return rsnd_src_stop(mod, rdai); + return rsnd_src_stop(mod); } static struct rsnd_mod_ops rsnd_src_gen2_ops = { -- cgit v0.10.2 From 7cc78f8fa02c2485104b86434acbc1538a3bd807 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Wed, 3 Dec 2014 15:37:08 -0800 Subject: context_tracking: Restore previous state in schedule_user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It appears that some SCHEDULE_USER (asm for schedule_user) callers in arch/x86/kernel/entry_64.S are called from RCU kernel context, and schedule_user will return in RCU user context. This causes RCU warnings and possible failures. This is intended to be a minimal fix suitable for 3.18. Reported-and-tested-by: Dave Jones Cc: Oleg Nesterov Cc: Frédéric Weisbecker Acked-by: Paul E. McKenney Signed-off-by: Andy Lutomirski Signed-off-by: Linus Torvalds diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 24beb9b..89e7283 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2874,10 +2874,14 @@ asmlinkage __visible void __sched schedule_user(void) * or we have been woken up remotely but the IPI has not yet arrived, * we haven't yet exited the RCU idle mode. Do it here manually until * we find a better solution. + * + * NB: There are buggy callers of this function. Ideally we + * should warn if prev_state != IN_USER, but that will trigger + * too frequently to make sense yet. */ - user_exit(); + enum ctx_state prev_state = exception_enter(); schedule(); - user_enter(); + exception_exit(prev_state); } #endif -- cgit v0.10.2 From 2b21ef0aae65f22f5ba86b13c4588f6f0c2dbefb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 4 Dec 2014 13:13:28 -0500 Subject: ahci: disable MSI on SAMSUNG 0xa800 SSD Just like 0x1600 which got blacklisted by 66a7cbc303f4 ("ahci: disable MSI instead of NCQ on Samsung pci-e SSDs on macbooks"), 0xa800 chokes on NCQ commands if MSI is enabled. Disable MSI. Signed-off-by: Tejun Heo Reported-by: Dominik Mierzejewski Link: https://bugzilla.kernel.org/show_bug.cgi?id=89171 Cc: stable@vger.kernel.org diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 0ef0e39..49f1e68 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -495,6 +495,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { * enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731 */ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi }, + { PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi }, /* Enmotus */ { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, -- cgit v0.10.2 From 9ea359f7314132cbcb5a502d2d8ef095be1f45e4 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 1 Dec 2014 17:34:04 +0200 Subject: i2c: davinci: generate STP always when NACK is received MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to I2C specification the NACK should be handled as follows: "When SDA remains HIGH during this ninth clock pulse, this is defined as the Not Acknowledge signal. The master can then generate either a STOP condition to abort the transfer, or a repeated START condition to start a new transfer." [I2C spec Rev. 6, 3.1.6: http://www.nxp.com/documents/user_manual/UM10204.pdf] Currently the Davinci i2c driver interrupts the transfer on receipt of a NACK but fails to send a STOP in some situations and so makes the bus stuck until next I2C IP reset (idle/enable). For example, the issue will happen during SMBus read transfer which consists from two i2c messages write command/address and read data: S Slave Address Wr A Command Code A Sr Slave Address Rd A D1..Dn A P <--- write -----------------------> <--- read ---------------------> The I2C client device will send NACK if it can't recognize "Command Code" and it's expected from I2C master to generate STP in this case. But now, Davinci i2C driver will just exit with -EREMOTEIO and STP will not be generated. Hence, fix it by generating Stop condition (STP) always when NACK is received. This patch fixes Davinci I2C in the same way it was done for OMAP I2C commit cda2109a26eb ("i2c: omap: query STP always when NACK is received"). Reviewed-by: Uwe Kleine-König Reported-by: Hein Tibosch Signed-off-by: Grygorii Strashko Signed-off-by: Wolfram Sang Cc: stable@kernel.org diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index d15b7c9..01f0cd8 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -407,11 +407,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) if (dev->cmd_err & DAVINCI_I2C_STR_NACK) { if (msg->flags & I2C_M_IGNORE_NAK) return msg->len; - if (stop) { - w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); - w |= DAVINCI_I2C_MDR_STP; - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); - } + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); return -EREMOTEIO; } return -EIO; -- cgit v0.10.2 From 681d15a0f527af7ab3a783e1037de86fbcb136ac Mon Sep 17 00:00:00 2001 From: Vishnu Motghare Date: Wed, 3 Dec 2014 18:05:25 +0530 Subject: i2c: cadence: Set the hardware time-out register to maximum value Cadence I2C controller has bug wherein it generates invalid read transactions after timeout in master receiver mode. This driver does not use the HW timeout and this interrupt is disabled but the feature itself cannot be disabled. Hence, this patch writes the maximum value (0xFF) to this register. This is one of the workarounds to this bug and it will not avoid the issue completely but reduces the chances of error. Signed-off-by: Vishnu Motghare Signed-off-by: Harini Katakam Signed-off-by: Wolfram Sang Cc: stable@kernel.org diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 63f3f03..c604f4c 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -111,6 +111,8 @@ #define CDNS_I2C_DIVA_MAX 4 #define CDNS_I2C_DIVB_MAX 64 +#define CDNS_I2C_TIMEOUT_MAX 0xFF + #define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset) #define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset) @@ -852,6 +854,15 @@ static int cdns_i2c_probe(struct platform_device *pdev) goto err_clk_dis; } + /* + * Cadence I2C controller has a bug wherein it generates + * invalid read transaction after HW timeout in master receiver mode. + * HW timeout is not used by this driver and the interrupt is disabled. + * But the feature itself cannot be disabled. Hence maximum value + * is written to this register to reduce the chances of error. + */ + cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET); + dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n", id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq); -- cgit v0.10.2 From f8781db8aeb18d34edb191bfed9c0a68affaaa74 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Thu, 27 Nov 2014 22:02:42 +0100 Subject: ASoC: Augment existing card DAPM routes in snd_soc_of_parse_audio_routing If a snd_soc_card has any DAPM routes when it calls snd_soc_of_parse_audio_routing, those are clobbered without this change. Signed-off-by: Peter Rosin Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 443be00..b6e0e31 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4585,7 +4585,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { struct device_node *np = card->dev->of_node; - int num_routes; + int num_routes, old_routes; struct snd_soc_dapm_route *routes; int i, ret; @@ -4603,7 +4603,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes), + old_routes = card->num_dapm_routes; + routes = devm_kzalloc(card->dev, + (old_routes + num_routes) * sizeof(*routes), GFP_KERNEL); if (!routes) { dev_err(card->dev, @@ -4611,9 +4613,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } + memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes)); + for (i = 0; i < num_routes; i++) { ret = of_property_read_string_index(np, propname, - 2 * i, &routes[i].sink); + 2 * i, &routes[old_routes + i].sink); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -4621,7 +4625,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } ret = of_property_read_string_index(np, propname, - (2 * i) + 1, &routes[i].source); + (2 * i) + 1, &routes[old_routes + i].source); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -4630,7 +4634,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes = num_routes; + card->num_dapm_routes += num_routes; card->dapm_routes = routes; return 0; -- cgit v0.10.2 From 36fba62cce8184ea6a0cecfa43f07f712716a6f8 Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Wed, 3 Dec 2014 10:13:43 +0800 Subject: ASoC: soc-pcm: do not hw_free BE if it's still used Do not free BE hw if it's still used by other FE during dpcm runtime shutdown. Otherwise the BE runtime state will be STATE_HW_FREE and won't be updated to STATE_CLOSE when shutdown ends, because BE dai shutdown function won't close pcm when detecting BE is still under use. With STATE_HW_FREE, BE can't be triggered start again. This corner case can easily appear when one BE is used by two FE, without this patch "ASoC: dpcm: Fix race between FE/BE updates and trigger"(ea9d0d771fcd32cd56070819749477d511ec9117). One FE tries to shutdown but it's raced against xrun on another FE. It improves the be dai hw_free logic. Signed-off-by: Qiao Zhou Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 002311a..8435719 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1641,6 +1641,10 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) continue; + /* do not free hw if this BE is used by other FE */ + if (be->dpcm[stream].users > 1) + continue; + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && -- cgit v0.10.2 From 2ffa531078037a002862d4befb14bc31aff5900d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 1 Dec 2014 19:57:14 -0200 Subject: ASoC: fsl_ssi: Fix module unbound Trying to remove the snd-soc-fsl-ssi module leads to the following warning: [ 31.515336] ------------[ cut here ]------------ [ 31.520091] WARNING: CPU: 2 PID: 434 at fs/proc/generic.c:521 remove_proc_entry+0x14c/0x16c() [ 31.528708] remove_proc_entry: removing non-empty directory 'irq/79', leaking at least '202c000.ss' [ 31.537911] Modules linked in: snd_soc_wm8962 snd_soc_imx_wm8962 snd_soc_fsl_ssi(-) evbug [ 31.546249] CPU: 2 PID: 434 Comm: rmmod Not tainted 3.18.0-rc6-00028-g3314bf6-dirty #1 [ 31.554235] Backtrace: [ 31.556816] [<80011ea8>] (dump_backtrace) from [<80012044>] (show_stack+0x18/0x1c) [ 31.564416] r6:80142c88 r5:00000000 r4:00000000 r3:00000000 [ 31.570267] [<8001202c>] (show_stack) from [<806980ec>] (dump_stack+0x88/0xa4) [ 31.577588] [<80698064>] (dump_stack) from [<80029d78>] (warn_slowpath_common+0x70/0x94) [ 31.585711] r5:00000009 r4:bb61fd90 [ 31.589423] [<80029d08>] (warn_slowpath_common) from [<80029e40>] (warn_slowpath_fmt+0x38/0x40) [ 31.598187] r8:bb61fdfe r7:be05d76d r6:be05d9a8 r5:00000002 r4:be05d700 [ 31.605054] [<80029e0c>] (warn_slowpath_fmt) from [<80142c88>] (remove_proc_entry+0x14c/0x16c) [ 31.613709] r3:806a79c0 r2:808229a0 [ 31.617371] [<80142b3c>] (remove_proc_entry) from [<80070380>] (unregister_irq_proc+0x94/0xb8) [ 31.625989] r10:00000000 r8:8000ede4 r7:80955f2c r6:0000004f r5:8118e738 r4:be00af00 [ 31.633952] [<800702ec>] (unregister_irq_proc) from [<80069dac>] (free_desc+0x2c/0x64) [ 31.641898] r6:0000004f r5:80955f38 r4:be00af00 [ 31.646604] [<80069d80>] (free_desc) from [<80069e68>] (irq_free_descs+0x4c/0x8c) [ 31.654092] r7:00000081 r6:00000001 r5:0000004f r4:00000001 [ 31.659863] [<80069e1c>] (irq_free_descs) from [<8006fc3c>] (irq_dispose_mapping+0x40/0x5c) [ 31.668247] r6:be17b844 r5:be17b800 r4:0000004f r3:802c5ec0 [ 31.673998] [<8006fbfc>] (irq_dispose_mapping) from [<7f004ea4>] (fsl_ssi_remove+0x58/0x70 [snd_so) [ 31.683948] r4:bb5bba10 r3:00000001 [ 31.687618] [<7f004e4c>] (fsl_ssi_remove [snd_soc_fsl_ssi]) from [<803720a0>] (platform_drv_remove) [ 31.697564] r5:7f0064f8 r4:be17b810 [ 31.701195] [<80372080>] (platform_drv_remove) from [<80370494>] (__device_release_driver+0x78/0xc) [ 31.710361] r5:7f0064f8 r4:be17b810 [ 31.713987] [<8037041c>] (__device_release_driver) from [<80370d20>] (driver_detach+0xbc/0xc0) [ 31.722631] r5:7f0064f8 r4:be17b810 [ 31.726259] [<80370c64>] (driver_detach) from [<80370304>] (bus_remove_driver+0x54/0x98) [ 31.734382] r6:00000800 r5:00000000 r4:7f0064f8 r3:bb67f500 [ 31.740149] [<803702b0>] (bus_remove_driver) from [<80371398>] (driver_unregister+0x30/0x50) [ 31.748617] r4:7f0064f8 r3:bd9f7080 [ 31.752245] [<80371368>] (driver_unregister) from [<80371f3c>] (platform_driver_unregister+0x14/0x) [ 31.761498] r4:7f00655c r3:7f005a70 [ 31.765130] [<80371f28>] (platform_driver_unregister) from [<7f005a84>] (fsl_ssi_driver_exit+0x14/) [ 31.776147] [<7f005a70>] (fsl_ssi_driver_exit [snd_soc_fsl_ssi]) from [<8008ed80>] (SyS_delete_mod) [ 31.786553] [<8008ec64>] (SyS_delete_module) from [<8000ec20>] (ret_fast_syscall+0x0/0x48) [ 31.794824] r6:00c46d18 r5:00000800 r4:00c46d18 [ 31.799530] ---[ end trace 954e8a3a15379e52 ]--- The cause of problem and solution are well explained by Lars-Peter: "The driver creates the mapping by calling irq_of_parse_and_map(), so it also has to dispose the mapping. But the easy way out is to simply use platform_get_irq() instead of irq_of_parse_map(). In this case the mapping is not managed by the device but by the of core, so the device has not to dispose the mapping." Tested on a imx6q-sabresd board. Reported-by: Jiada Wang Suggested-by: Lars-Peter Clausen Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index bc19849..ad12d4c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1361,7 +1361,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) return PTR_ERR(ssi_private->regs); } - ssi_private->irq = irq_of_parse_and_map(np, 0); + ssi_private->irq = platform_get_irq(pdev, 0); if (!ssi_private->irq) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); return -ENXIO; @@ -1387,7 +1387,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (ssi_private->soc->imx) { ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem); if (ret) - goto error_irqmap; + return ret; } ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component, @@ -1458,10 +1458,6 @@ error_asoc_register: if (ssi_private->soc->imx) fsl_ssi_imx_clean(pdev, ssi_private); -error_irqmap: - if (ssi_private->use_dma) - irq_dispose_mapping(ssi_private->irq); - return ret; } @@ -1478,9 +1474,6 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (ssi_private->soc->imx) fsl_ssi_imx_clean(pdev, ssi_private); - if (ssi_private->use_dma) - irq_dispose_mapping(ssi_private->irq); - return 0; } -- cgit v0.10.2 From 4c9a8845f95e852a21fe6cffbd7912107d71619c Mon Sep 17 00:00:00 2001 From: Jiada Wang Date: Tue, 2 Dec 2014 14:55:06 +0900 Subject: ASoC: fsl_ssi: fix error path in probe SSI component isn't unregistered if fsl_ssi_debugfs_create() fails in probe phase. To fix it, this commit replaces label error_asoc_register with error_irq. Signed-off-by: Jiada Wang Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index ad12d4c..7dee341 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1410,7 +1410,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev); if (ret) - goto error_asoc_register; + goto error_irq; /* * If codec-handle property is missing from SSI node, we assume -- cgit v0.10.2 From d0747f10ed5fec3d1f40c4b350dc9673011fc8e2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 4 Dec 2014 14:42:25 -0800 Subject: uapi: fix to export linux/vm_sockets.h A typo "header=y" was introduced by commit 7071cf7fc435 ("uapi: add missing network related headers to kbuild"). Signed-off-by: Masahiro Yamada Cc: Stephen Hemminger Cc: David Miller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 4c94f31..8523f9b 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -427,7 +427,7 @@ header-y += virtio_net.h header-y += virtio_pci.h header-y += virtio_ring.h header-y += virtio_rng.h -header=y += vm_sockets.h +header-y += vm_sockets.h header-y += vt.h header-y += wait.h header-y += wanrouter.h -- cgit v0.10.2 From 40e3262e425a04743f2a579a379f2f189f084580 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Thu, 4 Dec 2014 17:00:13 -0800 Subject: ASoC: rt5677: make volume TLV closer to reality The volume blocks have an step of 0.375dB, but TLV uses 0.01dB for units. Only use the resolution supported, ignoring the LSB of the volume register. This results in half the steps and 0.75dB per step, but reports accurate levels through TLV. Update the masks to reflect that these are registers have the LSB ignored. Signed-off-by: Dylan Reid Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 133010d..81fe146 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -763,9 +763,9 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) } static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); -static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); -static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0); @@ -817,13 +817,13 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = { /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL, - RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv), + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv), SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL, - RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv), + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv), SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL, - RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv), + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv), SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL, - RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv), + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv), /* IN1/IN2 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv), @@ -842,19 +842,19 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = { RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("ADC1 Capture Volume", RT5677_STO1_ADC_DIG_VOL, - RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, adc_vol_tlv), SOC_DOUBLE_TLV("ADC2 Capture Volume", RT5677_STO2_ADC_DIG_VOL, - RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, adc_vol_tlv), SOC_DOUBLE_TLV("ADC3 Capture Volume", RT5677_STO3_ADC_DIG_VOL, - RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, adc_vol_tlv), SOC_DOUBLE_TLV("ADC4 Capture Volume", RT5677_STO4_ADC_DIG_VOL, - RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, adc_vol_tlv), SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5677_MONO_ADC_DIG_VOL, - RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0, + RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 63, 0, adc_vol_tlv), /* Sidetone Control */ diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index dbd9ffd..c0a625f 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -306,10 +306,10 @@ #define RT5677_R_MUTE_SFT 7 #define RT5677_VOL_R_MUTE (0x1 << 6) #define RT5677_VOL_R_SFT 6 -#define RT5677_L_VOL_MASK (0x3f << 8) -#define RT5677_L_VOL_SFT 8 -#define RT5677_R_VOL_MASK (0x3f) -#define RT5677_R_VOL_SFT 0 +#define RT5677_L_VOL_MASK (0x7f << 9) +#define RT5677_L_VOL_SFT 9 +#define RT5677_R_VOL_MASK (0x7f << 1) +#define RT5677_R_VOL_SFT 1 /* LOUT1 Control (0x01) */ #define RT5677_LOUT1_L_MUTE (0x1 << 15) @@ -447,16 +447,16 @@ #define RT5677_SEL_DAC2_R_SRC_SFT 0 /* Stereo1 ADC Digital Volume Control (0x1c) */ -#define RT5677_STO1_ADC_L_VOL_MASK (0x7f << 8) -#define RT5677_STO1_ADC_L_VOL_SFT 8 -#define RT5677_STO1_ADC_R_VOL_MASK (0x7f) -#define RT5677_STO1_ADC_R_VOL_SFT 0 +#define RT5677_STO1_ADC_L_VOL_MASK (0x3f << 9) +#define RT5677_STO1_ADC_L_VOL_SFT 9 +#define RT5677_STO1_ADC_R_VOL_MASK (0x3f << 1) +#define RT5677_STO1_ADC_R_VOL_SFT 1 /* Mono ADC Digital Volume Control (0x1d) */ -#define RT5677_MONO_ADC_L_VOL_MASK (0x7f << 8) -#define RT5677_MONO_ADC_L_VOL_SFT 8 -#define RT5677_MONO_ADC_R_VOL_MASK (0x7f) -#define RT5677_MONO_ADC_R_VOL_SFT 0 +#define RT5677_MONO_ADC_L_VOL_MASK (0x3f << 9) +#define RT5677_MONO_ADC_L_VOL_SFT 9 +#define RT5677_MONO_ADC_R_VOL_MASK (0x3f << 1) +#define RT5677_MONO_ADC_R_VOL_SFT 1 /* Stereo 1/2 ADC Boost Gain Control (0x1e) */ #define RT5677_STO1_ADC_L_BST_MASK (0x3 << 14) -- cgit v0.10.2 From 5476b2b77dae50c88aa6a85f21abeac38dde590f Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Fri, 17 Oct 2014 21:42:53 +0530 Subject: watchdog: s3c2410_wdt: Fix the mask bit offset for Exynos7 The watchdog mask bit offset listed for Exynos7 is incorrect. Fix this. Signed-off-by: Abhilash Kesavan Acked-by: Naveen Krishna Chatradhi Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 8532c3e..1626dc6 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -161,7 +161,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { static const struct s3c2410_wdt_variant drv_data_exynos7 = { .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, - .mask_bit = 0, + .mask_bit = 23, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 23, /* A57 WDTRESET */ .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, -- cgit v0.10.2 From ba56447c3586465fd6eaf9869410dafb748a4d0d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Dec 2014 19:58:32 +0000 Subject: ASoC: samsung: Fix error handling for clock lookup Return the error code we got from clk_get() and check to make sure that clk_prepare_enable() worked. Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 0d76bc1..c60ab07 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -972,6 +972,7 @@ 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; + int ret; if (other && other->clk) { /* If this is probe on secondary */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, @@ -989,9 +990,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (IS_ERR(i2s->clk)) { dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n"); iounmap(i2s->addr); - return -ENOENT; + return PTR_ERR(i2s->clk); + } + + ret = clk_prepare_enable(i2s->clk); + if (ret != 0) { + dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; } - clk_prepare_enable(i2s->clk); samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); -- cgit v0.10.2 From 4e7d606cd52aa8997805b44a50065a7f74cd2953 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:07:47 +0000 Subject: ASoC: rsnd: add salvage support for under/over flow error on SSI L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SSI. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5af016e..6f7080b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -202,14 +202,14 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, } cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? - DMEN : /* DMA : use DMA */ - UIEN | OIEN | DIEN; /* PIO : enable interrupt */ + DMEN : /* DMA : enable DMA */ + DIEN; /* PIO : enable Data interrupt */ cr = ssi->cr_own | ssi->cr_clk | cr_mode | - EN; + UIEN | OIEN | EN; rsnd_mod_write(&ssi->mod, SSICR, cr); @@ -355,22 +355,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) /* * SSI PIO */ +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + + rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); + + rsnd_ssi_hw_start(ssi, rdai, io); + + rsnd_src_ssi_irq_enable(mod, rdai); + + return 0; +} + +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + rsnd_src_ssi_irq_disable(mod, rdai); + + rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); + + rsnd_ssi_hw_stop(ssi, rdai); + + rsnd_src_ssiu_stop(mod, rdai); + + return 0; +} + static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) { struct rsnd_ssi *ssi = data; + struct rsnd_dai *rdai = ssi->rdai; struct rsnd_mod *mod = &ssi->mod; struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); u32 status = rsnd_mod_read(mod, SSISR); - irqreturn_t ret = IRQ_NONE; - if (io && (status & DIRQ)) { - struct rsnd_dai *rdai = ssi->rdai; + if (!io) + return IRQ_NONE; + + /* PIO only */ + if (status & DIRQ) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 *buf = (u32 *)(runtime->dma_area + rsnd_dai_pointer_offset(io, 0)); - rsnd_ssi_record_error(ssi, status); - /* * 8/16/32 data can be assesse to TDR/RDR register * directly as 32bit data @@ -382,11 +414,26 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) *buf = rsnd_mod_read(mod, SSIRDR); rsnd_dai_pointer_update(io, sizeof(*buf)); + } - ret = IRQ_HANDLED; + /* PIO / DMA */ + if (status & (UIRQ | OIRQ)) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + /* + * restart SSI + */ + rsnd_ssi_stop(mod, rdai); + rsnd_ssi_start(mod, rdai); + + dev_dbg(dev, "%s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); } - return ret; + rsnd_ssi_record_error(ssi, status); + + return IRQ_HANDLED; } static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, @@ -412,37 +459,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, return ret; } -static int rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - - rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); - - rsnd_ssi_hw_start(ssi, rdai, io); - - rsnd_src_ssi_irq_enable(mod, rdai); - - return 0; -} - -static int rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - rsnd_src_ssi_irq_disable(mod, rdai); - - rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - - rsnd_ssi_hw_stop(ssi, rdai); - - rsnd_src_ssiu_stop(mod, rdai); - - return 0; -} - static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, .probe = rsnd_ssi_pio_probe, @@ -461,17 +477,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, int dma_id = ssi->info->dma_id; int ret; + ret = devm_request_irq(dev, ssi->info->pio_irq, + rsnd_ssi_pio_interrupt, + IRQF_SHARED, + dev_name(dev), ssi); + if (ret) + goto rsnd_ssi_dma_probe_fail; + ret = rsnd_dma_init( priv, rsnd_mod_to_dma(mod), rsnd_info_is_playback(priv, ssi), dma_id); + if (ret) + goto rsnd_ssi_dma_probe_fail; - if (ret < 0) - dev_err(dev, "%s[%d] (DMA) is failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - else - dev_dbg(dev, "%s[%d] (DMA) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_dbg(dev, "%s[%d] (DMA) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return ret; + +rsnd_ssi_dma_probe_fail: + dev_err(dev, "%s[%d] (DMA) is failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -479,8 +506,16 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct device *dev = rsnd_priv_to_dev(priv); + int irq = ssi->info->pio_irq; + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); + /* PIO will request IRQ again */ + devm_free_irq(dev, irq, ssi); + return 0; } -- cgit v0.10.2 From 6cfad789610342443923737a9def8b637151dc4d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Nov 2014 08:08:10 +0000 Subject: ASoC: rsnd: rename SSI function name of PIO Current R-Car sound SSI PIO/DMA mode are using interrupt. it is no longer "xxx_pio_xxx", rename it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index d76412b..83284ca 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,14 +36,14 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */ -#define RSND_SSI(_dma_id, _pio_irq, _flags) \ -{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI(_dma_id, _irq, _flags) \ +{ .dma_id = _dma_id, .irq = _irq, .flags = _flags } #define RSND_SSI_UNUSED \ -{ .dma_id = -1, .pio_irq = -1, .flags = 0 } +{ .dma_id = -1, .irq = -1, .flags = 0 } struct rsnd_ssi_platform_info { int dma_id; - int pio_irq; + int irq; u32 flags; }; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 6f7080b..3844fbe 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -82,7 +82,7 @@ struct rsnd_ssi { #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) -#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) #define rsnd_ssi_dma_available(ssi) \ rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) @@ -352,9 +352,6 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) } } -/* - * SSI PIO - */ static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai *rdai) { @@ -386,7 +383,7 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, return 0; } -static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) +static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) { struct rsnd_ssi *ssi = data; struct rsnd_dai *rdai = ssi->rdai; @@ -436,17 +433,19 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) return IRQ_HANDLED; } +/* + * SSI PIO + */ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int irq = ssi->info->pio_irq; int ret; - ret = devm_request_irq(dev, irq, - rsnd_ssi_pio_interrupt, + ret = devm_request_irq(dev, ssi->info->irq, + rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); if (ret) @@ -477,8 +476,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, int dma_id = ssi->info->dma_id; int ret; - ret = devm_request_irq(dev, ssi->info->pio_irq, - rsnd_ssi_pio_interrupt, + ret = devm_request_irq(dev, ssi->info->irq, + rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); if (ret) @@ -509,7 +508,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); - int irq = ssi->info->pio_irq; + int irq = ssi->info->irq; rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); @@ -680,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, /* * irq */ - ssi_info->pio_irq = irq_of_parse_and_map(np, 0); + ssi_info->irq = irq_of_parse_and_map(np, 0); /* * DMA -- cgit v0.10.2 From b2776bf7149bddd1f4161f14f79520f17fc1d71d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 7 Dec 2014 14:21:05 -0800 Subject: Linux 3.18 diff --git a/Makefile b/Makefile index ce70361..fd80c6e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 18 SUBLEVEL = 0 -EXTRAVERSION = -rc7 +EXTRAVERSION = NAME = Diseased Newt # *DOCUMENTATION* -- cgit v0.10.2 From 3f024980fbf39ef6448e53345d51fc59f1da08ae Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Dec 2014 19:56:17 +0000 Subject: ASoC: samsung: Fix non-DT use of I2S controller The changes in commit a5a56871f804e (ASoC: samsung: add support for exynos7 I2S controller) introduce a new variant_regs structure in the driver data which is now mandatory for accessing registers. Unfortunately this is only hooked up for DT platforms so non-DT platforms like my primary development platform for audio are broken by this change and crash on boot. Since the only non-DT user of these device is s3c64xx fix this by making the standard samsung-i2s device be of type I2Sv3 and add a new I2Sv4 name to the platform data section, currently using the I2Sv5 information which should be about right. Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index c60ab07..c7aafcd 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1424,10 +1424,13 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = { static struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", - .driver_data = (kernel_ulong_t)&samsung_dai_type_pri, + .driver_data = (kernel_ulong_t)&i2sv3_dai_type, }, { .name = "samsung-i2s-sec", .driver_data = (kernel_ulong_t)&samsung_dai_type_sec, + }, { + .name = "samsung-i2sv4", + .driver_data = (kernel_ulong_t)&i2sv5_dai_type, }, {}, }; -- cgit v0.10.2 From 47370022d2ca552ab524bb14211fefa3a2518ba8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Dec 2014 20:06:31 +0000 Subject: ASoC: wm5102: Initialize dac_comp_lock mutex Commit d74bcaaeb6682 (ASoC: wm5102: Move ultrasonic response settings lock to the driver level) created a driver local mutex for protecting the ultrasonic response settings but neglected to initialize that mutex, causing loud complaints from lockep and potential runtime failures. Fix this by initializing the mutex. Signed-off-by: Mark Brown Acked-by: Charles Keepax diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 1f75534..d78fb8d 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1900,6 +1900,8 @@ static int wm5102_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm5102); + mutex_init(&arizona->dac_comp_lock); + wm5102->core.arizona = arizona; wm5102->core.num_inputs = 6; -- cgit v0.10.2