ASoC: adau1701: Implement sigmadsp safeload
authorLars-Peter Clausen <lars@metafoo.de>
Wed, 19 Nov 2014 17:29:07 +0000 (18:29 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 20 Nov 2014 09:55:35 +0000 (09:55 +0000)
The safeload feature allows to load up to 5 parameter memory registers
atomically. This is helpful for switching between e.g. filter settings
without causing any glitches.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/adau1701.c

index 05d5eb5..d4e219b 100644 (file)
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
+#include <asm/unaligned.h>
+
 #include "sigmadsp.h"
 #include "adau1701.h"
 
+#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i))
+#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i))
+
 #define ADAU1701_DSPCTRL       0x081c
 #define ADAU1701_SEROCTL       0x081e
 #define ADAU1701_SERICTL       0x081f
@@ -42,6 +47,7 @@
 #define ADAU1701_DSPCTRL_CR            (1 << 2)
 #define ADAU1701_DSPCTRL_DAM           (1 << 3)
 #define ADAU1701_DSPCTRL_ADM           (1 << 4)
+#define ADAU1701_DSPCTRL_IST           (1 << 5)
 #define ADAU1701_DSPCTRL_SR_48         0x00
 #define ADAU1701_DSPCTRL_SR_96         0x01
 #define ADAU1701_DSPCTRL_SR_192                0x02
@@ -102,6 +108,7 @@ struct adau1701 {
        unsigned int pll_clkdiv;
        unsigned int sysclk;
        struct regmap *regmap;
+       struct i2c_client *client;
        u8 pin_config[12];
 
        struct sigmadsp *sigmadsp;
@@ -161,6 +168,7 @@ static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
        case ADAU1701_DACSET:
+       case ADAU1701_DSPCTRL:
                return true;
        default:
                return false;
@@ -240,6 +248,50 @@ static int adau1701_reg_read(void *context, unsigned int reg,
        return 0;
 }
 
+static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+       const uint8_t bytes[], size_t len)
+{
+       struct i2c_client *client = to_i2c_client(sigmadsp->dev);
+       struct adau1701 *adau1701 = i2c_get_clientdata(client);
+       unsigned int val;
+       unsigned int i;
+       uint8_t buf[10];
+       int ret;
+
+       ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val);
+       if (ret)
+               return ret;
+
+       if (val & ADAU1701_DSPCTRL_IST)
+               msleep(50);
+
+       for (i = 0; i < len / 4; i++) {
+               put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf);
+               buf[2] = 0x00;
+               memcpy(buf + 3, bytes + i * 4, 4);
+               ret = i2c_master_send(client, buf, 7);
+               if (ret < 0)
+                       return ret;
+               else if (ret != 7)
+                       return -EIO;
+
+               put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf);
+               put_unaligned_le16(addr + i, buf + 2);
+               ret = i2c_master_send(client, buf, 4);
+               if (ret < 0)
+                       return ret;
+               else if (ret != 4)
+                       return -EIO;
+       }
+
+       return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
+               ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST);
+}
+
+static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
+       .safeload = adau1701_safeload,
+};
+
 static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
        unsigned int rate)
 {
@@ -684,6 +736,7 @@ static int adau1701_i2c_probe(struct i2c_client *client,
        if (!adau1701)
                return -ENOMEM;
 
+       adau1701->client = client;
        adau1701->regmap = devm_regmap_init(dev, NULL, client,
                                            &adau1701_regmap);
        if (IS_ERR(adau1701->regmap))
@@ -740,8 +793,8 @@ static int adau1701_i2c_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, adau1701);
 
-       adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, NULL,
-               ADAU1701_FIRMWARE);
+       adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
+               &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE);
        if (IS_ERR(adau1701->sigmadsp))
                return PTR_ERR(adau1701->sigmadsp);