Merge remote-tracking branch 'asoc/topic/tas5086' into asoc-next
diff --git a/Documentation/devicetree/bindings/sound/adi,adau1701.txt b/Documentation/devicetree/bindings/sound/adi,adau1701.txt
new file mode 100644
index 0000000..547a49b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/adi,adau1701.txt
@@ -0,0 +1,35 @@
+Analog Devices ADAU1701
+
+Required properties:
+
+ - compatible:		Should contain "adi,adau1701"
+ - reg:			The i2c address. Value depends on the state of ADDR0
+			and ADDR1, as wired in hardware.
+
+Optional properties:
+
+ - reset-gpio: 		A GPIO spec to define which pin is connected to the
+			chip's !RESET pin. If specified, the driver will
+			assert a hardware reset at probe time.
+ - adi,pll-mode-gpios:	An array of two GPIO specs to describe the GPIOs
+			the ADAU's PLL config pins are connected to.
+			The state of the pins are set according to the
+			configured clock divider on ASoC side before the
+			firmware is loaded.
+ - adi,pin-config:	An array of 12 numerical values selecting one of the
+			pin configurations as described in the datasheet,
+			table 53. Note that the value of this property has
+			to be prefixed with '/bits/ 8'.
+
+Examples:
+
+	i2c_bus {
+		adau1701@34 {
+			compatible = "adi,adau1701";
+			reg = <0x34>;
+			reset-gpio = <&gpio 23 0>;
+			adi,pll-mode-gpios = <&gpio 24 0 &gpio 25 0>;
+			adi,pin-config = /bits/ 8 <0x4 0x7 0x5 0x5 0x4 0x4
+                                                   0x4 0x4 0x4 0x4 0x4 0x4>;
+		};
+	};
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 85c1522..6eabee7 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -340,7 +340,7 @@
 
 typedef int (*hw_write_t)(void *,const char* ,int);
 
-extern struct snd_ac97_bus_ops soc_ac97_ops;
+extern struct snd_ac97_bus_ops *soc_ac97_ops;
 
 enum snd_soc_control_type {
 	SND_SOC_I2C = 1,
@@ -467,6 +467,8 @@
 	struct snd_ac97_bus_ops *ops, int num);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
+
 /*
  *Controls
  */
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index 44b8dce..d6f7694 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -179,13 +179,12 @@
 }
 
 /* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops ac97c_bus_ops = {
 	.read		= au1xac97c_ac97_read,
 	.write		= au1xac97c_ac97_write,
 	.reset		= au1xac97c_ac97_cold_reset,
 	.warm_reset	= au1xac97c_ac97_warm_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);	/* globals be gone! */
 
 static int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
 				 struct snd_soc_dai *dai)
@@ -272,6 +271,10 @@
 
 	platform_set_drvdata(pdev, ctx);
 
+	ret = snd_soc_set_ac97_ops(&ac97c_bus_ops);
+	if (ret)
+		return ret;
+
 	ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component,
 					 &au1xac97c_dai_driver, 1);
 	if (ret)
@@ -338,19 +341,7 @@
 	.remove		= au1xac97c_drvremove,
 };
 
-static int __init au1xac97c_load(void)
-{
-	ac97c_workdata = NULL;
-	return platform_driver_register(&au1xac97c_driver);
-}
-
-static void __exit au1xac97c_unload(void)
-{
-	platform_driver_unregister(&au1xac97c_driver);
-}
-
-module_init(au1xac97c_load);
-module_exit(au1xac97c_unload);
+module_platform_driver(&au1xac97c_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 8f1862a..a822ab8 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -201,13 +201,12 @@
 }
 
 /* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops psc_ac97_ops = {
 	.read		= au1xpsc_ac97_read,
 	.write		= au1xpsc_ac97_write,
 	.reset		= au1xpsc_ac97_cold_reset,
 	.warm_reset	= au1xpsc_ac97_warm_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 				  struct snd_pcm_hw_params *params,
@@ -383,15 +382,9 @@
 	if (!iores)
 		return -ENODEV;
 
-	if (!devm_request_mem_region(&pdev->dev, iores->start,
-				     resource_size(iores),
-				     pdev->name))
-		return -EBUSY;
-
-	wd->mmio = devm_ioremap(&pdev->dev, iores->start,
-				resource_size(iores));
-	if (!wd->mmio)
-		return -EBUSY;
+	wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
+	if (IS_ERR(wd->mmio))
+		return PTR_ERR(wd->mmio);
 
 	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 	if (!dmares)
@@ -423,6 +416,10 @@
 
 	platform_set_drvdata(pdev, wd);
 
+	ret = snd_soc_set_ac97_ops(&psc_ac97_ops);
+	if (ret)
+		return ret;
+
 	ret = snd_soc_register_component(&pdev->dev, &au1xpsc_ac97_component,
 					 &wd->dai_drv, 1);
 	if (ret)
@@ -503,19 +500,7 @@
 	.remove		= au1xpsc_ac97_drvremove,
 };
 
-static int __init au1xpsc_ac97_load(void)
-{
-	au1xpsc_ac97_workdata = NULL;
-	return platform_driver_register(&au1xpsc_ac97_driver);
-}
-
-static void __exit au1xpsc_ac97_unload(void)
-{
-	platform_driver_unregister(&au1xpsc_ac97_driver);
-}
-
-module_init(au1xpsc_ac97_load);
-module_exit(au1xpsc_ac97_unload);
+module_platform_driver(au1xpsc_ac97_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index 4902173..c5af677 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -198,13 +198,12 @@
 #endif
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops bf5xx_ac97_ops = {
 	.read	= bf5xx_ac97_read,
 	.write	= bf5xx_ac97_write,
 	.warm_reset	= bf5xx_ac97_warm_reset,
 	.reset	= bf5xx_ac97_cold_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 #ifdef CONFIG_PM
 static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
@@ -293,13 +292,14 @@
 
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 	/* Request PB3 as reset pin */
-	if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
-		pr_err("Failed to request GPIO_%d for reset\n",
-				CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-		ret =  -1;
+	ret = devm_gpio_request_one(&pdev->dev,
+				    CONFIG_SND_BF5XX_RESET_GPIO_NUM,
+				    GPIOF_OUT_INIT_HIGH, "SND_AD198x RESET") {
+		dev_err(&pdev->dev,
+			"Failed to request GPIO_%d for reset: %d\n",
+			CONFIG_SND_BF5XX_RESET_GPIO_NUM, ret);
 		goto gpio_err;
 	}
-	gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 #endif
 
 	sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
@@ -335,6 +335,12 @@
 		goto sport_config_err;
 	}
 
+	ret = snd_soc_set_ac97_ops(&bf5xx_ac97_ops);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+		goto sport_config_err;
+	}
+
 	ret = snd_soc_register_component(&pdev->dev, &bfin_ac97_component,
 					 &bfin_ac97_dai, 1);
 	if (ret) {
@@ -349,10 +355,7 @@
 sport_config_err:
 	sport_done(sport_handle);
 sport_err:
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-	gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-gpio_err:
-#endif
+	snd_soc_set_ac97_ops(NULL);
 
 	return ret;
 }
@@ -363,9 +366,7 @@
 
 	snd_soc_unregister_component(&pdev->dev);
 	sport_done(sport_handle);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-	gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
+	snd_soc_set_ac97_ops(NULL);
 
 	return 0;
 }
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index 7798fbd..4bc9490 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -237,13 +237,12 @@
 	return IRQ_HANDLED;
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops ep93xx_ac97_ops = {
 	.read		= ep93xx_ac97_read,
 	.write		= ep93xx_ac97_write,
 	.reset		= ep93xx_ac97_cold_reset,
 	.warm_reset	= ep93xx_ac97_warm_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
 			       int cmd, struct snd_soc_dai *dai)
@@ -395,6 +394,10 @@
 	ep93xx_ac97_info = info;
 	platform_set_drvdata(pdev, info);
 
+	ret = snd_soc_set_ac97_ops(&ep93xx_ac97_ops);
+	if (ret)
+		goto fail;
+
 	ret = snd_soc_register_component(&pdev->dev, &ep93xx_ac97_component,
 					 &ep93xx_ac97_dai, 1);
 	if (ret)
@@ -405,7 +408,7 @@
 fail:
 	platform_set_drvdata(pdev, NULL);
 	ep93xx_ac97_info = NULL;
-	dev_set_drvdata(&pdev->dev, NULL);
+	snd_soc_set_ac97_ops(NULL);
 	return ret;
 }
 
@@ -420,7 +423,8 @@
 
 	platform_set_drvdata(pdev, NULL);
 	ep93xx_ac97_info = NULL;
