From 38784764bbcdfedd7c9b220d6d593d281e52c059 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 Jun 2014 20:38:27 +0200 Subject: [PATCH 01/14] ASoC: pxa: add I2C dependencies as needed We have in the past added 'depends on I2C' for some of the PXA boards after hitting randconfig build bugs. I have seens a couple of new bugs in this area during the linux-next cycle for 3.16, after it became possible to build some more PXA machines with I2C disabled. To shut this up for good, this adds the dependency to every board that uses I2C as the interface to the codec. I have gone through all board files and verified that they all either use AC97 or I2C, and this annotates the latter. Some of these already enable I2C from mach-pxa/Kconfig, but since that can change it's better to be explicit here. The link error that can result otherwise happens when CONFIG_I2C is set to 'm' and the codec driver is built-in as a result of being selected by the platform specific glue. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 6473052b6899..ea6372b15dd5 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -40,7 +40,7 @@ config SND_MMP_SOC_SSPA config SND_PXA2XX_SOC_CORGI tristate "SoC Audio support for Sharp Zaurus SL-C7x0" - depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx + depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C select SND_PXA2XX_SOC_I2S select SND_SOC_WM8731 help @@ -49,7 +49,7 @@ config SND_PXA2XX_SOC_CORGI config SND_PXA2XX_SOC_SPITZ tristate "SoC Audio support for Sharp Zaurus SL-Cxx00" - depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00 + depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00 && I2C select SND_PXA2XX_SOC_I2S select SND_SOC_WM8750 help @@ -58,7 +58,7 @@ config SND_PXA2XX_SOC_SPITZ config SND_PXA2XX_SOC_Z2 tristate "SoC Audio support for Zipit Z2" - depends on SND_PXA2XX_SOC && MACH_ZIPIT2 + depends on SND_PXA2XX_SOC && MACH_ZIPIT2 && I2C select SND_PXA2XX_SOC_I2S select SND_SOC_WM8750 help @@ -66,7 +66,7 @@ config SND_PXA2XX_SOC_Z2 config SND_PXA2XX_SOC_POODLE tristate "SoC Audio support for Poodle" - depends on SND_PXA2XX_SOC && MACH_POODLE + depends on SND_PXA2XX_SOC && MACH_POODLE && I2C select SND_PXA2XX_SOC_I2S select SND_SOC_WM8731 help @@ -181,7 +181,7 @@ config SND_PXA2XX_SOC_HX4700 config SND_PXA2XX_SOC_MAGICIAN tristate "SoC Audio support for HTC Magician" - depends on SND_PXA2XX_SOC && MACH_MAGICIAN + depends on SND_PXA2XX_SOC && MACH_MAGICIAN && I2C select SND_PXA2XX_SOC_I2S select SND_PXA_SOC_SSP select SND_SOC_UDA1380 From 5ab0862e5df95b081bea0205464659baa26b43fe Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 5 Jun 2014 12:40:04 +0200 Subject: [PATCH 02/14] ASoC: MMP audio needs sram support From e7a94bb7fb871c73cc85712d89c1f48d0271c1be Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 5 Jun 2014 12:31:28 +0200 Subject: [PATCH] ASoC: MMP audio needs sram support Building the pxa/mmp audio driver without support for the mmp sram driver enabled results in this link error: sound/built-in.o: In function `mmp_pcm_free_dma_buffers': :(.text+0x3e734): undefined reference to `sram_get_gpool' sound/built-in.o: In function `mmp_pcm_new': :(.text+0x3e7c0): undefined reference to `sram_get_gpool' The sram driver is cannot be manually enabled and needs to be turned on by selecting MMP_SRAM from each module that needs it, which is what this patch does. Ideally, MMP should move over to the generic SRAM support, but for the moment, we can avoid the build error. Signed-off-by: Arnd Bergmann Cc: Eric Miao Cc: Haojian Zhuang Cc: Qiao Zhou Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index ea6372b15dd5..a3a7385c969d 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -11,6 +11,7 @@ config SND_PXA2XX_SOC config SND_MMP_SOC bool "Soc Audio for Marvell MMP chips" depends on ARCH_MMP + select MMP_SRAM select SND_SOC_GENERIC_DMAENGINE_PCM select SND_ARM help From 6b10998d74398ec8745d54dfdcbcc1eb445a2f9f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 6 Jun 2014 14:09:17 +0200 Subject: [PATCH 03/14] ASoC: sigmadsp: Split regmap and I2C support into separate modules When the SigmaDSP module is built-in, but the I2C core is build as a module we'll get a undefined reference: sound/built-in.o: In function `sigma_action_write_i2c': :(.text+0x5d8d4): undefined reference to `i2c_master_send' This can happen if a audio driver that is using the regmap SigmaDSP interface is built into the kernel, but core I2C support is build as a module. To fix this split the SigmaDSP module into three modules, one module providing the core infrastructure and two small modules implementing the regmap and I2C interfaces. This allows e.g. the core infrastructure and regmap support to be built into the kernel while I2C support can still be build as a module. Fixes: dab464b60 ("ASoC: Add ADAU1361/ADAU1761 audio CODEC support") Reported-by: Arnd Bergmann Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 12 +++++- sound/soc/codecs/Makefile | 4 ++ sound/soc/codecs/sigmadsp-i2c.c | 35 ++++++++++++++++ sound/soc/codecs/sigmadsp-regmap.c | 36 +++++++++++++++++ sound/soc/codecs/sigmadsp.c | 65 +----------------------------- sound/soc/codecs/sigmadsp.h | 20 +++++++++ 6 files changed, 107 insertions(+), 65 deletions(-) create mode 100644 sound/soc/codecs/sigmadsp-i2c.c create mode 100644 sound/soc/codecs/sigmadsp-regmap.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cbfa1e18f651..0b9571c858f8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -225,11 +225,11 @@ config SND_SOC_ADAU1373 config SND_SOC_ADAU1701 tristate "Analog Devices ADAU1701 CODEC" depends on I2C - select SND_SOC_SIGMADSP + select SND_SOC_SIGMADSP_I2C config SND_SOC_ADAU17X1 tristate - select SND_SOC_SIGMADSP + select SND_SOC_SIGMADSP_REGMAP config SND_SOC_ADAU1761 tristate @@ -476,6 +476,14 @@ config SND_SOC_SIGMADSP tristate select CRC32 +config SND_SOC_SIGMADSP_I2C + tristate + select SND_SOC_SIGMADSP + +config SND_SOC_SIGMADSP_REGMAP + tristate + select SND_SOC_SIGMADSP + config SND_SOC_SIRF_AUDIO_CODEC tristate "SiRF SoC internal audio codec" select REGMAP_MMIO diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b8d73f..1bd6e1cf6f82 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -77,6 +77,8 @@ snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o snd-soc-sigmadsp-objs := sigmadsp.o +snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o +snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o snd-soc-si476x-objs := si476x.o snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o snd-soc-sn95031-objs := sn95031.o @@ -240,6 +242,8 @@ obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.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 +obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c new file mode 100644 index 000000000000..246081aae8ca --- /dev/null +++ b/sound/soc/codecs/sigmadsp-i2c.c @@ -0,0 +1,35 @@ +/* + * Load Analog Devices SigmaStudio firmware files + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include + +#include "sigmadsp.h" + +static int sigma_action_write_i2c(void *control_data, + const struct sigma_action *sa, size_t len) +{ + return i2c_master_send(control_data, (const unsigned char *)&sa->addr, + len); +} + +int process_sigma_firmware(struct i2c_client *client, const char *name) +{ + struct sigma_firmware ssfw; + + ssfw.control_data = client; + ssfw.write = sigma_action_write_i2c; + + return _process_sigma_firmware(&client->dev, &ssfw, name); +} +EXPORT_SYMBOL(process_sigma_firmware); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("SigmaDSP I2C firmware loader"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c new file mode 100644 index 000000000000..f78ed8d2cfb2 --- /dev/null +++ b/sound/soc/codecs/sigmadsp-regmap.c @@ -0,0 +1,36 @@ +/* + * Load Analog Devices SigmaStudio firmware files + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include + +#include "sigmadsp.h" + +static int sigma_action_write_regmap(void *control_data, + const struct sigma_action *sa, size_t len) +{ + return regmap_raw_write(control_data, be16_to_cpu(sa->addr), + sa->payload, len - 2); +} + +int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap, + const char *name) +{ + struct sigma_firmware ssfw; + + ssfw.control_data = regmap; + ssfw.write = sigma_action_write_regmap; + + return _process_sigma_firmware(dev, &ssfw, name); +} +EXPORT_SYMBOL(process_sigma_firmware_regmap); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("SigmaDSP regmap firmware loader"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 4068f2491232..f2de7e049bc6 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -34,23 +34,6 @@ enum { SIGMA_ACTION_END, }; -struct sigma_action { - u8 instr; - u8 len_hi; - __le16 len; - __be16 addr; - unsigned char payload[]; -} __packed; - -struct sigma_firmware { - const struct firmware *fw; - size_t pos; - - void *control_data; - int (*write)(void *control_data, const struct sigma_action *sa, - size_t len); -}; - static inline u32 sigma_action_len(struct sigma_action *sa) { return (sa->len_hi << 16) | le16_to_cpu(sa->len); @@ -138,7 +121,7 @@ process_sigma_actions(struct sigma_firmware *ssfw) return 0; } -static int _process_sigma_firmware(struct device *dev, +int _process_sigma_firmware(struct device *dev, struct sigma_firmware *ssfw, const char *name) { int ret; @@ -197,50 +180,6 @@ static int _process_sigma_firmware(struct device *dev, return ret; } - -#if IS_ENABLED(CONFIG_I2C) - -static int sigma_action_write_i2c(void *control_data, - const struct sigma_action *sa, size_t len) -{ - return i2c_master_send(control_data, (const unsigned char *)&sa->addr, - len); -} - -int process_sigma_firmware(struct i2c_client *client, const char *name) -{ - struct sigma_firmware ssfw; - - ssfw.control_data = client; - ssfw.write = sigma_action_write_i2c; - - return _process_sigma_firmware(&client->dev, &ssfw, name); -} -EXPORT_SYMBOL(process_sigma_firmware); - -#endif - -#if IS_ENABLED(CONFIG_REGMAP) - -static int sigma_action_write_regmap(void *control_data, - const struct sigma_action *sa, size_t len) -{ - return regmap_raw_write(control_data, be16_to_cpu(sa->addr), - sa->payload, len - 2); -} - -int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap, - const char *name) -{ - struct sigma_firmware ssfw; - - ssfw.control_data = regmap; - ssfw.write = sigma_action_write_regmap; - - return _process_sigma_firmware(dev, &ssfw, name); -} -EXPORT_SYMBOL(process_sigma_firmware_regmap); - -#endif +EXPORT_SYMBOL_GPL(_process_sigma_firmware); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h index e439cbd7af7d..c47cd23e9827 100644 --- a/sound/soc/codecs/sigmadsp.h +++ b/sound/soc/codecs/sigmadsp.h @@ -12,6 +12,26 @@ #include #include +struct sigma_action { + u8 instr; + u8 len_hi; + __le16 len; + __be16 addr; + unsigned char payload[]; +} __packed; + +struct sigma_firmware { + const struct firmware *fw; + size_t pos; + + void *control_data; + int (*write)(void *control_data, const struct sigma_action *sa, + size_t len); +}; + +int _process_sigma_firmware(struct device *dev, + struct sigma_firmware *ssfw, const char *name); + struct i2c_client; extern int process_sigma_firmware(struct i2c_client *client, const char *name); From 18626c7ebc05e6486712cc129d8da83d07da9dc7 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 9 Jun 2014 14:20:29 +0300 Subject: [PATCH 04/14] ASoC: dapm: Make sure register value is in sync with DAPM kcontrol state Commit c9e065c27fe9 ("ASoC: dapm: Make sure to always update the DAPM graph in _put_volsw()") stopped updating register values in those cases where initial after boot state of kcontrol appears to not change but where register value still needs update because it is not in sync with the kcontrol state. Fix this by doing snd_soc_test_bits() unconditionally as it was before but by using separate flags for kcontrol and register state changes. This allow both DAPM graph to be updated when disabling auto-muted control and update register if it is out-of-sync in respect of kcontrol state. Signed-off-by: Jarkko Nikula Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a74b9bf23d9f..cdc837ed144d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2755,7 +2755,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; unsigned int val; - int connect, change; + int connect, change, reg_change = 0; struct snd_soc_dapm_update update; int ret = 0; @@ -2773,20 +2773,23 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); change = dapm_kcontrol_set_value(kcontrol, val); - if (change) { - if (reg != SND_SOC_NOPM) { - mask = mask << shift; - val = val << shift; - if (snd_soc_test_bits(codec, reg, mask, val)) { - update.kcontrol = kcontrol; - update.reg = reg; - update.mask = mask; - update.val = val; - card->update = &update; - } + if (reg != SND_SOC_NOPM) { + mask = mask << shift; + val = val << shift; + reg_change = snd_soc_test_bits(codec, reg, mask, val); + } + + if (change || reg_change) { + if (reg_change) { + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; + card->update = &update; } + change |= reg_change; ret = soc_dapm_mixer_update_power(card, kcontrol, connect); From e9b383dc940f4cba6876887ecb47df3082ec925e Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 6 May 2014 16:41:39 +0800 Subject: [PATCH 05/14] ASoC: fsl_spdif: Fix incorrect usage of regmap_read() We should not copy the return value into this val since it's supposed to get the value of the register not the success result of regmap_read(). Thus fix it. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index b912d45a2a4c..9ea2dd471172 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -762,7 +762,7 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol, struct regmap *regmap = spdif_priv->regmap; u32 val; - val = regmap_read(regmap, REG_SPDIF_SIS, &val); + regmap_read(regmap, REG_SPDIF_SIS, &val); ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0; regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD); From c89c7e94bb7d89b39471c79034e3ba1b25d817f5 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 9 Jun 2014 19:16:43 +0300 Subject: [PATCH 06/14] ASoC: fsl_spdif: Fix integer overflow when calculating divisors The calculation code does u64 = (u32 - u32) * 100000; The 64 bits are of no help here as the type is casted only after the multiplication, and therefore the result may overflow, possibly causing inoptimal or wrong clock setup in an unfortunate case (the maximum result value of the first substraction is currently 47999). Fix the code to cast before multiplication. Signed-off-by: Anssi Hannula Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 9ea2dd471172..d7a60614dd21 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1076,7 +1076,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, goto out; } else if (arate / rate[index] == 1) { /* A little bigger than expect */ - sub = (arate - rate[index]) * 100000; + sub = (u64)(arate - rate[index]) * 100000; do_div(sub, rate[index]); if (sub >= savesub) continue; @@ -1086,7 +1086,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, spdif_priv->txrate[index] = arate; } else if (rate[index] / arate == 1) { /* A little smaller than expect */ - sub = (rate[index] - arate) * 100000; + sub = (u64)(rate[index] - arate) * 100000; do_div(sub, rate[index]); if (sub >= savesub) continue; From 4cf612780cec81317a0278b28679a8b69ea8f09c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 10 Jun 2014 23:41:03 -0700 Subject: [PATCH 07/14] ASoC: rsnd: fixup index of src/dst mod when capture Index of dma name should use -1, not +1 when capture case. Thank you Dan. Reported-by: Dan Carpenter Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 91880156e1ae..4e86265f625c 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -315,7 +315,7 @@ static void rsnd_dma_of_name(struct rsnd_dma *dma, dst_mod = mod[index]; } else { src_mod = mod[index]; - dst_mod = mod[index + 1]; + dst_mod = mod[index - 1]; } index = 0; From 3d5f615f9fcba6df382bd9a204b5e9ad3080ac48 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 11 Jun 2014 00:13:52 -0700 Subject: [PATCH 08/14] ASoC: fsl: Fix build problem Commit 432481220 (ASoC: fsl-ssi: Use regmap) removed struct ccsr_ssi. Unfortunately, the structure is still used. This causes mpc85xx_smp_defconfig and mpc85xx_defconfig builds to fail with sound/soc/fsl/fsl_dma.c:926:50: error: invalid use of undefined type 'struct ccsr_ssi' dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); ound/soc/fsl/fsl_dma.c:927:50: error: invalid use of undefined type 'struct ccsr_ssi' dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); Fix by using constants, similar to original commit. Cc: Markus Pargmann Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 6bb0ea59284f..a609aafc994d 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -923,8 +923,8 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) dma->dai.pcm_free = fsl_dma_free_dma_buffers; /* Store the SSI-specific information that we need */ - dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); - dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); + dma->ssi_stx_phys = res.start + CCSR_SSI_STX0; + dma->ssi_srx_phys = res.start + CCSR_SSI_SRX0; iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); if (iprop) From 74b0c2d75fb4cc89173944e6d8f9eb47aca0c343 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Jun 2014 15:14:34 +0200 Subject: [PATCH 09/14] drm/i915, HD-audio: Don't continue probing when nomodeset is given When a machine is booted with nomodeset option, i915 driver skips the whole initialization. Meanwhile, HD-audio tries to bind wth i915 just by request_symbol() without knowing that the initialization was skipped, and eventually it hits WARN_ON() in i915_request_power_well() and i915_release_power_well() wrongly but still continues probing, even though it doesn't work at all. In this patch, both functions are changed to return an error in case of uninitialized state instead of WARN_ON(), so that HD-audio driver can give up HDMI controller initialization at the right time. Acked-by: Daniel Vetter Cc: [3.15] Signed-off-by: Takashi Iwai --- drivers/gpu/drm/i915/intel_pm.c | 14 ++++++++------ include/drm/i915_powerwell.h | 4 ++-- sound/pci/hda/hda_i915.c | 12 ++++++------ sound/pci/hda/hda_i915.h | 4 ++-- sound/pci/hda/hda_intel.c | 7 ++++++- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d1e53abec1b5..6463f0201cf2 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6024,30 +6024,32 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, static struct i915_power_domains *hsw_pwr; /* Display audio driver power well request */ -void i915_request_power_well(void) +int i915_request_power_well(void) { struct drm_i915_private *dev_priv; - if (WARN_ON(!hsw_pwr)) - return; + if (!hsw_pwr) + return -ENODEV; dev_priv = container_of(hsw_pwr, struct drm_i915_private, power_domains); intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); + return 0; } EXPORT_SYMBOL_GPL(i915_request_power_well); /* Display audio driver power well release */ -void i915_release_power_well(void) +int i915_release_power_well(void) { struct drm_i915_private *dev_priv; - if (WARN_ON(!hsw_pwr)) - return; + if (!hsw_pwr) + return -ENODEV; dev_priv = container_of(hsw_pwr, struct drm_i915_private, power_domains); intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); + return 0; } EXPORT_SYMBOL_GPL(i915_release_power_well); diff --git a/include/drm/i915_powerwell.h b/include/drm/i915_powerwell.h index cfdc884405b7..2baba9996094 100644 --- a/include/drm/i915_powerwell.h +++ b/include/drm/i915_powerwell.h @@ -30,7 +30,7 @@ #define _I915_POWERWELL_H_ /* For use by hda_i915 driver */ -extern void i915_request_power_well(void); -extern void i915_release_power_well(void); +extern int i915_request_power_well(void); +extern int i915_release_power_well(void); #endif /* _I915_POWERWELL_H_ */ diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c index 9d07e4edacdb..e9e8a4a4a9a1 100644 --- a/sound/pci/hda/hda_i915.c +++ b/sound/pci/hda/hda_i915.c @@ -22,20 +22,20 @@ #include #include "hda_i915.h" -static void (*get_power)(void); -static void (*put_power)(void); +static int (*get_power)(void); +static int (*put_power)(void); -void hda_display_power(bool enable) +int hda_display_power(bool enable) { if (!get_power || !put_power) - return; + return -ENODEV; pr_debug("HDA display power %s \n", enable ? "Enable" : "Disable"); if (enable) - get_power(); + return get_power(); else - put_power(); + return put_power(); } int hda_i915_init(void) diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h index 5a63da2c53e5..bfd835f8f1aa 100644 --- a/sound/pci/hda/hda_i915.h +++ b/sound/pci/hda/hda_i915.h @@ -17,11 +17,11 @@ #define __SOUND_HDA_I915_H #ifdef CONFIG_SND_HDA_I915 -void hda_display_power(bool enable); +int hda_display_power(bool enable); int hda_i915_init(void); int hda_i915_exit(void); #else -static inline void hda_display_power(bool enable) {} +static inline int hda_display_power(bool enable) { return 0; } static inline int hda_i915_init(void) { return -ENODEV; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index bb65a124e006..23fd6b9aecca 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1656,8 +1656,13 @@ static int azx_probe_continue(struct azx *chip) "Error request power-well from i915\n"); goto out_free; } + err = hda_display_power(true); + if (err < 0) { + dev_err(chip->card->dev, + "Cannot turn on display power on i915\n"); + goto out_free; + } #endif - hda_display_power(true); } err = azx_first_init(chip); From 07f4d9d74a04aa7c72c5dae0ef97565f28f17b92 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 18 Jun 2014 13:32:31 +0200 Subject: [PATCH 10/14] ALSA: control: Protect user controls against concurrent access The user-control put and get handlers as well as the tlv do not protect against concurrent access from multiple threads. Since the state of the control is not updated atomically it is possible that either two write operations or a write and a read operation race against each other. Both can lead to arbitrary memory disclosure. This patch introduces a new lock that protects user-controls from concurrent access. Since applications typically access controls sequentially than in parallel a single lock per card should be fine. Signed-off-by: Lars-Peter Clausen Acked-by: Jaroslav Kysela Cc: Signed-off-by: Takashi Iwai --- include/sound/core.h | 2 ++ sound/core/control.c | 31 +++++++++++++++++++++++++------ sound/core/init.c | 1 + 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/sound/core.h b/include/sound/core.h index eedda2cdfe57..1df3f2fe5350 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -116,6 +116,8 @@ struct snd_card { int user_ctl_count; /* count of all user controls */ struct list_head controls; /* all controls for this card */ struct list_head ctl_files; /* active control files */ + struct mutex user_ctl_lock; /* protects user controls against + concurrent access */ struct snd_info_entry *proc_root; /* root for soundcard specific files */ struct snd_info_entry *proc_id; /* the card id */ diff --git a/sound/core/control.c b/sound/core/control.c index f038f5afafe2..00ab034f5fcb 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -991,6 +991,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, struct user_element { struct snd_ctl_elem_info info; + struct snd_card *card; void *elem_data; /* element data */ unsigned long elem_data_size; /* size of element data in bytes */ void *tlv_data; /* TLV data */ @@ -1034,7 +1035,9 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol, { struct user_element *ue = kcontrol->private_data; + mutex_lock(&ue->card->user_ctl_lock); memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); + mutex_unlock(&ue->card->user_ctl_lock); return 0; } @@ -1043,10 +1046,12 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol, { int change; struct user_element *ue = kcontrol->private_data; - + + mutex_lock(&ue->card->user_ctl_lock); change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; if (change) memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); + mutex_unlock(&ue->card->user_ctl_lock); return change; } @@ -1066,19 +1071,32 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, new_data = memdup_user(tlv, size); if (IS_ERR(new_data)) return PTR_ERR(new_data); + mutex_lock(&ue->card->user_ctl_lock); change = ue->tlv_data_size != size; if (!change) change = memcmp(ue->tlv_data, new_data, size); kfree(ue->tlv_data); ue->tlv_data = new_data; ue->tlv_data_size = size; + mutex_unlock(&ue->card->user_ctl_lock); } else { - if (! ue->tlv_data_size || ! ue->tlv_data) - return -ENXIO; - if (size < ue->tlv_data_size) - return -ENOSPC; + int ret = 0; + + mutex_lock(&ue->card->user_ctl_lock); + if (!ue->tlv_data_size || !ue->tlv_data) { + ret = -ENXIO; + goto err_unlock; + } + if (size < ue->tlv_data_size) { + ret = -ENOSPC; + goto err_unlock; + } if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size)) - return -EFAULT; + ret = -EFAULT; +err_unlock: + mutex_unlock(&ue->card->user_ctl_lock); + if (ret) + return ret; } return change; } @@ -1210,6 +1228,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); if (ue == NULL) return -ENOMEM; + ue->card = card; ue->info = *info; ue->info.access = 0; ue->elem_data = (char *)ue + sizeof(*ue); diff --git a/sound/core/init.c b/sound/core/init.c index 5ee83845c5de..7bdfd19e24a8 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -232,6 +232,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid, INIT_LIST_HEAD(&card->devices); init_rwsem(&card->controls_rwsem); rwlock_init(&card->ctl_files_rwlock); + mutex_init(&card->user_ctl_lock); INIT_LIST_HEAD(&card->controls); INIT_LIST_HEAD(&card->ctl_files); spin_lock_init(&card->files_lock); From 82262a46627bebb0febcc26664746c25cef08563 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 18 Jun 2014 13:32:32 +0200 Subject: [PATCH 11/14] ALSA: control: Fix replacing user controls There are two issues with the current implementation for replacing user controls. The first is that the code does not check if the control is actually a user control and neither does it check if the control is owned by the process that tries to remove it. That allows userspace applications to remove arbitrary controls, which can cause a user after free if a for example a driver does not expect a control to be removed from under its feed. The second issue is that on one hand when a control is replaced the user_ctl_count limit is not checked and on the other hand the user_ctl_count is increased (even though the number of user controls does not change). This allows userspace, once the user_ctl_count limit as been reached, to repeatedly replace a control until user_ctl_count overflows. Once that happens new controls can be added effectively bypassing the user_ctl_count limit. Both issues can be fixed by instead of open-coding the removal of the control that is to be replaced to use snd_ctl_remove_user_ctl(). This function does proper permission checks as well as decrements user_ctl_count after the control has been removed. Note that by using snd_ctl_remove_user_ctl() the check which returns -EBUSY at beginning of the function if the control already exists is removed. This is not a problem though since the check is quite useless, because the lock that is protecting the control list is released between the check and before adding the new control to the list, which means that it is possible that a different control with the same settings is added to the list after the check. Luckily there is another check that is done while holding the lock in snd_ctl_add(), so we'll rely on that to make sure that the same control is not added twice. Signed-off-by: Lars-Peter Clausen Acked-by: Jaroslav Kysela Cc: Signed-off-by: Takashi Iwai --- sound/core/control.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sound/core/control.c b/sound/core/control.c index 00ab034f5fcb..1f413c286511 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1154,8 +1154,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, struct user_element *ue; int idx, err; - if (!replace && card->user_ctl_count >= MAX_USER_CONTROLS) - return -ENOMEM; if (info->count < 1) return -EINVAL; access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : @@ -1164,21 +1162,16 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); info->id.numid = 0; memset(&kctl, 0, sizeof(kctl)); - down_write(&card->controls_rwsem); - _kctl = snd_ctl_find_id(card, &info->id); - err = 0; - if (_kctl) { - if (replace) - err = snd_ctl_remove(card, _kctl); - else - err = -EBUSY; - } else { - if (replace) - err = -ENOENT; + + if (replace) { + err = snd_ctl_remove_user_ctl(file, &info->id); + if (err) + return err; } - up_write(&card->controls_rwsem); - if (err < 0) - return err; + + if (card->user_ctl_count >= MAX_USER_CONTROLS) + return -ENOMEM; + memcpy(&kctl.id, &info->id, sizeof(info->id)); kctl.count = info->owner ? info->owner : 1; access |= SNDRV_CTL_ELEM_ACCESS_USER; From fd9f26e4eca5d08a27d12c0933fceef76ed9663d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 18 Jun 2014 13:32:33 +0200 Subject: [PATCH 12/14] ALSA: control: Don't access controls outside of protected regions A control that is visible on the card->controls list can be freed at any time. This means we must not access any of its memory while not holding the controls_rw_lock. Otherwise we risk a use after free access. Signed-off-by: Lars-Peter Clausen Acked-by: Jaroslav Kysela Cc: Signed-off-by: Takashi Iwai --- sound/core/control.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sound/core/control.c b/sound/core/control.c index 1f413c286511..5c49f976fc7b 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -330,6 +330,7 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) { struct snd_ctl_elem_id id; unsigned int idx; + unsigned int count; int err = -EINVAL; if (! kcontrol) @@ -358,8 +359,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1; card->last_numid += kcontrol->count; + count = kcontrol->count; up_write(&card->controls_rwsem); - for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) + for (idx = 0; idx < count; idx++, id.index++, id.numid++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); return 0; @@ -388,6 +390,7 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace) { struct snd_ctl_elem_id id; + unsigned int count; unsigned int idx; struct snd_kcontrol *old; int ret; @@ -423,8 +426,9 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1; card->last_numid += kcontrol->count; + count = kcontrol->count; up_write(&card->controls_rwsem); - for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) + for (idx = 0; idx < count; idx++, id.index++, id.numid++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); return 0; @@ -897,9 +901,9 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, result = kctl->put(kctl, control); } if (result > 0) { + struct snd_ctl_elem_id id = control->id; up_read(&card->controls_rwsem); - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, - &control->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id); return 0; } } @@ -1333,8 +1337,9 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, } err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); if (err > 0) { + struct snd_ctl_elem_id id = kctl->id; up_read(&card->controls_rwsem); - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id); return 0; } } else { From ac902c112d90a89e59916f751c2745f4dbdbb4bd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 18 Jun 2014 13:32:34 +0200 Subject: [PATCH 13/14] ALSA: control: Handle numid overflow Each control gets automatically assigned its numids when the control is created. The allocation is done by incrementing the numid by the amount of allocated numids per allocation. This means that excessive creation and destruction of controls (e.g. via SNDRV_CTL_IOCTL_ELEM_ADD/REMOVE) can cause the id to eventually overflow. Currently when this happens for the control that caused the overflow kctl->id.numid + kctl->count will also over flow causing it to be smaller than kctl->id.numid. Most of the code assumes that this is something that can not happen, so we need to make sure that it won't happen Signed-off-by: Lars-Peter Clausen Acked-by: Jaroslav Kysela Cc: Signed-off-by: Takashi Iwai --- sound/core/control.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/control.c b/sound/core/control.c index 5c49f976fc7b..8d6e4bae7407 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -288,6 +288,10 @@ static bool snd_ctl_remove_numid_conflict(struct snd_card *card, { struct snd_kcontrol *kctl; + /* Make sure that the ids assigned to the control do not wrap around */ + if (card->last_numid >= UINT_MAX - count) + card->last_numid = 0; + list_for_each_entry(kctl, &card->controls, list) { if (kctl->id.numid < card->last_numid + 1 + count && kctl->id.numid + kctl->count > card->last_numid + 1) { From 883a1d49f0d77d30012f114b2e19fc141beb3e8e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 18 Jun 2014 13:32:35 +0200 Subject: [PATCH 14/14] ALSA: control: Make sure that id->index does not overflow The ALSA control code expects that the range of assigned indices to a control is continuous and does not overflow. Currently there are no checks to enforce this. If a control with a overflowing index range is created that control becomes effectively inaccessible and unremovable since snd_ctl_find_id() will not be able to find it. This patch adds a check that makes sure that controls with a overflowing index range can not be created. Signed-off-by: Lars-Peter Clausen Acked-by: Jaroslav Kysela Cc: Signed-off-by: Takashi Iwai --- sound/core/control.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/core/control.c b/sound/core/control.c index 8d6e4bae7407..f0b0e14497a5 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -342,6 +342,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) if (snd_BUG_ON(!card || !kcontrol->info)) goto error; id = kcontrol->id; + if (id.index > UINT_MAX - kcontrol->count) + goto error; + down_write(&card->controls_rwsem); if (snd_ctl_find_id(card, &id)) { up_write(&card->controls_rwsem);