Merge remote-tracking branch 'asoc/topic/twl6040' into asoc-next
authorMark Brown <broonie@linaro.org>
Fri, 28 Jun 2013 11:17:06 +0000 (12:17 +0100)
committerMark Brown <broonie@linaro.org>
Fri, 28 Jun 2013 11:17:06 +0000 (12:17 +0100)
36 files changed:
Documentation/devicetree/bindings/sound/adi,adau1701.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ti,tas5086.txt
include/sound/soc.h
sound/soc/au1x/ac97c.c
sound/soc/au1x/psc-ac97.c
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/cirrus/ep93xx-ac97.c
sound/soc/codecs/88pm860x-codec.c
sound/soc/codecs/ac97.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/adau1701.c
sound/soc/codecs/stac9766.c
sound/soc/codecs/tas5086.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8991.h
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8995.h
sound/soc/codecs/wm9705.c
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/codecs/wm_hubs.c
sound/soc/fsl/imx-ssi.c
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/nuc900/nuc900-ac97.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/samsung/ac97.c
sound/soc/sh/hac.c
sound/soc/soc-core.c
sound/soc/tegra/tegra20_ac97.c
sound/soc/txx9/txx9aclc-ac97.c

diff --git a/Documentation/devicetree/bindings/sound/adi,adau1701.txt b/Documentation/devicetree/bindings/sound/adi,adau1701.txt
new file mode 100644 (file)
index 0000000..547a49b
--- /dev/null
@@ -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>;
+               };
+       };
index 8ea4f5b4818d22dfbb233350152c624e77aed56c..d2866a0d6a262694ddc7dfd447da87a37a031a23 100644 (file)
@@ -20,6 +20,17 @@ Optional properties:
                        When not specified, the hardware default of 1300ms
                        is retained.
 
+ - ti,mid-z-channel-X: Boolean properties, X being a number from 1 to 6.
+                       If given, channel X will start with the Mid-Z start
+                       sequence, otherwise the default Low-Z scheme is used.
+
+                       The correct configuration depends on how the power
+                       stages connected to the PWM output pins work. Not all
+                       power stages are compatible to Mid-Z - please refer
+                       to the datasheets for more details.
+
+                       Most systems should not set any of these properties.
+
 Examples:
 
        i2c_bus {
index 85c15226103b09c17c3b48b6366b182cac235ded..6eabee7ec15a90197ff3024f827d47cfaad91095 100644 (file)
@@ -340,7 +340,7 @@ struct snd_soc_jack_gpio;
 
 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 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
        struct snd_ac97_bus_ops *ops, int num);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
+
 /*
  *Controls
  */