-	dev_set_drvdata(&pdev->dev, NULL);
+
+	snd_soc_set_ac97_ops(NULL);
 
 	return 0;
 }
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 60159c0..e2bd3dd 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -120,10 +120,8 @@
  * before DAC & PGA in DAPM power-off sequence.
  */
 #define PM860X_DAPM_OUTPUT(wname, wevent)	\
-{	.id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
-	.shift = 0, .invert = 0, .kcontrol_news = NULL, \
-	.num_kcontrols = 0, .event = wevent, \
-	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
+	SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \
+			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD)
 
 struct pm860x_det {
 	struct snd_soc_jack	*hp_jack;
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index ef2ae32..ec73518 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -62,13 +62,13 @@
 static unsigned int ac97_read(struct snd_soc_codec *codec,
 	unsigned int reg)
 {
-	return soc_ac97_ops.read(codec->ac97, reg);
+	return soc_ac97_ops->read(codec->ac97, reg);
 }
 
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 	unsigned int val)
 {
-	soc_ac97_ops.write(codec->ac97, reg, val);
+	soc_ac97_ops->write(codec->ac97, reg, val);
 	return 0;
 }
 
@@ -79,7 +79,8 @@
 	int ret;
 
 	/* add codec as bus device for standard ac97 */
-	ret = snd_ac97_bus(codec->card->snd_card, 0, &soc_ac97_ops, NULL, &ac97_bus);
+	ret = snd_ac97_bus(codec->card->snd_card, 0, soc_ac97_ops, NULL,
+			   &ac97_bus);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index f385342..89fcf7d 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -108,7 +108,7 @@
 	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(codec->ac97, reg);
 	default:
 		reg = reg >> 1;
 
@@ -124,7 +124,7 @@
 {
 	u16 *cache = codec->reg_cache;
 
-	soc_ac97_ops.write(codec->ac97, reg, val);
+	soc_ac97_ops->write(codec->ac97, reg, val);
 	reg = reg >> 1;
 	if (reg < ARRAY_SIZE(ad1980_reg))
 		cache[reg] = val;
@@ -154,13 +154,13 @@
 	u16 retry_cnt = 0;
 
 retry:
-	if (try_warm && soc_ac97_ops.warm_reset) {
-		soc_ac97_ops.warm_reset(codec->ac97);
+	if (try_warm && soc_ac97_ops->warm_reset) {
+		soc_ac97_ops->warm_reset(codec->ac97);
 		if (ac97_read(codec, AC97_RESET) == 0x0090)
 			return 1;
 	}
 
-	soc_ac97_ops.reset(codec->ac97);
+	soc_ac97_ops->reset(codec->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 case the
 	 * first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
@@ -186,7 +186,7 @@
 
 	printk(KERN_INFO "AD1980 SoC Audio Codec\n");
 
-	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
 	if (ret < 0) {
 		printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
 		return ret;
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index dafdbe8..d1124a5 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -13,6 +13,10 @@
 #include <linux/i2c.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -21,16 +25,19 @@
 #include "sigmadsp.h"
 #include "adau1701.h"
 
-#define ADAU1701_DSPCTRL	0x1c
-#define ADAU1701_SEROCTL	0x1e
-#define ADAU1701_SERICTL	0x1f
+#define ADAU1701_DSPCTRL	0x081c
+#define ADAU1701_SEROCTL	0x081e
+#define ADAU1701_SERICTL	0x081f
 
-#define ADAU1701_AUXNPOW	0x22
+#define ADAU1701_AUXNPOW	0x0822
+#define ADAU1701_PINCONF_0	0x0820
+#define ADAU1701_PINCONF_1	0x0821
+#define ADAU1701_AUXNPOW	0x0822
 
-#define ADAU1701_OSCIPOW	0x26
-#define ADAU1701_DACSET		0x27
+#define ADAU1701_OSCIPOW	0x0826
+#define ADAU1701_DACSET		0x0827
 
-#define ADAU1701_NUM_REGS	0x28
+#define ADAU1701_MAX_REGISTER	0x0828
 
 #define ADAU1701_DSPCTRL_CR		(1 << 2)
 #define ADAU1701_DSPCTRL_DAM		(1 << 3)
@@ -84,10 +91,18 @@
 #define ADAU1701_OSCIPOW_OPD		0x04
 #define ADAU1701_DACSET_DACINIT		1
 
+#define ADAU1707_CLKDIV_UNSET		(-1UL)
+
 #define ADAU1701_FIRMWARE "adau1701.bin"
 
 struct adau1701 {
+	int gpio_nreset;
+	int gpio_pll_mode[2];
 	unsigned int dai_fmt;
+	unsigned int pll_clkdiv;
+	unsigned int sysclk;
+	struct regmap *regmap;
+	u8 pin_config[12];
 };
 
 static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -119,10 +134,13 @@
 	{ "ADC", NULL, "IN1" },
 };
 
-static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
+static unsigned int adau1701_register_size(struct device *dev,
 		unsigned int reg)
 {
 	switch (reg) {
+	case ADAU1701_PINCONF_0:
+	case ADAU1701_PINCONF_1:
+		return 3;
 	case ADAU1701_DSPCTRL:
 	case ADAU1701_SEROCTL:
 	case ADAU1701_AUXNPOW:
@@ -133,33 +151,42 @@
 		return 1;
 	}
 
-	dev_err(codec->dev, "Unsupported register address: %d\n", reg);
+	dev_err(dev, "Unsupported register address: %d\n", reg);
 	return 0;
 }
 
-static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
-		unsigned int value)
+static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
 {
+	switch (reg) {
+	case ADAU1701_DACSET:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int adau1701_reg_write(void *context, unsigned int reg,
+			      unsigned int value)
+{
+	struct i2c_client *client = context;
 	unsigned int i;
 	unsigned int size;
-	uint8_t buf[4];
+	uint8_t buf[5];
 	int ret;
 
-	size = adau1701_register_size(codec, reg);
+	size = adau1701_register_size(&client->dev, reg);
 	if (size == 0)
 		return -EINVAL;
 
-	snd_soc_cache_write(codec, reg, value);
-
-	buf[0] = 0x08;
-	buf[1] = reg;
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
 
 	for (i = size + 1; i >= 2; --i) {
 		buf[i] = value;
 		value >>= 8;
 	}
 
-	ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2);
+	ret = i2c_master_send(client, buf, size + 2);
 	if (ret == size + 2)
 		return 0;
 	else if (ret < 0)
@@ -168,21 +195,107 @@
 		return -EIO;
 }
 
-static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
+static int adau1701_reg_read(void *context, unsigned int reg,
+			     unsigned int *value)
 {
-	unsigned int value;
-	unsigned int ret;
+	int ret;
+	unsigned int i;
+	unsigned int size;
+	uint8_t send_buf[2], recv_buf[3];
+	struct i2c_client *client = context;
+	struct i2c_msg msgs[2];
 
-	ret = snd_soc_cache_read(codec, reg, &value);
-	if (ret)
+	size = adau1701_register_size(&client->dev, reg);
+	if (size == 0)
+		return -EINVAL;
+
+	send_buf[0] = reg >> 8;
+	send_buf[1] = reg & 0xff;
+
+	msgs[0].addr = client->addr;
+	msgs[0].len = sizeof(send_buf);
+	msgs[0].buf = send_buf;
+	msgs[0].flags = 0;
+
+	msgs[1].addr = client->addr;
+	msgs[1].len = size;
+	msgs[1].buf = recv_buf;
+	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 value;
+	*value = 0;
+
+	for (i = 0; i < size; i++)
+		*value |= recv_buf[i] << (i * 8);
+
+	return 0;
 }
 
-static int adau1701_load_firmware(struct snd_soc_codec *codec)
+static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
 {
-	return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE);
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+	struct i2c_client *client = to_i2c_client(codec->dev);
+	int ret;
+
+	if (clkdiv != ADAU1707_CLKDIV_UNSET &&
+	    gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
+	    gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+		switch (clkdiv) {
+		case 64:
+			gpio_set_value(adau1701->gpio_pll_mode[0], 0);
+			gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+			break;
+		case 256:
+			gpio_set_value(adau1701->gpio_pll_mode[0], 0);
+			gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+			break;
+		case 384:
+			gpio_set_value(adau1701->gpio_pll_mode[0], 1);
+			gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+			break;
+		case 0:	/* fallback */
+		case 512:
+			gpio_set_value(adau1701->gpio_pll_mode[0], 1);
+			gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+			break;
+		}
+	}
+
+	adau1701->pll_clkdiv = clkdiv;
+
+	if (gpio_is_valid(adau1701->gpio_nreset)) {
+		gpio_set_value(adau1701->gpio_nreset, 0);
+		/* minimum reset time is 20ns */
+		udelay(1);
+		gpio_set_value(adau1701->gpio_nreset, 1);
+		/* power-up time may be as long as 85ms */
+		mdelay(85);
+	}
+
+	/*
+	 * Postpone the firmware download to a point in time when we
+	 * know the correct PLL setup
+	 */
+	if (clkdiv != ADAU1707_CLKDIV_UNSET) {
+		ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
+		if (ret) {
+			dev_warn(codec->dev, "Failed to load firmware\n");
+			return ret;
+		}
+	}
+
+	regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
+	regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+
+	regcache_mark_dirty(adau1701->regmap);
+	regcache_sync(adau1701->regmap);
+
+	return 0;
 }
 
 static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
@@ -221,7 +334,7 @@
 		mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
 	}
 
-	snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val);
+	regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val);
 
 	return 0;
 }
@@ -249,7 +362,7 @@
 		return -EINVAL;
 	}
 
-	snd_soc_update_bits(codec, ADAU1701_SERICTL,
+	regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL,
 		ADAU1701_SERICTL_MODE_MASK, val);
 
 	return 0;
@@ -259,8 +372,22 @@
 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
 	struct snd_soc_codec *codec = dai->codec;
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+	unsigned int clkdiv = adau1701->sysclk / params_rate(params);
 	snd_pcm_format_t format;
 	unsigned int val;
+	int ret;
+
+	/*
+	 * If the mclk/lrclk ratio changes, the chip needs updated PLL
+	 * mode GPIO settings, and a full reset cycle, including a new
+	 * firmware upload.
+	 */
+	if (clkdiv != adau1701->pll_clkdiv) {
+		ret = adau1701_reset(codec, clkdiv);
+		if (ret < 0)
+			return ret;
+	}
 
 	switch (params_rate(params)) {
 	case 192000:
@@ -276,7 +403,7 @@
 		return -EINVAL;
 	}
 
-	snd_soc_update_bits(codec, ADAU1701_DSPCTRL,
+	regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
 		ADAU1701_DSPCTRL_SR_MASK, val);
 
 	format = params_format(params);
@@ -352,8 +479,8 @@
 
 	adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
 
-	snd_soc_write(codec, ADAU1701_SERICTL, serictl);
-	snd_soc_update_bits(codec, ADAU1701_SEROCTL,
+	regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl);
+	regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL,
 		~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
 
 	return 0;
@@ -363,6 +490,7 @@
 		enum snd_soc_bias_level level)
 {
 	unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
@@ -371,11 +499,13 @@
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* Enable VREF and VREF buffer */
-		snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00);
+		regmap_update_bits(adau1701->regmap,
+				   ADAU1701_AUXNPOW, mask, 0x00);
 		break;
 	case SND_SOC_BIAS_OFF:
 		/* Disable VREF and VREF buffer */
-		snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask);
+		regmap_update_bits(adau1701->regmap,
+				   ADAU1701_AUXNPOW, mask, mask);
 		break;
 	}
 
@@ -387,6 +517,7 @@
 {
 	struct snd_soc_codec *codec = dai->codec;
 	unsigned int mask = ADAU1701_DSPCTRL_DAM;
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
 	unsigned int val;
 
 	if (mute)
@@ -394,7 +525,7 @@
 	else
 		val = mask;
 
-	snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val);
+	regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val);
 
 	return 0;
 }
@@ -403,6 +534,7 @@
 	int source, unsigned int freq, int dir)
 {
 	unsigned int val;
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
 
 	switch (clk_id) {
 	case ADAU1701_CLK_SRC_OSC:
@@ -415,7 +547,9 @@
 		return -EINVAL;
 	}
 
-	snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
+	regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW,
+			   ADAU1701_OSCIPOW_OPD, val);
+	adau1701->sysclk = freq;
 
 	return 0;
 }
@@ -452,18 +586,45 @@
 	.symmetric_rates = 1,
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id adau1701_dt_ids[] = {
+	{ .compatible = "adi,adau1701", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
+#endif
+
 static int adau1701_probe(struct snd_soc_codec *codec)
 {
-	int ret;
+	int i, ret;
+	unsigned int val;
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
 
-	codec->control_data = to_i2c_client(codec->dev);
+	/*
+	 * Let the pll_clkdiv variable default to something that won't happen
+	 * at runtime. That way, we can postpone the firmware download from
+	 * adau1701_reset() to a point in time when we know the correct PLL
+	 * mode parameters.
+	 */
+	adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
 
-	ret = adau1701_load_firmware(codec);
-	if (ret)
-		dev_warn(codec->dev, "Failed to load firmware\n");
+	/* initalize with pre-configured pll mode settings */
+	ret = adau1701_reset(codec, adau1701->pll_clkdiv);
+	if (ret < 0)
+		return ret;
 
-	snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
-	snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+	/* set up pin config */
+	val = 0;
+	for (i = 0; i < 6; i++)
+		val |= adau1701->pin_config[i] << (i * 4);
+
+	regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val);
+
+	val = 0;
+	for (i = 0; i < 6; i++)
+		val |= adau1701->pin_config[i + 6] << (i * 4);
+
+	regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
 
 	return 0;
 }
@@ -473,9 +634,6 @@
 	.set_bias_level		= adau1701_set_bias_level,
 	.idle_bias_off		= true,
 
-	.reg_cache_size		= ADAU1701_NUM_REGS,
-	.reg_word_size		= sizeof(u16),
-
 	.controls		= adau1701_controls,
 	.num_controls		= ARRAY_SIZE(adau1701_controls),
 	.dapm_widgets		= adau1701_dapm_widgets,
@@ -483,22 +641,86 @@
 	.dapm_routes		= adau1701_dapm_routes,
 	.num_dapm_routes	= ARRAY_SIZE(adau1701_dapm_routes),
 
-	.write			= adau1701_write,
-	.read			= adau1701_read,
-
 	.set_sysclk		= adau1701_set_sysclk,
 };
 
+static const struct regmap_config adau1701_regmap = {
+	.reg_bits		= 16,
+	.val_bits		= 32,
+	.max_register		= ADAU1701_MAX_REGISTER,
+	.cache_type		= REGCACHE_RBTREE,
+	.volatile_reg		= adau1701_volatile_reg,
+	.reg_write		= adau1701_reg_write,
+	.reg_read		= adau1701_reg_read,
+};
+
 static int adau1701_i2c_probe(struct i2c_client *client,
 			      const struct i2c_device_id *id)
 {
 	struct adau1701 *adau1701;
+	struct device *dev = &client->dev;
+	int gpio_nreset = -EINVAL;
+	int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
 	int ret;
 
-	adau1701 = devm_kzalloc(&client->dev, sizeof(*adau1701), GFP_KERNEL);
+	adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
 	if (!adau1701)
 		return -ENOMEM;
 
+	adau1701->regmap = devm_regmap_init(dev, NULL, client,
+					    &adau1701_regmap);
+	if (IS_ERR(adau1701->regmap))
+		return PTR_ERR(adau1701->regmap);
+
+	if (dev->of_node) {
+		gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+		if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
+			return gpio_nreset;
+
+		gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
+						   "adi,pll-mode-gpios", 0);
+		if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
+			return gpio_pll_mode[0];
+
+		gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
+						   "adi,pll-mode-gpios", 1);
+		if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
+			return gpio_pll_mode[1];
+
+		of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
+				     &adau1701->pll_clkdiv);
+
+		of_property_read_u8_array(dev->of_node, "adi,pin-config",
+					  adau1701->pin_config,
+					  ARRAY_SIZE(adau1701->pin_config));
+	}
+
+	if (gpio_is_valid(gpio_nreset)) {
+		ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
+					    "ADAU1701 Reset");
+		if (ret < 0)
+			return ret;
+	}
+
+	if (gpio_is_valid(gpio_pll_mode[0]) &&
+	    gpio_is_valid(gpio_pll_mode[1])) {
+		ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
+					    GPIOF_OUT_INIT_LOW,
+					    "ADAU1701 PLL mode 0");
+		if (ret < 0)
+			return ret;
+
+		ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
+					    GPIOF_OUT_INIT_LOW,
+					    "ADAU1701 PLL mode 1");
+		if (ret < 0)
+			return ret;
+	}
+
+	adau1701->gpio_nreset = gpio_nreset;
+	adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
+	adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
+
 	i2c_set_clientdata(client, adau1701);
 	ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
 			&adau1701_dai, 1);
@@ -521,6 +743,7 @@
 	.driver = {
 		.name	= "adau1701",
 		.owner	= THIS_MODULE,
+		.of_match_table	= of_match_ptr(adau1701_dt_ids),
 	},
 	.probe		= adau1701_i2c_probe,
 	.remove		= adau1701_i2c_remove,
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 2eda85ba..a5455c1 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -28,8 +28,6 @@
 
 #include "stac9766.h"
 