index 44b8dcecf57179c16dbb695da2e0cad1ebfa8937..d6f7694fcad4f78ae1cb300d8d9fe3a4ef3b11d1 100644 (file)
@@ -179,13 +179,12 @@ static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97)
 }
 
 /* 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 @@ static int au1xac97c_drvprobe(struct platform_device *pdev)
 
        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 @@ static struct platform_driver au1xac97c_driver = {
        .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");
index 8f1862aa733311b35552079ace7a8fa314b4c3b6..a822ab822bb7ba40f5f1ead77a04a7caa7069233 100644 (file)
@@ -201,13 +201,12 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
 }
 
 /* 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 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
        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 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
 
        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 @@ static struct platform_driver au1xpsc_ac97_driver = {
        .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");
index 490217325975ab47f9c393fc66d01365e16410b1..c5af677ba49c6ba35156888b26b4498f03505814 100644 (file)
@@ -198,13 +198,12 @@ static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
 #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 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
 
 #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 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
                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 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
 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 @@ static int asoc_bfin_ac97_remove(struct platform_device *pdev)
 
        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;
 }
index 7798fbd5e81dac21ca7c5c6bb54836786510bd79..4bc9490e2c8480a752250503a014f720a2766174 100644 (file)
@@ -237,13 +237,12 @@ static irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
        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 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
        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 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
 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 @@ static int ep93xx_ac97_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
        ep93xx_ac97_info = NULL;
-       dev_set_drvdata(&pdev->dev, NULL);
+
+       snd_soc_set_ac97_ops(NULL);
 
        return 0;
 }
index 60159c07448da792e62e8e68f61e7d48d7ec04a4..e2bd3dd02c0e087d9da76c670a723aff646813e5 100644 (file)
  * 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;
index ef2ae32ffc669849bbfb791eb657ed4ff2501497..ec7351803c245b7ba209a582e0d82fc958effb24 100644 (file)
@@ -62,13 +62,13 @@ static struct snd_soc_dai_driver ac97_dai = {
 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 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
        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;
 
index f385342947d33317118d924fd5a88c44e3b9532c..89fcf7d6e7b8fdfb94bac498c935ad9bf3646fcc 100644 (file)
@@ -108,7 +108,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
        case AC97_EXTENDED_STATUS:
        case AC97_VENDOR_ID1:
        case AC97_VENDOR_ID2:
-               return soc_ac97_ops.read(codec->ac97, reg);
+               return soc_ac97_ops->read(codec->ac97, reg);
        default:
                reg = reg >> 1;
 
@@ -124,7 +124,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 {
        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 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
        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 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
 
        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;
index dafdbe87edeb56d82369db5f310658a25298ee17..0e250f118c0e3ff4b52165ca853648e72e78ce0a 100644 (file)
 #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>
 #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)
 #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 @@ static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
        { "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 @@ static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
                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 @@ static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
                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];
+
+       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 = snd_soc_cache_read(codec, reg, &value);
-       if (ret)
+       ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+       if (ret < 0)
                return ret;
+       else if (ret != ARRAY_SIZE(msgs))
+               return -EIO;
+
+       *value = 0;
+
+       for (i = 0; i < size; i++)
+               *value |= recv_buf[i] << (i * 8);
 
-       return value;
+       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,
@@ -259,8 +372,22 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
                struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
+       struct 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:
@@ -352,8 +479,8 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
 
        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;
@@ -403,6 +530,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        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:
@@ -416,6 +544,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        }
 
        snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
+       adau1701->sysclk = freq;
 
        return 0;
 }
@@ -452,18 +581,47 @@ static struct snd_soc_dai_driver adau1701_dai = {
        .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);
 
-       ret = adau1701_load_firmware(codec);
-       if (ret)
-               dev_warn(codec->dev, "Failed to load firmware\n");
+       /*
+        * 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;
+
+       /* initalize with pre-configured pll mode settings */
+       ret = adau1701_reset(codec, adau1701->pll_clkdiv);
+       if (ret < 0)
+               return ret;
+
+       /* set up pin config */
+       val = 0;
+       for (i = 0; i < 6; i++)
+               val |= adau1701->pin_config[i] << (i * 4);
 
-       snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
-       snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+       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 +631,6 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
        .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 +638,86 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
        .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 +740,7 @@ static struct i2c_driver adau1701_i2c_driver = {
        .driver = {
                .name   = "adau1701",
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(adau1701_dt_ids),
        },
        .probe          = adau1701_i2c_probe,
        .remove         = adau1701_i2c_remove,
index 2eda85ba79acd2cdd442b78fa4631542f0caffdd..a5455c1aea421daee730c56b1c0e4b1a3d2ce575 100644 (file)
@@ -28,8 +28,6 @@
 
 #include "stac9766.h"
 
-#define STAC9766_VERSION "0.10"
-
 /*
  * STAC9766 register cache
  */
@@ -145,14 +143,14 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 
        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 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
 
        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 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
                reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
                reg == AC97_VENDOR_ID2) {
 
-               val = soc_ac97_ops.read(codec->ac97, reg);
+               val = soc_ac97_ops->read(codec->ac97, reg);
                return val;
        }
        return cache[reg / 2];