-#define STAC9766_VERSION "0.10"
-
 /*
  * STAC9766 register cache
  */
@@ -145,14 +143,14 @@
 
 	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(codec->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(codec->ac97, reg, val);
 	cache[reg / 2] = val;
 	return 0;
 }
@@ -164,7 +162,7 @@
 
 	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(codec->ac97, reg - AC97_STAC_PAGE0);
 		stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
 		return val;
 	}
@@ -175,7 +173,7 @@
 		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(codec->ac97, reg);
 		return val;
 	}
 	return cache[reg / 2];
@@ -242,15 +240,15 @@
 
 static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
 {
-	if (try_warm && soc_ac97_ops.warm_reset) {
-		soc_ac97_ops.warm_reset(codec->ac97);
+	if (try_warm && soc_ac97_ops->warm_reset) {
+		soc_ac97_ops->warm_reset(codec->ac97);
 		if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
 			return 1;
 	}
 
-	soc_ac97_ops.reset(codec->ac97);
-	if (soc_ac97_ops.warm_reset)
-		soc_ac97_ops.warm_reset(codec->ac97);
+	soc_ac97_ops->reset(codec->ac97);
+	if (soc_ac97_ops->warm_reset)
+		soc_ac97_ops->warm_reset(codec->ac97);
 	if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
 		return -EIO;
 	return 0;
@@ -274,7 +272,7 @@
 		return -EIO;
 	}
 	codec->ac97->bus->ops->warm_reset(codec->ac97);
-	id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
+	id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
 	if (id != 0x4c13) {
 		stac9766_reset(codec, 0);
 		reset++;
@@ -338,9 +336,7 @@
 {
 	int ret = 0;
 
-	printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
-
-	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
 	if (ret < 0)
 		goto codec_err;
 
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 1514bf84..e5b9268 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -128,10 +128,8 @@
 };
 
 #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_volsw, \
-	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \
-	.private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+	SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \
+		snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x)
 
 /*
  * All input lines are connected when !0xf and disconnected with 0xf bit field,
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index af6d227..d2a0928 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -143,13 +143,8 @@
 }
 
 #define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
-{	.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 = snd_soc_info_volsw, \
-	.get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \
-	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+	SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+		snd_soc_get_volsw, wm8400_outpga_put_volsw_vu, tlv_array)
 
 
 static const char *wm8400_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 9d88437..fa24ced 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -403,10 +403,8 @@
 }
 
 #define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_volsw, \
-	.get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \
-	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+	SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+		snd_soc_dapm_get_volsw, wm8903_class_w_put)
 
 
 static int wm8903_deemph[] = { 0, 32000, 44100, 48000 };
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 3ff195c..4c9fb14 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -603,13 +603,8 @@
 
 SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0),
 SOC_ENUM("High Pass Filter Mode", hpf_mode),
-
-{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "ADC 128x OSR Switch",
-	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,
-	.put = wm8904_adc_osr_put,
-	.private_value = SOC_SINGLE_VALUE(WM8904_ANALOGUE_ADC_0, 0, 1, 0),
-},
+SOC_SINGLE_EXT("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0,
+	snd_soc_get_volsw, wm8904_adc_osr_put),
 };
 
 static const char *drc_path_text[] = {
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 837978e..253c88b 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -151,14 +151,9 @@
 }
 
 #define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
-	 tlv_array) {\
-	.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 = snd_soc_info_volsw, \
-	.get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
-	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+	tlv_array) \
+	SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+		snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
 
 
 static const char *wm8990_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
index 8a942ef..07707d8 100644
--- a/sound/soc/codecs/wm8991.h
+++ b/sound/soc/codecs/wm8991.h
@@ -822,12 +822,7 @@
 
 #define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
 					 tlv_array) \
-{	.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 = snd_soc_info_volsw, \
-	.get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
-	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+	SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+		snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
 
 #endif /* _WM8991_H */
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 29e95f93d..9e13edd 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -289,10 +289,8 @@
 static const DECLARE_TLV_DB_SCALE(mixin_boost_tlv, 0, 900, 0);
 
 #define WM8994_DRC_SWITCH(xname, reg, shift) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
-	.put = wm8994_put_drc_sw, \
-	.private_value =  SOC_SINGLE_VALUE(reg, shift, 1, 0) }
+	SOC_SINGLE_EXT(xname, reg, shift, 1, 0, \
+		snd_soc_get_volsw, wm8994_put_drc_sw)
 
 static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
 			     struct snd_ctl_elem_value *ucontrol)
@@ -1432,10 +1430,8 @@
 };
 
 #define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_volsw, \
-	.get = snd_soc_dapm_get_volsw, .put = wm8994_put_class_w, \
-	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+	SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+		snd_soc_get_volsw, wm8994_put_class_w)
 
 static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/codecs/wm8995.h b/sound/soc/codecs/wm8995.h
index 5642121..508ad27 100644
--- a/sound/soc/codecs/wm8995.h
+++ b/sound/soc/codecs/wm8995.h
@@ -4237,11 +4237,8 @@
 #define WM8995_SPK2_MUTE_SEQ1_WIDTH                  8	/* SPK2_MUTE_SEQ1 - [7:0] */
 
 #define WM8995_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_volsw, \
-	.get = snd_soc_dapm_get_volsw, .put = wm8995_put_class_w, \
-	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) \
-}
+	SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+		snd_soc_dapm_get_volsw, wm8995_put_class_w)
 
 struct wm8995_reg_access {
 	u16 read;
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 05b1f34..70ce6793 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -209,7 +209,7 @@
 	case AC97_RESET:
 	case AC97_VENDOR_ID1:
 	case AC97_VENDOR_ID2:
-		return soc_ac97_ops.read(codec->ac97, reg);
+		return soc_ac97_ops->read(codec->ac97, reg);
 	default:
 		reg = reg >> 1;
 
@@ -225,7 +225,7 @@
 {
 	u16 *cache = codec->reg_cache;
 
-	soc_ac97_ops.write(codec->ac97, reg, val);
+	soc_ac97_ops->write(codec->ac97, reg, val);
 	reg = reg >> 1;
 	if (reg < (ARRAY_SIZE(wm9705_reg)))
 		cache[reg] = val;
@@ -294,8 +294,8 @@
 
 static int wm9705_reset(struct snd_soc_codec *codec)
 {
-	if (soc_ac97_ops.reset) {
-		soc_ac97_ops.reset(codec->ac97);
+	if (soc_ac97_ops->reset) {
+		soc_ac97_ops->reset(codec->ac97);
 		if (ac97_read(codec, 0) == wm9705_reg[0])
 			return 0; /* Success */
 	}
@@ -306,7 +306,7 @@
 #ifdef CONFIG_PM
 static int wm9705_soc_suspend(struct snd_soc_codec *codec)
 {
-	soc_ac97_ops.write(codec->ac97, AC97_POWERDOWN, 0xffff);
+	soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
 
 	return 0;
 }
@@ -323,7 +323,7 @@
 	}
 
 	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(codec->ac97, i, cache[i>>1]);
 	}
 
 	return 0;
@@ -337,9 +337,7 @@
 {
 	int ret = 0;
 
-	printk(KERN_INFO "WM9705 SoC Audio Codec\n");
-
-	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
 	if (ret < 0) {
 		printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
 		return ret;
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 8e9a6a3..c5eb746 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -455,7 +455,7 @@
 	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(codec->ac97, reg);
 	else {
 		reg = reg >> 1;
 
@@ -472,7 +472,7 @@
 	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;
@@ -581,15 +581,15 @@
 
 static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
 {
-	if (try_warm && soc_ac97_ops.warm_reset) {
-		soc_ac97_ops.warm_reset(codec->ac97);
+	if (try_warm && soc_ac97_ops->warm_reset) {
+		soc_ac97_ops->warm_reset(codec->ac97);
 		if (ac97_read(codec, 0) == wm9712_reg[0])
 			return 1;
 	}
 
-	soc_ac97_ops.reset(codec->ac97);
-	if (soc_ac97_ops.warm_reset)
-		soc_ac97_ops.warm_reset(codec->ac97);
+	soc_ac97_ops->reset(codec->ac97);
+	if (soc_ac97_ops->warm_reset)
+		soc_ac97_ops->warm_reset(codec->ac97);
 	if (ac97_read(codec, 0) != wm9712_reg[0])
 		goto err;
 	return 0;
@@ -624,7 +624,7 @@
 			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(codec->ac97, i, cache[i>>1]);
 		}
 	}
 
@@ -635,7 +635,7 @@
 {
 	int ret = 0;
 
-	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
 	if (ret < 0) {
 		printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
 		return ret;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index f7afa68..a53e175 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -652,7 +652,7 @@
 	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(codec->ac97, reg);
 	else {
 		reg = reg >> 1;
 
@@ -668,7 +668,7 @@
 {
 	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;
@@ -1095,15 +1095,15 @@
 
 int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
 {
-	if (try_warm && soc_ac97_ops.warm_reset) {
-		soc_ac97_ops.warm_reset(codec->ac97);
+	if (try_warm && soc_ac97_ops->warm_reset) {
+		soc_ac97_ops->warm_reset(codec->ac97);
 		if (ac97_read(codec, 0) == wm9713_reg[0])
 			return 1;
 	}
 
-	soc_ac97_ops.reset(codec->ac97);
-	if (soc_ac97_ops.warm_reset)
-		soc_ac97_ops.warm_reset(codec->ac97);
+	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])
 		return -EIO;
 	return 0;
@@ -1180,7 +1180,7 @@
 			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(codec->ac97, i, cache[i>>1]);
 		}
 	}
 
@@ -1197,7 +1197,7 @@
 		return -ENOMEM;
 	snd_soc_codec_set_drvdata(codec, wm9713);
 
-	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
 	if (ret < 0)
 		goto codec_err;
 
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 3470b64..ddba3fe 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -21,6 +21,7 @@
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -215,6 +216,36 @@
 	[WM_ADSP_FW_RX_ANC] =  { .file = "rx-anc" },
 };
 
+struct wm_coeff_ctl_ops {
+	int (*xget)(struct snd_kcontrol *kcontrol,
+		    struct snd_ctl_elem_value *ucontrol);
+	int (*xput)(struct snd_kcontrol *kcontrol,
+		    struct snd_ctl_elem_value *ucontrol);
+	int (*xinfo)(struct snd_kcontrol *kcontrol,
+		     struct snd_ctl_elem_info *uinfo);
+};
+
+struct wm_coeff {
+	struct device *dev;
+	struct list_head ctl_list;
+	struct regmap *regmap;
+};
+
+struct wm_coeff_ctl {
+	const char *name;
+	struct snd_card *card;
+	struct wm_adsp_alg_region region;
+	struct wm_coeff_ctl_ops ops;
+	struct wm_adsp *adsp;
+	void *private;
+	unsigned int enabled:1;
+	struct list_head list;
+	void *cache;
+	size_t len;
+	unsigned int set:1;
+	struct snd_kcontrol *kcontrol;
+};
+
 static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
 			  struct snd_ctl_elem_value *ucontrol)
 {
@@ -334,6 +365,181 @@
 	}
 }
 
+static int wm_coeff_info(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_info *uinfo)
+{
+	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = ctl->len;
+	return 0;
+}
+
+static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
+				  const void *buf, size_t len)
+{
+	struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
+	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+	struct wm_adsp_alg_region *region = &ctl->region;
+	const struct wm_adsp_region *mem;
+	struct wm_adsp *adsp = ctl->adsp;
+	void *scratch;
+	int ret;
+	unsigned int reg;
+
+	mem = wm_adsp_find_region(adsp, region->type);
+	if (!mem) {
+		adsp_err(adsp, "No base for region %x\n",
+			 region->type);
+		return -EINVAL;
+	}
+
+	reg = ctl->region.base;
+	reg = wm_adsp_region_to_reg(mem, reg);
+
+	scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
+	if (!scratch)
+		return -ENOMEM;
+
+	ret = regmap_raw_write(wm_coeff->regmap, reg, scratch,
+			       ctl->len);
+	if (ret) {
+		adsp_err(adsp, "Failed to write %zu bytes to %x\n",
+			 ctl->len, reg);
+		kfree(scratch);
+		return ret;
+	}
+
+	kfree(scratch);
+
+	return 0;
+}
+
+static int wm_coeff_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+	char *p = ucontrol->value.bytes.data;
+
+	memcpy(ctl->cache, p, ctl->len);
+
+	if (!ctl->enabled) {
+		ctl->set = 1;
+		return 0;
+	}
+
+	return wm_coeff_write_control(kcontrol, p, ctl->len);
+}
+
+static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
+				 void *buf, size_t len)
+{
+	struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
+	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+	struct wm_adsp_alg_region *region = &ctl->region;
+	const struct wm_adsp_region *mem;
+	struct wm_adsp *adsp = ctl->adsp;
+	void *scratch;
+	int ret;
+	unsigned int reg;
+
+	mem = wm_adsp_find_region(adsp, region->type);
+	if (!mem) {
+		adsp_err(adsp, "No base for region %x\n",
+			 region->type);
+		return -EINVAL;
+	}
+
+	reg = ctl->region.base;
+	reg = wm_adsp_region_to_reg(mem, reg);
+
+	scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
+	if (!scratch)
+		return -ENOMEM;
+
+	ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len);
+	if (ret) {
+		adsp_err(adsp, "Failed to read %zu bytes from %x\n",
+			 ctl->len, reg);
+		kfree(scratch);
+		return ret;
+	}
+
+	memcpy(buf, scratch, ctl->len);
+	kfree(scratch);
+
+	return 0;
+}
+
+static int wm_coeff_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+	char *p = ucontrol->value.bytes.data;
+
+	memcpy(p, ctl->cache, ctl->len);
+	return 0;
+}
+
+static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff,
+				 struct wm_coeff_ctl *ctl,
+				 const struct snd_kcontrol_new *kctl)
+{
+	int ret;
+	struct snd_kcontrol *kcontrol;
+
+	kcontrol = snd_ctl_new1(kctl, wm_coeff);
+	ret = snd_ctl_add(ctl->card, kcontrol);
+	if (ret < 0) {
+		dev_err(wm_coeff->dev, "Failed to add %s: %d\n",
+			kctl->name, ret);
+		return ret;
+	}
+	ctl->kcontrol = kcontrol;
+	return 0;
+}
+
+struct wmfw_ctl_work {
+	struct wm_coeff *wm_coeff;
+	struct wm_coeff_ctl *ctl;
+	struct work_struct work;
+};
+
+static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
+			struct wm_coeff_ctl *ctl)
+{
+	struct snd_kcontrol_new *kcontrol;
+	int ret;
+
+	if (!wm_coeff || !ctl || !ctl->name || !ctl->card)
+		return -EINVAL;
+
+	kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
+	if (!kcontrol)
+		return -ENOMEM;
+	kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+
+	kcontrol->name = ctl->name;
+	kcontrol->info = wm_coeff_info;
+	kcontrol->get = wm_coeff_get;
+	kcontrol->put = wm_coeff_put;
+	kcontrol->private_value = (unsigned long)ctl;
+
+	ret = wm_coeff_add_kcontrol(wm_coeff,
+				    ctl, kcontrol);
+	if (ret < 0)
+		goto err_kcontrol;
+
+	kfree(kcontrol);
+
+	list_add(&ctl->list, &wm_coeff->ctl_list);
+	return 0;
+
+err_kcontrol:
+	kfree(kcontrol);
+	return ret;
+}
+
 static int wm_adsp_load(struct wm_adsp *dsp)
 {
 	LIST_HEAD(buf_list);
@@ -547,7 +753,157 @@
 	return ret;
 }
 