@@ -242,15 +240,15 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
 
 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 @@ reset:
                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 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
 {
        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;
 
index d447c4aa1d5eb64bc06ee76f8d5c8aa2b752205a..6d31d88f72040098700a1b57ef3f4d118976a604 100644 (file)
 #define TAS5086_SPLIT_CAP_CHARGE       0x1a    /* Split cap charge period register */
 #define TAS5086_OSC_TRIM               0x1b    /* Oscillator trim register */
 #define TAS5086_BKNDERR                0x1c
+#define TAS5086_INPUT_MUX              0x20
+#define TAS5086_PWM_OUTPUT_MUX         0x25
+
+#define TAS5086_MAX_REGISTER           TAS5086_PWM_OUTPUT_MUX
+
+#define TAS5086_PWM_START_MIDZ_FOR_START_1     (1 << 7)
+#define TAS5086_PWM_START_MIDZ_FOR_START_2     (1 << 6)
+#define TAS5086_PWM_START_CHANNEL_MASK         (0x3f)
 
 /*
  * Default TAS5086 power-up configuration
@@ -119,9 +127,30 @@ static const struct reg_default tas5086_reg_defaults[] = {
        { 0x1c, 0x05 },
 };
 
+static int tas5086_register_size(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR:
+               return 1;
+       case TAS5086_INPUT_MUX:
+       case TAS5086_PWM_OUTPUT_MUX:
+               return 4;
+       }
+
+       dev_err(dev, "Unsupported register address: %d\n", reg);
+       return 0;
+}
+
 static bool tas5086_accessible_reg(struct device *dev, unsigned int reg)
 {
-       return !((reg == 0x0f) || (reg >= 0x11 && reg <= 0x17));
+       switch (reg) {
+       case 0x0f:
+       case 0x11 ... 0x17:
+       case 0x1d ... 0x1f:
+               return false;
+       default:
+               return true;
+       }
 }
 
 static bool tas5086_volatile_reg(struct device *dev, unsigned int reg)
@@ -140,6 +169,76 @@ static bool tas5086_writeable_reg(struct device *dev, unsigned int reg)
        return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID);
 }
 
+static int tas5086_reg_write(void *context, unsigned int reg,
+                             unsigned int value)
+{
+       struct i2c_client *client = context;
+       unsigned int i, size;
+       uint8_t buf[5];
+       int ret;
+
+       size = tas5086_register_size(&client->dev, reg);
+       if (size == 0)
+               return -EINVAL;
+
+       buf[0] = reg;
+
+       for (i = size; i >= 1; --i) {
+               buf[i] = value;
+               value >>= 8;
+       }
+
+       ret = i2c_master_send(client, buf, size + 1);
+       if (ret == size + 1)
+               return 0;
+       else if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
+
+static int tas5086_reg_read(void *context, unsigned int reg,
+                            unsigned int *value)
+{
+       struct i2c_client *client = context;
+       uint8_t send_buf, recv_buf[4];
+       struct i2c_msg msgs[2];
+       unsigned int size;
+       unsigned int i;
+       int ret;
+
+       size = tas5086_register_size(&client->dev, reg);
+       if (size == 0)
+               return -EINVAL;
+
+       send_buf = reg;
+
+       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;
+
+       *value = 0;
+
+       for (i = 0; i < size; i++) {
+               *value <<= 8;
+               *value |= recv_buf[i];
+       }
+
+       return 0;
+}
+
 struct tas5086_private {
        struct regmap   *regmap;
        unsigned int    mclk, sclk;
@@ -376,6 +475,202 @@ static const struct snd_kcontrol_new tas5086_controls[] = {
                            tas5086_get_deemph, tas5086_put_deemph),
 };
 
+/* Input mux controls */
+static const char *tas5086_dapm_sdin_texts[] =
+{
+       "SDIN1-L", "SDIN1-R", "SDIN2-L", "SDIN2-R",
+       "SDIN3-L", "SDIN3-R", "Ground (0)", "nc"
+};
+
+static const struct soc_enum tas5086_dapm_input_mux_enum[] = {
+       SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 20, 8, tas5086_dapm_sdin_texts),
+       SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 16, 8, tas5086_dapm_sdin_texts),
+       SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 12, 8, tas5086_dapm_sdin_texts),
+       SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 8,  8, tas5086_dapm_sdin_texts),
+       SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 4,  8, tas5086_dapm_sdin_texts),
+       SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 0,  8, tas5086_dapm_sdin_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_input_mux_controls[] = {
+       SOC_DAPM_ENUM("Channel 1 input", tas5086_dapm_input_mux_enum[0]),
+       SOC_DAPM_ENUM("Channel 2 input", tas5086_dapm_input_mux_enum[1]),
+       SOC_DAPM_ENUM("Channel 3 input", tas5086_dapm_input_mux_enum[2]),
+       SOC_DAPM_ENUM("Channel 4 input", tas5086_dapm_input_mux_enum[3]),
+       SOC_DAPM_ENUM("Channel 5 input", tas5086_dapm_input_mux_enum[4]),
+       SOC_DAPM_ENUM("Channel 6 input", tas5086_dapm_input_mux_enum[5]),
+};
+
+/* Output mux controls */
+static const char *tas5086_dapm_channel_texts[] =
+       { "Channel 1 Mux", "Channel 2 Mux", "Channel 3 Mux",
+         "Channel 4 Mux", "Channel 5 Mux", "Channel 6 Mux" };
+
+static const struct soc_enum tas5086_dapm_output_mux_enum[] = {
+       SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 20, 6, tas5086_dapm_channel_texts),
+       SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 16, 6, tas5086_dapm_channel_texts),
+       SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 12, 6, tas5086_dapm_channel_texts),
+       SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 8,  6, tas5086_dapm_channel_texts),
+       SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 4,  6, tas5086_dapm_channel_texts),
+       SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 0,  6, tas5086_dapm_channel_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_output_mux_controls[] = {
+       SOC_DAPM_ENUM("PWM1 Output", tas5086_dapm_output_mux_enum[0]),
+       SOC_DAPM_ENUM("PWM2 Output", tas5086_dapm_output_mux_enum[1]),
+       SOC_DAPM_ENUM("PWM3 Output", tas5086_dapm_output_mux_enum[2]),
+       SOC_DAPM_ENUM("PWM4 Output", tas5086_dapm_output_mux_enum[3]),
+       SOC_DAPM_ENUM("PWM5 Output", tas5086_dapm_output_mux_enum[4]),
+       SOC_DAPM_ENUM("PWM6 Output", tas5086_dapm_output_mux_enum[5]),
+};
+
+static const struct snd_soc_dapm_widget tas5086_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("SDIN1-L"),
+       SND_SOC_DAPM_INPUT("SDIN1-R"),
+       SND_SOC_DAPM_INPUT("SDIN2-L"),
+       SND_SOC_DAPM_INPUT("SDIN2-R"),
+       SND_SOC_DAPM_INPUT("SDIN3-L"),
+       SND_SOC_DAPM_INPUT("SDIN3-R"),
+       SND_SOC_DAPM_INPUT("SDIN4-L"),
+       SND_SOC_DAPM_INPUT("SDIN4-R"),
+
+       SND_SOC_DAPM_OUTPUT("PWM1"),
+       SND_SOC_DAPM_OUTPUT("PWM2"),
+       SND_SOC_DAPM_OUTPUT("PWM3"),
+       SND_SOC_DAPM_OUTPUT("PWM4"),
+       SND_SOC_DAPM_OUTPUT("PWM5"),
+       SND_SOC_DAPM_OUTPUT("PWM6"),
+
+       SND_SOC_DAPM_MUX("Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_input_mux_controls[0]),
+       SND_SOC_DAPM_MUX("Channel 2 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_input_mux_controls[1]),
+       SND_SOC_DAPM_MUX("Channel 3 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_input_mux_controls[2]),
+       SND_SOC_DAPM_MUX("Channel 4 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_input_mux_controls[3]),
+       SND_SOC_DAPM_MUX("Channel 5 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_input_mux_controls[4]),
+       SND_SOC_DAPM_MUX("Channel 6 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_input_mux_controls[5]),
+
+       SND_SOC_DAPM_MUX("PWM1 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_output_mux_controls[0]),
+       SND_SOC_DAPM_MUX("PWM2 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_output_mux_controls[1]),
+       SND_SOC_DAPM_MUX("PWM3 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_output_mux_controls[2]),
+       SND_SOC_DAPM_MUX("PWM4 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_output_mux_controls[3]),
+       SND_SOC_DAPM_MUX("PWM5 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_output_mux_controls[4]),
+       SND_SOC_DAPM_MUX("PWM6 Mux", SND_SOC_NOPM, 0, 0,
+                        &tas5086_dapm_output_mux_controls[5]),
+};
+
+static const struct snd_soc_dapm_route tas5086_dapm_routes[] = {
+       /* SDIN inputs -> channel muxes */
+       { "Channel 1 Mux", "SDIN1-L", "SDIN1-L" },
+       { "Channel 1 Mux", "SDIN1-R", "SDIN1-R" },
+       { "Channel 1 Mux", "SDIN2-L", "SDIN2-L" },
+       { "Channel 1 Mux", "SDIN2-R", "SDIN2-R" },
+       { "Channel 1 Mux", "SDIN3-L", "SDIN3-L" },
+       { "Channel 1 Mux", "SDIN3-R", "SDIN3-R" },
+
+       { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+       { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+       { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+       { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+       { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+       { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+       { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+       { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+       { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+       { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+       { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+       { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+       { "Channel 3 Mux", "SDIN1-L", "SDIN1-L" },
+       { "Channel 3 Mux", "SDIN1-R", "SDIN1-R" },
+       { "Channel 3 Mux", "SDIN2-L", "SDIN2-L" },
+       { "Channel 3 Mux", "SDIN2-R", "SDIN2-R" },
+       { "Channel 3 Mux", "SDIN3-L", "SDIN3-L" },
+       { "Channel 3 Mux", "SDIN3-R", "SDIN3-R" },
+
+       { "Channel 4 Mux", "SDIN1-L", "SDIN1-L" },
+       { "Channel 4 Mux", "SDIN1-R", "SDIN1-R" },
+       { "Channel 4 Mux", "SDIN2-L", "SDIN2-L" },
+       { "Channel 4 Mux", "SDIN2-R", "SDIN2-R" },
+       { "Channel 4 Mux", "SDIN3-L", "SDIN3-L" },
+       { "Channel 4 Mux", "SDIN3-R", "SDIN3-R" },
+
+       { "Channel 5 Mux", "SDIN1-L", "SDIN1-L" },
+       { "Channel 5 Mux", "SDIN1-R", "SDIN1-R" },
+       { "Channel 5 Mux", "SDIN2-L", "SDIN2-L" },
+       { "Channel 5 Mux", "SDIN2-R", "SDIN2-R" },
+       { "Channel 5 Mux", "SDIN3-L", "SDIN3-L" },
+       { "Channel 5 Mux", "SDIN3-R", "SDIN3-R" },
+
+       { "Channel 6 Mux", "SDIN1-L", "SDIN1-L" },
+       { "Channel 6 Mux", "SDIN1-R", "SDIN1-R" },
+       { "Channel 6 Mux", "SDIN2-L", "SDIN2-L" },
+       { "Channel 6 Mux", "SDIN2-R", "SDIN2-R" },
+       { "Channel 6 Mux", "SDIN3-L", "SDIN3-L" },
+       { "Channel 6 Mux", "SDIN3-R", "SDIN3-R" },
+
+       /* Channel muxes -> PWM muxes */
+       { "PWM1 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+       { "PWM2 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+       { "PWM3 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+       { "PWM4 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+       { "PWM5 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+       { "PWM6 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+
+       { "PWM1 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+       { "PWM2 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+       { "PWM3 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+       { "PWM4 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+       { "PWM5 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+       { "PWM6 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+
+       { "PWM1 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+       { "PWM2 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+       { "PWM3 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+       { "PWM4 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+       { "PWM5 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+       { "PWM6 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+
+       { "PWM1 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+       { "PWM2 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+       { "PWM3 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+       { "PWM4 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+       { "PWM5 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+       { "PWM6 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+
+       { "PWM1 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+       { "PWM2 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+       { "PWM3 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+       { "PWM4 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+       { "PWM5 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+       { "PWM6 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+
+       { "PWM1 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+       { "PWM2 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+       { "PWM3 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+       { "PWM4 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+       { "PWM5 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+       { "PWM6 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+
+       /* The PWM muxes are directly connected to the PWM outputs */
+       { "PWM1", NULL, "PWM1 Mux" },
+       { "PWM2", NULL, "PWM2 Mux" },
+       { "PWM3", NULL, "PWM3 Mux" },
+       { "PWM4", NULL, "PWM4 Mux" },
+       { "PWM5", NULL, "PWM5 Mux" },
+       { "PWM6", NULL, "PWM6 Mux" },
+
+};
+
 static const struct snd_soc_dai_ops tas5086_dai_ops = {
        .hw_params      = tas5086_hw_params,
        .set_sysclk     = tas5086_set_dai_sysclk,
@@ -426,13 +721,34 @@ static int tas5086_probe(struct snd_soc_codec *codec)
 {
        struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
        int charge_period = 1300000; /* hardware default is 1300 ms */
+       u8 pwm_start_mid_z = 0;
        int i, ret;
 
        if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) {
                struct device_node *of_node = codec->dev->of_node;
                of_property_read_u32(of_node, "ti,charge-period", &charge_period);
+
+               for (i = 0; i < 6; i++) {
+                       char name[25];
+
+                       snprintf(name, sizeof(name),
+                                "ti,mid-z-channel-%d", i + 1);
+
+                       if (of_get_property(of_node, name, NULL) != NULL)
+                               pwm_start_mid_z |= 1 << i;
+               }
        }
 
+       /*
+        * If any of the channels is configured to start in Mid-Z mode,
+        * configure 'part 1' of the PWM starts to use Mid-Z, and tell
+        * all configured mid-z channels to start start under 'part 1'.
+        */
+       if (pwm_start_mid_z)
+               regmap_write(priv->regmap, TAS5086_PWM_START,
+                            TAS5086_PWM_START_MIDZ_FOR_START_1 |
+                               pwm_start_mid_z);
+
        /* lookup and set split-capacitor charge period */
        if (charge_period == 0) {
                regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
@@ -490,6 +806,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
        .resume                 = tas5086_soc_resume,
        .controls               = tas5086_controls,
        .num_controls           = ARRAY_SIZE(tas5086_controls),
+       .dapm_widgets           = tas5086_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(tas5086_dapm_widgets),
+       .dapm_routes            = tas5086_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(tas5086_dapm_routes),
 };
 
 static const struct i2c_device_id tas5086_i2c_id[] = {
@@ -500,14 +820,16 @@ MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
 
 static const struct regmap_config tas5086_regmap = {
        .reg_bits               = 8,
-       .val_bits               = 8,
-       .max_register           = ARRAY_SIZE(tas5086_reg_defaults),
+       .val_bits               = 32,
+       .max_register           = TAS5086_MAX_REGISTER,
        .reg_defaults           = tas5086_reg_defaults,
        .num_reg_defaults       = ARRAY_SIZE(tas5086_reg_defaults),
        .cache_type             = REGCACHE_RBTREE,
        .volatile_reg           = tas5086_volatile_reg,
        .writeable_reg          = tas5086_writeable_reg,
        .readable_reg           = tas5086_accessible_reg,
+       .reg_read               = tas5086_reg_read,
+       .reg_write              = tas5086_reg_write,
 };
 
 static int tas5086_i2c_probe(struct i2c_client *i2c,
@@ -522,7 +844,7 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
        if (!priv)
                return -ENOMEM;
 
-       priv->regmap = devm_regmap_init_i2c(i2c, &tas5086_regmap);
+       priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap);
        if (IS_ERR(priv->regmap)) {
                ret = PTR_ERR(priv->regmap);
                dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
index 1514bf845e4b1e6e5e8ab37b7400632d5d60f3ad..e5b926883131180c71cfe6239d1bca3be8fcf674 100644 (file)
@@ -128,10 +128,8 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
 };
 
 #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,
index af6d227e67be02f72da11eaefa12a97d2fcd8c80..d2a092850283f36ed035f20e4e6cd0c51a20412b 100644 (file)
@@ -143,13 +143,8 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
 }
 
 #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[] =
index 9d88437cdcd1db314b444022f25ce91e11466cb5..fa24cedee68769f2b723e2fb8b25c03869ee286a 100644 (file)
@@ -403,10 +403,8 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
 }
 
 #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 };
index 3ff195c541dbf839e946e036f1f314aa27662f38..4c9fb142cb2d21d18ac90dd1b6e0272bf48c7e24 100644 (file)
@@ -603,13 +603,8 @@ SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0,
 
 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[] = {
index 837978e16e9dc85150b96ed5b8671cd569138e38..253c88bb7a4cbdb06d3d1df2862f10e80095aaa8 100644 (file)
@@ -151,14 +151,9 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
 }
 
 #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[] =
index 8a942efd18a50d318ca2da224334b2ef7d09f0a1..07707d8d7e20e8ef2625f4986c3b24f70285bc5a 100644 (file)
 
 #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 */
index 29e95f93d482201a91cd0523c008bb2ca2b01bd3..9e13edd81292fcf8a3a85eb3e7747df28b5bab00 100644 (file)
@@ -289,10 +289,8 @@ static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
 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 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
 };
 
 #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)
index 5642121c4977481e9abd25e74925983f361309e2..508ad27fe2bb1a84b08bfb267f89f9c53e4558cf 100644 (file)
 #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;
index 05b1f346695bce8d10c7e406ed6413a744117102..70ce6793c5bd51cc8f32c893d28932e2db6358da 100644 (file)
@@ -209,7 +209,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
        case AC97_RESET:
        case AC97_VENDOR_ID1:
        case AC97_VENDOR_ID2:
-               return soc_ac97_ops.read(codec->ac97, reg);
+               return soc_ac97_ops->read(codec->ac97, reg);
        default:
                reg = reg >> 1;
 
@@ -225,7 +225,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 {
        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 struct snd_soc_dai_driver wm9705_dai[] = {
 
 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 @@ static int wm9705_reset(struct snd_soc_codec *codec)
 #ifdef CONFIG_PM
 static int wm9705_soc_suspend(struct snd_soc_codec *codec)
 {
-       soc_ac97_ops.write(codec->ac97, AC97_POWERDOWN, 0xffff);
+       soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
 
        return 0;
 }
@@ -323,7 +323,7 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
        }
 
        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 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
 {
        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;
index 8e9a6a3eeb1a257886415be532a186a1900de1e7..c5eb746087b4103394420fbdf42fff6602399297 100644 (file)
@@ -455,7 +455,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
        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 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        u16 *cache = codec->reg_cache;
 
        if (reg < 0x7c)
-               soc_ac97_ops.write(codec->ac97, reg, val);
+               soc_ac97_ops->write(codec->ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9712_reg)))
                cache[reg] = val;
@@ -581,15 +581,15 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
 
 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 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
                        if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
                            (i > 0x58 && i != 0x5c))
                                continue;
-                       soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+                       soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
                }
        }
 
@@ -635,7 +635,7 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
 {
        int ret = 0;
 
-       ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
        if (ret < 0) {
                printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
                return ret;
index f7afa68d8c7fce0a119eb98810e969609e28e225..a53e175c015ad54d60ff6ce085680613ee19d28d 100644 (file)
@@ -652,7 +652,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
        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 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 {
        u16 *cache = codec->reg_cache;
        if (reg < 0x7c)
-               soc_ac97_ops.write(codec->ac97, reg, val);
+               soc_ac97_ops->write(codec->ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9713_reg)))
                cache[reg] = val;
@@ -1095,15 +1095,15 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
 
 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 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
                        if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
                                i == AC97_EXTENDED_MSTATUS || i > 0x66)
                                continue;
-                       soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+                       soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
                }
        }
 
@@ -1197,7 +1197,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
                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;
 
index 3470b649c0b26b6479ea89c9a1ecd799cbc6ce7c..ddba3fea39ebb4565f34f154aff94f76c2962f06 100644 (file)
@@ -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 @@ static struct {
        [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 unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
        }
 }
 
+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 @@ out:
        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 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        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 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        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 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        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 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        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 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        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 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
        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 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                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 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                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 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
 
                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 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        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 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                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 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                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 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                                        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 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
 
        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);
index fea514627526b18e3556a94137967da7371f0ec1..9f922c82536c52f8939db5834bbcfda80e1c5b59 100644 (file)
@@ -30,6 +30,7 @@ struct wm_adsp_alg_region {
        unsigned int alg;
        int type;
        unsigned int base;
+       size_t len;
 };
 
 struct wm_adsp {
@@ -55,17 +56,17 @@ struct wm_adsp {
        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[];
index f5d81b9487598753e3f0ce4c93b766d7d3347f54..2d9e099415a58cbe353c99f32f6f089a200db2de 100644 (file)
@@ -693,10 +693,8 @@ void wm_hubs_update_class_w(struct snd_soc_codec *codec)
 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)
index c6fa03e2114ab985186cb8f86a3ee6f4317dc4bc..bd40849454a89e2f0dd197b26f0076eda39a741d 100644 (file)
@@ -501,13 +501,12 @@ static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
        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 @@ static int imx_ssi_probe(struct platform_device *pdev)
 
        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 @@ failed_register:
        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 @@ static int imx_ssi_remove(struct platform_device *pdev)
 
        release_mem_region(res->start, resource_size(res));
        clk_disable_unprepare(ssi->clk);
+       snd_soc_set_ac97_ops(NULL);
 
        return 0;
 }
index 4141b35ef0bbd27947d3d4147e482b34d45f58a1..3ef7a0c92efa07eb4719aadfefacf5c050a008cd 100644 (file)
@@ -131,13 +131,12 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
        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 @@ static int psc_ac97_of_probe(struct platform_device *op)
        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 @@ static int psc_ac97_of_remove(struct platform_device *op)
 {
        mpc5200_audio_dma_destroy(op);
        snd_soc_unregister_component(&op->dev);
+       snd_soc_set_ac97_ops(NULL);
        return 0;
 }
 
index fe3285ceaf5bdc4b8903260aed762ab78b02a890..f4c2417a8730bcf0ae99c37fe1bfcc76a685cb51 100644 (file)
@@ -197,13 +197,12 @@ static void nuc900_ac97_cold_reset(struct snd_ac97 *ac97)
 }
 
 /* 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 @@ static int nuc900_ac97_drvprobe(struct platform_device *pdev)
        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 (!request_mem_region(nuc900_audio->res->start,
-                       resource_size(nuc900_audio->res), pdev->name)) {
-               ret = -EBUSY;
-               goto out0;
-       }
+       if (!nuc900_audio->res)
+               return ret;
 
-       nuc900_audio->mmio = ioremap(nuc900_audio->res->start,
-                                       resource_size(nuc900_audio->res));
-       if (!nuc900_audio->mmio) {
-               ret = -ENOMEM;
-               goto out1;
-       }
+       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->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 @@ static int nuc900_ac97_drvremove(struct platform_device *pdev)
 {
        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;
 }
index 57ea8e6c54885372f7808376968b7f56783ef78a..a3c22ba25f08326311625429b93748e3edfbc212 100644 (file)
@@ -41,13 +41,12 @@ static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
        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",
@@ -244,6 +243,10 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
                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 +258,7 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
 static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
+       snd_soc_set_ac97_ops(NULL);
        return 0;
 }
 
index cb88ead98917b354c459673ae879a80a37b40ad5..2dd623fa3882b8fe17ceab2c6a86f45f54059f9d 100644 (file)
@@ -214,13 +214,12 @@ static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
        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 @@ static int s3c_ac97_probe(struct platform_device *pdev)
                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 @@ static int s3c_ac97_probe(struct platform_device *pdev)
        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 @@ static int s3c_ac97_probe(struct platform_device *pdev)
                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 @@ err5:
 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 @@ static int s3c_ac97_remove(struct platform_device *pdev)
                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;
 }
index af19f77b7bf0fa84c101170b5bee45cad6920f26..0af2e4dfd13949a502e670846a84e098bff1719a 100644 (file)
@@ -227,13 +227,12 @@ static void hac_ac97_coldrst(struct snd_ac97 *ac97)
        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 const struct snd_soc_component_driver sh4_hac_component = {
 
 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_probe(struct platform_device *pdev)
 static int hac_soc_platform_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
+       snd_soc_set_ac97_ops(NULL);
        return 0;
 }
 
index d56bbea6e75e60158405f45b1cf280f4d0fb9ac1..562d72e04e6e09c17ef9b3452187429e2f567452 100644 (file)
@@ -2079,6 +2079,22 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
 
+struct snd_ac97_bus_ops *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
index 2f70ea7f6618ae04dc8f125925638d4e9f90c147..f52eab6d2231ff581e64d4006a244548544ec87d 100644 (file)
@@ -142,13 +142,12 @@ static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd,
        } 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)
 {
@@ -327,7 +326,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
        }
        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,10 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
                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);
+               dev_err(&pdev->dev, "ioremap failed: %d\n", ret);
                goto err_clk_put;
        }
 
@@ -403,23 +394,9 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
        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 +408,40 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
                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 +455,8 @@ static int tegra20_ac97_platform_remove(struct platform_device *pdev)
        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;
 }
index 8a2840304d288e80cbce6e0b7946d354eaa8b1cb..4bcce8a3cdedd618e18844a55373c273cbd6427c 100644 (file)
@@ -119,12 +119,11 @@ static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97)
 }
 
 /* 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 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
        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 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
            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_probe(struct platform_device *pdev)
 static int txx9aclc_ac97_dev_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
+       snd_soc_set_ac97_ops(NULL);
        return 0;
 }