-static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
+{
+	struct wm_coeff_ctl *ctl;
+	int ret;
+
+	list_for_each_entry(ctl, &wm_coeff->ctl_list,
+			    list) {
+		if (!ctl->enabled || ctl->set)
+			continue;
+		ret = wm_coeff_read_control(ctl->kcontrol,
+					    ctl->cache,
+					    ctl->len);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff)
+{
+	struct wm_coeff_ctl *ctl;
+	int ret;
+
+	list_for_each_entry(ctl, &wm_coeff->ctl_list,
+			    list) {
+		if (!ctl->enabled)
+			continue;
+		if (ctl->set) {
+			ret = wm_coeff_write_control(ctl->kcontrol,
+						     ctl->cache,
+						     ctl->len);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void wm_adsp_ctl_work(struct work_struct *work)
+{
+	struct wmfw_ctl_work *ctl_work = container_of(work,
+						      struct wmfw_ctl_work,
+						      work);
+
+	wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl);
+	kfree(ctl_work);
+}
+
+static int wm_adsp_create_control(struct snd_soc_codec *codec,
+				  const struct wm_adsp_alg_region *region)
+
+{
+	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
+	struct wm_coeff_ctl *ctl;
+	struct wmfw_ctl_work *ctl_work;
+	char *name;
+	char *region_name;
+	int ret;
+
+	name = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+
+	switch (region->type) {
+	case WMFW_ADSP1_PM:
+		region_name = "PM";
+		break;
+	case WMFW_ADSP1_DM:
+		region_name = "DM";
+		break;
+	case WMFW_ADSP2_XM:
+		region_name = "XM";
+		break;
+	case WMFW_ADSP2_YM:
+		region_name = "YM";
+		break;
+	case WMFW_ADSP1_ZM:
+		region_name = "ZM";
+		break;
+	default:
+		ret = -EINVAL;
+		goto err_name;
+	}
+
+	snprintf(name, PAGE_SIZE, "DSP%d %s %x",
+		 dsp->num, region_name, region->alg);
+
+	list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+			    list) {
+		if (!strcmp(ctl->name, name)) {
+			if (!ctl->enabled)
+				ctl->enabled = 1;
+			goto found;
+		}
+	}
+
+	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+	if (!ctl) {
+		ret = -ENOMEM;
+		goto err_name;
+	}
+	ctl->region = *region;
+	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
+	if (!ctl->name) {
+		ret = -ENOMEM;
+		goto err_ctl;
+	}
+	ctl->enabled = 1;
+	ctl->set = 0;
+	ctl->ops.xget = wm_coeff_get;
+	ctl->ops.xput = wm_coeff_put;
+	ctl->card = codec->card->snd_card;
+	ctl->adsp = dsp;
+
+	ctl->len = region->len;
+	ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+	if (!ctl->cache) {
+		ret = -ENOMEM;
+		goto err_ctl_name;
+	}
+
+	ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
+	if (!ctl_work) {
+		ret = -ENOMEM;
+		goto err_ctl_cache;
+	}
+
+	ctl_work->wm_coeff = dsp->wm_coeff;
+	ctl_work->ctl = ctl;
+	INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
+	schedule_work(&ctl_work->work);
+
+found:
+	kfree(name);
+
+	return 0;
+
+err_ctl_cache:
+	kfree(ctl->cache);
+err_ctl_name:
+	kfree(ctl->name);
+err_ctl:
+	kfree(ctl);
+err_name:
+	kfree(name);
+	return ret;
+}
+
+static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
 {
 	struct regmap *regmap = dsp->regmap;
 	struct wmfw_adsp1_id_hdr adsp1_id;
@@ -730,7 +1086,16 @@
 			region->type = WMFW_ADSP1_DM;
 			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
 			region->base = be32_to_cpu(adsp1_alg[i].dm);
+			region->len = 0;
 			list_add_tail(&region->list, &dsp->alg_regions);
+			if (i + 1 < algs) {
+				region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
+				region->len -= be32_to_cpu(adsp1_alg[i].dm);
+				wm_adsp_create_control(codec, region);
+			} else {
+				adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
+					  be32_to_cpu(adsp1_alg[i].alg.id));
+			}
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
 			if (!region)
@@ -738,7 +1103,16 @@
 			region->type = WMFW_ADSP1_ZM;
 			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
 			region->base = be32_to_cpu(adsp1_alg[i].zm);
+			region->len = 0;
 			list_add_tail(&region->list, &dsp->alg_regions);
+			if (i + 1 < algs) {
+				region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
+				region->len -= be32_to_cpu(adsp1_alg[i].zm);
+				wm_adsp_create_control(codec, region);
+			} else {
+				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+					  be32_to_cpu(adsp1_alg[i].alg.id));
+			}
 			break;
 
 		case WMFW_ADSP2:
@@ -758,7 +1132,16 @@
 			region->type = WMFW_ADSP2_XM;
 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
 			region->base = be32_to_cpu(adsp2_alg[i].xm);
+			region->len = 0;
 			list_add_tail(&region->list, &dsp->alg_regions);
+			if (i + 1 < algs) {
+				region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
+				region->len -= be32_to_cpu(adsp2_alg[i].xm);
+				wm_adsp_create_control(codec, region);
+			} else {
+				adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
+					  be32_to_cpu(adsp2_alg[i].alg.id));
+			}
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
 			if (!region)
@@ -766,7 +1149,16 @@
 			region->type = WMFW_ADSP2_YM;
 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
 			region->base = be32_to_cpu(adsp2_alg[i].ym);
+			region->len = 0;
 			list_add_tail(&region->list, &dsp->alg_regions);
+			if (i + 1 < algs) {
+				region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
+				region->len -= be32_to_cpu(adsp2_alg[i].ym);
+				wm_adsp_create_control(codec, region);
+			} else {
+				adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
+					  be32_to_cpu(adsp2_alg[i].alg.id));
+			}
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
 			if (!region)
@@ -774,7 +1166,16 @@
 			region->type = WMFW_ADSP2_ZM;
 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
 			region->base = be32_to_cpu(adsp2_alg[i].zm);
+			region->len = 0;
 			list_add_tail(&region->list, &dsp->alg_regions);
+			if (i + 1 < algs) {
+				region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
+				region->len -= be32_to_cpu(adsp2_alg[i].zm);
+				wm_adsp_create_control(codec, region);
+			} else {
+				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+					  be32_to_cpu(adsp2_alg[i].alg.id));
+			}
 			break;
 		}
 	}
@@ -986,6 +1387,7 @@
 	struct snd_soc_codec *codec = w->codec;
 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
 	struct wm_adsp *dsp = &dsps[w->shift];
+	struct wm_coeff_ctl *ctl;
 	int ret;
 	int val;
 
@@ -1023,7 +1425,7 @@
 		if (ret != 0)
 			goto err;
 
-		ret = wm_adsp_setup_algs(dsp);
+		ret = wm_adsp_setup_algs(dsp, codec);
 		if (ret != 0)
 			goto err;
 
@@ -1031,6 +1433,16 @@
 		if (ret != 0)
 			goto err;
 
+		/* Initialize caches for enabled and unset controls */
+		ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+		if (ret != 0)
+			goto err;
+
+		/* Sync set controls */
+		ret = wm_coeff_sync_controls(dsp->wm_coeff);
+		if (ret != 0)
+			goto err;
+
 		/* Start the core running */
 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
 				   ADSP1_CORE_ENA | ADSP1_START,
@@ -1047,6 +1459,11 @@
 
 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
 				   ADSP1_SYS_ENA, 0);
+
+		list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+				    list) {
+			ctl->enabled = 0;
+		}
 		break;
 
 	default:
@@ -1099,6 +1516,7 @@
 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
 	struct wm_adsp *dsp = &dsps[w->shift];
 	struct wm_adsp_alg_region *alg_region;
+	struct wm_coeff_ctl *ctl;
 	unsigned int val;
 	int ret;
 
@@ -1164,7 +1582,7 @@
 		if (ret != 0)
 			goto err;
 
-		ret = wm_adsp_setup_algs(dsp);
+		ret = wm_adsp_setup_algs(dsp, codec);
 		if (ret != 0)
 			goto err;
 
@@ -1172,6 +1590,16 @@
 		if (ret != 0)
 			goto err;
 
+		/* Initialize caches for enabled and unset controls */
+		ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+		if (ret != 0)
+			goto err;
+
+		/* Sync set controls */
+		ret = wm_coeff_sync_controls(dsp->wm_coeff);
+		if (ret != 0)
+			goto err;
+
 		ret = regmap_update_bits(dsp->regmap,
 					 dsp->base + ADSP2_CONTROL,
 					 ADSP2_CORE_ENA | ADSP2_START,
@@ -1209,6 +1637,11 @@
 					ret);
 		}
 
+		list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+				    list) {
+			ctl->enabled = 0;
+		}
+
 		while (!list_empty(&dsp->alg_regions)) {
 			alg_region = list_first_entry(&dsp->alg_regions,
 						      struct wm_adsp_alg_region,
@@ -1247,36 +1680,48 @@
 
 	INIT_LIST_HEAD(&adsp->alg_regions);
 
+	adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff),
+				 GFP_KERNEL);
+	if (!adsp->wm_coeff)
+		return -ENOMEM;
+	adsp->wm_coeff->regmap = adsp->regmap;
+	adsp->wm_coeff->dev = adsp->dev;
+	INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list);
+
 	if (dvfs) {
 		adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
 		if (IS_ERR(adsp->dvfs)) {
 			ret = PTR_ERR(adsp->dvfs);
 			dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
-			return ret;
+			goto out_coeff;
 		}
 
 		ret = regulator_enable(adsp->dvfs);
 		if (ret != 0) {
 			dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
 				ret);
-			return ret;
+			goto out_coeff;
 		}
 
 		ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
 		if (ret != 0) {
 			dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
 				ret);
-			return ret;
+			goto out_coeff;
 		}
 
 		ret = regulator_disable(adsp->dvfs);
 		if (ret != 0) {
 			dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
 				ret);
-			return ret;
+			goto out_coeff;
 		}
 	}
 
 	return 0;
+
+out_coeff:
+	kfree(adsp->wm_coeff);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index fea5146..9f922c8 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -30,6 +30,7 @@
 	unsigned int alg;
 	int type;
 	unsigned int base;
+	size_t len;
 };
 
 struct wm_adsp {
@@ -55,17 +56,17 @@
 	bool running;
 
 	struct regulator *dvfs;
+
+	struct wm_coeff *wm_coeff;
 };
 
 #define WM_ADSP1(wname, num) \
-	{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
-	.shift = num, .event = wm_adsp1_event, \
-	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+	SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+		wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
 
 #define WM_ADSP2(wname, num) \
-{	.id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
-	.shift = num, .event = wm_adsp2_event, \
-	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+	SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+		wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
 
 extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
 extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index f5d81b9..2d9e099 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -693,10 +693,8 @@
 EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
 
 #define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_volsw, \
-	.get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \
-	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+	SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+		snd_soc_dapm_get_volsw, class_w_put_volsw)
 
 static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index c6fa03e..bd40849 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -501,13 +501,12 @@
 	imx_ssi_ac97_read(ac97, 0);
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops imx_ssi_ac97_ops = {
 	.read		= imx_ssi_ac97_read,
 	.write		= imx_ssi_ac97_write,
 	.reset		= imx_ssi_ac97_reset,
 	.warm_reset	= imx_ssi_ac97_warm_reset
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int imx_ssi_probe(struct platform_device *pdev)
 {
@@ -583,6 +582,12 @@
 
 	platform_set_drvdata(pdev, ssi);
 
+	ret = snd_soc_set_ac97_ops(&imx_ssi_ac97_ops);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+		goto failed_register;
+	}
+
 	ret = snd_soc_register_component(&pdev->dev, &imx_component,
 					 dai, 1);
 	if (ret) {
@@ -630,6 +635,7 @@
 	release_mem_region(res->start, resource_size(res));
 	clk_disable_unprepare(ssi->clk);
 failed_clk:
+	snd_soc_set_ac97_ops(NULL);
 
 	return ret;
 }
@@ -649,6 +655,7 @@
 
 	release_mem_region(res->start, resource_size(res));
 	clk_disable_unprepare(ssi->clk);
+	snd_soc_set_ac97_ops(NULL);
 
 	return 0;
 }
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 4141b35..3ef7a0c 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -131,13 +131,12 @@
 	psc_ac97_warm_reset(ac97);
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops psc_ac97_ops = {
 	.read		= psc_ac97_read,
 	.write		= psc_ac97_write,
 	.reset		= psc_ac97_cold_reset,
 	.warm_reset	= psc_ac97_warm_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
 				 struct snd_pcm_hw_params *params,
@@ -290,6 +289,12 @@
 	if (rc != 0)
 		return rc;
 
+	rc = snd_soc_set_ac97_ops(&psc_ac97_ops);
+	if (rc != 0) {
+		dev_err(&op->dev, "Failed to set AC'97 ops: %d\n", ret);
+		return rc;
+	}
+
 	rc = snd_soc_register_component(&op->dev, &psc_ac97_component,
 					psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
 	if (rc != 0) {
@@ -318,6 +323,7 @@
 {
 	mpc5200_audio_dma_destroy(op);
 	snd_soc_unregister_component(&op->dev);
+	snd_soc_set_ac97_ops(NULL);
 	return 0;
 }
 
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index fe3285c..f4c2417 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -197,13 +197,12 @@
 }
 
 /* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops nuc900_ac97_ops = {
 	.read		= nuc900_ac97_read,
 	.write		= nuc900_ac97_write,
 	.reset		= nuc900_ac97_cold_reset,
 	.warm_reset	= nuc900_ac97_warm_reset,
-}
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
+};
 
 static int nuc900_ac97_trigger(struct snd_pcm_substream *substream,
 				int cmd, struct snd_soc_dai *dai)
@@ -326,64 +325,52 @@
 	if (nuc900_ac97_data)
 		return -EBUSY;
 
-	nuc900_audio = kzalloc(sizeof(struct nuc900_audio), GFP_KERNEL);
+	nuc900_audio = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_audio),
+				    GFP_KERNEL);
 	if (!nuc900_audio)
 		return -ENOMEM;
 
 	spin_lock_init(&nuc900_audio->lock);
 
 	nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!nuc900_audio->res) {
-		ret = -ENODEV;
-		goto out0;
-	}
+	if (!nuc900_audio->res)
+		return ret;
 
-	if (!request_mem_region(nuc900_audio->res->start,
-			resource_size(nuc900_audio->res), pdev->name)) {
-		ret = -EBUSY;
-		goto out0;
-	}
+	nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev,
+						   nuc900_audio->res);
+	if (IS_ERR(nuc900_audio->mmio))
+		return PTR_ERR(nuc900_audio->mmio);
 
-	nuc900_audio->mmio = ioremap(nuc900_audio->res->start,
-					resource_size(nuc900_audio->res));
-	if (!nuc900_audio->mmio) {
-		ret = -ENOMEM;
-		goto out1;
-	}
-
-	nuc900_audio->clk = clk_get(&pdev->dev, NULL);
+	nuc900_audio->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(nuc900_audio->clk)) {
 		ret = PTR_ERR(nuc900_audio->clk);
-		goto out2;
+		goto out;
 	}
 
 	nuc900_audio->irq_num = platform_get_irq(pdev, 0);
 	if (!nuc900_audio->irq_num) {
 		ret = -EBUSY;
-		goto out3;
+		goto out;
 	}
 
 	nuc900_ac97_data = nuc900_audio;
 
+	ret = snd_soc_set_ac97_ops(&nuc900_ac97_ops);
+	if (ret)
+		goto out;
+
 	ret = snd_soc_register_component(&pdev->dev, &nuc900_ac97_component,
 					 &nuc900_ac97_dai, 1);
 	if (ret)
-		goto out3;
+		goto out;
 
 	/* enbale ac97 multifunction pin */
 	mfp_set_groupg(nuc900_audio->dev, NULL);
 
 	return 0;
 
-out3:
-	clk_put(nuc900_audio->clk);
-out2:
-	iounmap(nuc900_audio->mmio);
-out1:
-	release_mem_region(nuc900_audio->res->start,
-					resource_size(nuc900_audio->res));
-out0:
-	kfree(nuc900_audio);
+out:
+	snd_soc_set_ac97_ops(NULL);
 	return ret;
 }
 
@@ -391,13 +378,8 @@
 {
 	snd_soc_unregister_component(&pdev->dev);
 
-	clk_put(nuc900_ac97_data->clk);
-	iounmap(nuc900_ac97_data->mmio);
-	release_mem_region(nuc900_ac97_data->res->start,
-				resource_size(nuc900_ac97_data->res));
-
-	kfree(nuc900_ac97_data);
 	nuc900_ac97_data = NULL;
+	snd_soc_set_ac97_ops(NULL);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 57ea8e6..1475515 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -41,13 +41,12 @@
 	pxa2xx_ac97_finish_reset(ac97);
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
 	.read	= pxa2xx_ac97_read,
 	.write	= pxa2xx_ac97_write,
 	.warm_reset	= pxa2xx_ac97_warm_reset,
 	.reset	= pxa2xx_ac97_cold_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
 	.name			= "AC97 PCM Stereo out",
@@ -239,11 +238,17 @@
 
 static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
 {
+	int ret;
+
 	if (pdev->id != -1) {
 		dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
 		return -ENXIO;
 	}
 
+	ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops);
+	if (ret != 0)
+		return ret;
+
 	/* Punt most of the init to the SoC probe; we may need the machine
 	 * driver to do interesting things with the clocking to get us up
 	 * and running.
@@ -255,6 +260,7 @@
 static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
 {
 	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_set_ac97_ops(NULL);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
index eda891e..a49c21b 100644
--- a/sound/soc/pxa/pxa2xx-ac97.h
+++ b/sound/soc/pxa/pxa2xx-ac97.h
@@ -14,7 +14,4 @@
 #define PXA2XX_DAI_AC97_AUX		1
 #define PXA2XX_DAI_AC97_MIC		2
 
-/* platform data */
-extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
-
 #endif
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index cb88ead..2dd623f 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -214,13 +214,12 @@
 	return IRQ_HANDLED;
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops s3c_ac97_ops = {
 	.read       = s3c_ac97_read,
 	.write      = s3c_ac97_write,
 	.warm_reset = s3c_ac97_warm_reset,
 	.reset      = s3c_ac97_cold_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
 				  struct snd_pcm_hw_params *params,
@@ -417,11 +416,9 @@
 		return -ENXIO;
 	}
 
-	if (!request_mem_region(mem_res->start,
-				resource_size(mem_res), "ac97")) {
-		dev_err(&pdev->dev, "Unable to request register region\n");
-		return -EBUSY;
-	}
+	s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (IS_ERR(s3c_ac97.regs))
+		return PTR_ERR(s3c_ac97.regs);
 
 	s3c_ac97_pcm_out.channel = dmatx_res->start;
 	s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
@@ -433,14 +430,7 @@
 	init_completion(&s3c_ac97.done);
 	mutex_init(&s3c_ac97.lock);
 
-	s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
-	if (s3c_ac97.regs == NULL) {
-		dev_err(&pdev->dev, "Unable to ioremap register region\n");
-		ret = -ENXIO;
-		goto err1;
-	}
-
-	s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
+	s3c_ac97.ac97_clk = devm_clk_get(&pdev->dev, "ac97");
 	if (IS_ERR(s3c_ac97.ac97_clk)) {
 		dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n");
 		ret = -ENODEV;
@@ -461,6 +451,12 @@
 		goto err4;
 	}
 
+	ret = snd_soc_set_ac97_ops(&s3c_ac97_ops);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+		goto err4;
+	}
+
 	ret = snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
 					 s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
 	if (ret)
@@ -480,18 +476,14 @@
 err4:
 err3:
 	clk_disable_unprepare(s3c_ac97.ac97_clk);
-	clk_put(s3c_ac97.ac97_clk);
 err2:
-	iounmap(s3c_ac97.regs);
-err1:
-	release_mem_region(mem_res->start, resource_size(mem_res));
-
+	snd_soc_set_ac97_ops(NULL);
 	return ret;
 }
 
 static int s3c_ac97_remove(struct platform_device *pdev)
 {
-	struct resource *mem_res, *irq_res;
+	struct resource *irq_res;
 
 	asoc_dma_platform_unregister(&pdev->dev);
 	snd_soc_unregister_component(&pdev->dev);
@@ -501,13 +493,7 @@
 		free_irq(irq_res->start, NULL);
 
 	clk_disable_unprepare(s3c_ac97.ac97_clk);
-	clk_put(s3c_ac97.ac97_clk);
-
-	iounmap(s3c_ac97.regs);
-
-	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (mem_res)
-		release_mem_region(mem_res->start, resource_size(mem_res));
+	snd_soc_set_ac97_ops(NULL);
 
 	return 0;
 }
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index af19f77..0af2e4d 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -227,13 +227,12 @@
 	hac_ac97_warmrst(ac97);
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops hac_ac97_ops = {
 	.read	= hac_ac97_read,
 	.write	= hac_ac97_write,
 	.reset	= hac_ac97_coldrst,
 	.warm_reset = hac_ac97_warmrst,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int hac_hw_params(struct snd_pcm_substream *substream,
 			 struct snd_pcm_hw_params *params,
@@ -316,6 +315,10 @@
 
 static int hac_soc_platform_probe(struct platform_device *pdev)
 {
+	ret = snd_soc_set_ac97_ops(&hac_ac97_ops);
+	if (ret != 0)
+		return ret;
+
 	return snd_soc_register_component(&pdev->dev, &sh4_hac_component,
 					  sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
 }
@@ -323,6 +326,7 @@
 static int hac_soc_platform_remove(struct platform_device *pdev)
 {
 	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_set_ac97_ops(NULL);
 	return 0;
 }
 
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index d56bbea..0a8a5f5 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2079,6 +2079,23 @@
 }
 EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
 
+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_free_ac97_codec - free AC97 codec device
  * @codec: audio codec
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 2f70ea7..e58233f 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -142,13 +142,12 @@
 	} while (!time_after(jiffies, timeout));
 }
 
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops tegra20_ac97_ops = {
 	.read		= tegra20_ac97_codec_read,
 	.write		= tegra20_ac97_codec_write,
 	.reset		= tegra20_ac97_codec_reset,
 	.warm_reset	= tegra20_ac97_codec_warm_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97)
 {
@@ -313,7 +312,7 @@
 static int tegra20_ac97_platform_probe(struct platform_device *pdev)
 {
 	struct tegra20_ac97 *ac97;
-	struct resource *mem, *memregion;
+	struct resource *mem;
 	u32 of_dma[2];
 	void __iomem *regs;
 	int ret = 0;
@@ -327,7 +326,7 @@
 	}
 	dev_set_drvdata(&pdev->dev, ac97);
 
-	ac97->clk_ac97 = clk_get(&pdev->dev, NULL);
+	ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(ac97->clk_ac97)) {
 		dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
 		ret = PTR_ERR(ac97->clk_ac97);
@@ -341,18 +340,9 @@
 		goto err_clk_put;
 	}
 
-	memregion = devm_request_mem_region(&pdev->dev, mem->start,
-					    resource_size(mem), DRV_NAME);
-	if (!memregion) {
-		dev_err(&pdev->dev, "Memory region already claimed\n");
-		ret = -EBUSY;
-		goto err_clk_put;
-	}
-
-	regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
-	if (!regs) {
-		dev_err(&pdev->dev, "ioremap failed\n");
-		ret = -ENOMEM;
+	regs = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(regs)) {
+		ret = PTR_ERR(regs);
 		goto err_clk_put;
 	}
 
@@ -403,23 +393,9 @@
 	ac97->capture_dma_data.maxburst = 4;
 	ac97->capture_dma_data.slave_id = of_dma[0];
 
-	ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
-					 &tegra20_ac97_dai, 1);
-	if (ret) {
-		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
-		ret = -ENOMEM;
-		goto err_clk_put;
-	}
-
-	ret = tegra_pcm_platform_register(&pdev->dev);
-	if (ret) {
-		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
-		goto err_unregister_component;
-	}
-
 	ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev);
 	if (ret)
-		goto err_unregister_pcm;
+		goto err_clk_put;
 
 	ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data);
 	if (ret)
@@ -431,20 +407,40 @@
 		goto err_asoc_utils_fini;
 	}
 
+	ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+		goto err_asoc_utils_fini;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
+					 &tegra20_ac97_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+		ret = -ENOMEM;
+		goto err_asoc_utils_fini;
+	}
+
+	ret = tegra_pcm_platform_register(&pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+		goto err_unregister_component;
+	}
+
 	/* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
 	workdata = ac97;
 
 	return 0;
 
-err_asoc_utils_fini:
-	tegra_asoc_utils_fini(&ac97->util_data);
 err_unregister_pcm:
 	tegra_pcm_platform_unregister(&pdev->dev);
 err_unregister_component:
 	snd_soc_unregister_component(&pdev->dev);
+err_asoc_utils_fini:
+	tegra_asoc_utils_fini(&ac97->util_data);
 err_clk_put:
-	clk_put(ac97->clk_ac97);
 err:
+	snd_soc_set_ac97_ops(NULL);
 	return ret;
 }
 
@@ -458,7 +454,8 @@
 	tegra_asoc_utils_fini(&ac97->util_data);
 
 	clk_disable_unprepare(ac97->clk_ac97);
-	clk_put(ac97->clk_ac97);
+
+	snd_soc_set_ac97_ops(NULL);
 
 	return 0;
 }
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 8a28403..4bcce8a 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -119,12 +119,11 @@
 }
 
 /* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops txx9aclc_ac97_ops = {
 	.read		= txx9aclc_ac97_read,
 	.write		= txx9aclc_ac97_write,
 	.reset		= txx9aclc_ac97_cold_reset,
 };
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id)
 {
@@ -188,9 +187,9 @@
 	if (!r)
 		return -EBUSY;
 
-	if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
-				     dev_name(&pdev->dev)))
-		return -EBUSY;
+	drvdata->base = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(drvdata->base))
+		return PTR_ERR(drvdata->base);
 
 	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
 	if (!drvdata)
@@ -201,14 +200,15 @@
 	    r->start >= TXX9_DIRECTMAP_BASE &&
 	    r->start < TXX9_DIRECTMAP_BASE + 0x400000)
 		drvdata->physbase |= 0xf00000000ull;
-	drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
-	if (!drvdata->base)
-		return -EBUSY;
 	err = devm_request_irq(&pdev->dev, irq, txx9aclc_ac97_irq,
 			       0, dev_name(&pdev->dev), drvdata);
 	if (err < 0)
 		return err;
 
+	err = snd_soc_set_ac97_ops(&txx9aclc_ac97_ops);
+	if (err < 0)
+		return err;
+
 	return snd_soc_register_component(&pdev->dev, &txx9aclc_ac97_component,
 					  &txx9aclc_ac97_dai, 1);
 }
@@ -216,6 +216,7 @@
 static int txx9aclc_ac97_dev_remove(struct platform_device *pdev)
 {
 	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_set_ac97_ops(NULL);
 	return 0;
 }