CS35L41: Smart PA driver
authorlijilai <lijilai@huaqin.com>
Thu, 19 Jul 2018 10:52:06 +0000 (18:52 +0800)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:23:42 +0000 (20:23 +0300)
[CS35L41] Added SPA driver

Bug:HQ00000000

Workaround:no

Change-Id: I5d7232539dbd7b9ff97765e4d709e16165916a22
Signed-off-by: Shinhyung Kang<s47.kang@samsung.com>
14 files changed:
include/sound/cs35l41.h [new file with mode: 0755]
sound/soc/codecs/Kconfig [changed mode: 0644->0755]
sound/soc/codecs/Makefile [changed mode: 0644->0755]
sound/soc/codecs/cs35l41-i2c.c [new file with mode: 0755]
sound/soc/codecs/cs35l41-spi.c [new file with mode: 0755]
sound/soc/codecs/cs35l41-tables.c [new file with mode: 0755]
sound/soc/codecs/cs35l41.c [new file with mode: 0755]
sound/soc/codecs/cs35l41.h [new file with mode: 0755]
sound/soc/codecs/cs47l35.c [changed mode: 0644->0755]
sound/soc/codecs/madera-slimbus.c [new file with mode: 0755]
sound/soc/codecs/madera-slimbus.h [new file with mode: 0755]
sound/soc/codecs/wm_adsp.c [changed mode: 0644->0755]
sound/soc/codecs/wm_adsp.h [changed mode: 0644->0755]
sound/soc/codecs/wmfw.h [changed mode: 0644->0755]

diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
new file mode 100755 (executable)
index 0000000..872fe4b
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * linux/sound/cs35l41.h -- Platform data for CS35L41
+ *
+ * Copyright (c) 2018 Cirrus Logic Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CS35L41_H
+#define __CS35L41_H
+
+struct classh_cfg {
+       bool classh_bst_override;
+       bool classh_algo_enable;
+       int classh_bst_max_limit;
+       int classh_mem_depth;
+       int classh_release_rate;
+       int classh_headroom;
+       int classh_wk_fet_delay;
+       int classh_wk_fet_thld;
+};
+
+struct irq_cfg {
+       bool is_present;
+       bool irq_pol_inv;
+       bool irq_out_en;
+       int irq_src_sel;
+};
+
+struct cs35l41_platform_data {
+       bool sclk_frc;
+       bool lrclk_frc;
+       bool right_channel;
+       bool amp_gain_zc;
+       bool ng_enable;
+       int bst_ind;
+       int bst_vctrl;
+       int bst_ipk;
+       int bst_cap;
+       int temp_warn_thld;
+       int ng_pcm_thld;
+       int ng_delay;
+       int dout_hiz;
+       struct irq_cfg irq_config1;
+       struct irq_cfg irq_config2;
+       struct classh_cfg classh_config;
+};
+
+struct cs35l41_private {
+       struct wm_adsp dsp; /* needs to be first member */
+       struct snd_soc_codec *codec;
+       struct cs35l41_platform_data pdata;
+       struct device *dev;
+       struct regmap *regmap;
+       struct regulator_bulk_data supplies[2];
+       int num_supplies;
+       int irq;
+       int clksrc;
+       int extclk_freq;
+       int extclk_cfg;
+       int sclk;
+       bool tdm_mode;
+       bool i2s_mode;
+       bool swire_mode;
+       bool halo_booted;
+       bool bus_spi;
+       struct mutex rate_lock;
+       /* GPIO for /RST */
+       struct gpio_desc *reset_gpio;
+       struct completion global_pup_done;
+       struct completion global_pdn_done;
+};
+
+int cs35l41_probe(struct cs35l41_private *cs35l41,
+                               struct cs35l41_platform_data *pdata);
+
+#endif /* __CS35L41_H */
old mode 100644 (file)
new mode 100755 (executable)
index 6af4b91..45b30d7
@@ -52,6 +52,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS35L35 if I2C
        select SND_SOC_CS42L42 if I2C
        select SND_SOC_CS42L51_I2C if I2C
+       select SND_SOC_CS35L41_SPI if SPI_MASTER
+       select SND_SOC_CS35L41_I2C if I2C
        select SND_SOC_CS42L52 if I2C && INPUT
        select SND_SOC_CS42L56 if I2C && INPUT
        select SND_SOC_CS42L73 if I2C
@@ -433,6 +435,21 @@ config SND_SOC_CS35L35
 config SND_SOC_CS42L42
        tristate "Cirrus Logic CS42L42 CODEC"
        depends on I2C
+config SND_SOC_CS35L41
+       tristate
+       select SND_SOC_WM_ADSP
+
+config SND_SOC_CS35L41_SPI
+       tristate "Cirrus Logic CS35L41 CODEC (SPI)"
+       depends on SPI_MASTER
+       select SND_SOC_CS35L41
+       select REGMAP_SPI
+
+config SND_SOC_CS35L41_I2C
+       tristate "Cirrus Logic CS35L41 CODEC (I2C)"
+       depends on I2C
+       select SND_SOC_CS35L41
+       select REGMAP_I2C
 
 config SND_SOC_CS42L51
        tristate
old mode 100644 (file)
new mode 100755 (executable)
index f176ad6..820bfe2
@@ -42,6 +42,9 @@ snd-soc-cs35l33-objs := cs35l33.o
 snd-soc-cs35l34-objs := cs35l34.o
 snd-soc-cs35l35-objs := cs35l35.o
 snd-soc-cs42l42-objs := cs42l42.o
+snd-soc-cs35l41-objs := cs35l41.o
+snd-soc-cs35l41-spi-objs := cs35l41-spi.o cs35l41-tables.o
+snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o cs35l41-tables.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
 snd-soc-cs42l52-objs := cs42l52.o
@@ -86,6 +89,7 @@ snd-soc-l3-objs := l3.o
 snd-soc-lm4857-objs := lm4857.o
 snd-soc-lm49453-objs := lm49453.o
 snd-soc-madera-objs := madera.o
+
 snd-soc-max9768-objs := max9768.o
 snd-soc-max98088-objs := max98088.o
 snd-soc-max98090-objs := max98090.o
@@ -288,6 +292,9 @@ obj-$(CONFIG_SND_SOC_CS35L33)       += snd-soc-cs35l33.o
 obj-$(CONFIG_SND_SOC_CS35L34)  += snd-soc-cs35l34.o
 obj-$(CONFIG_SND_SOC_CS35L35)  += snd-soc-cs35l35.o
 obj-$(CONFIG_SND_SOC_CS42L42)  += snd-soc-cs42l42.o
+obj-$(CONFIG_SND_SOC_CS35L41)  += snd-soc-cs35l41.o
+obj-$(CONFIG_SND_SOC_CS35L41_SPI)      += snd-soc-cs35l41-spi.o
+obj-$(CONFIG_SND_SOC_CS35L41_I2C)      += snd-soc-cs35l41-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L51)  += snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)      += snd-soc-cs42l51-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L52)  += snd-soc-cs42l52.o
@@ -332,6 +339,7 @@ obj-$(CONFIG_SND_SOC_L3)    += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_LM4857)   += snd-soc-lm4857.o
 obj-$(CONFIG_SND_SOC_LM49453)   += snd-soc-lm49453.o
 obj-$(CONFIG_SND_SOC_MADERA)   += snd-soc-madera.o
+obj-$(CONFIG_SND_SOC_MADERA_SLIMBUS)   += snd-soc-madera-slimbus.o
 obj-$(CONFIG_SND_SOC_MAX9768)  += snd-soc-max9768.o
 obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
new file mode 100755 (executable)
index 0000000..2d6e590
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * cs35l41-i2c.c -- CS35l41 I2C driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author:     David Rhodes    <david.rhodes@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/gpio.h>
+
+#include "wm_adsp.h"
+#include "cs35l41.h"
+#include <sound/cs35l41.h>
+
+static struct regmap_config cs35l41_regmap_i2c = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = CS35L41_LASTREG,
+       .reg_defaults = cs35l41_reg,
+       .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+       .volatile_reg = cs35l41_volatile_reg,
+       .readable_reg = cs35l41_readable_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct i2c_device_id cs35l41_id_i2c[] = {
+       {"cs35l40", 0},
+       {"cs35l41", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c);
+
+static int cs35l41_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct cs35l41_private *cs35l41;
+       struct device *dev = &client->dev;
+       struct cs35l41_platform_data *pdata = dev_get_platdata(dev);
+       const struct regmap_config *regmap_config = &cs35l41_regmap_i2c;
+       int ret;
+
+       cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_private), GFP_KERNEL);
+
+       if (cs35l41 == NULL)
+               return -ENOMEM;
+
+       cs35l41->dev = dev;
+       cs35l41->irq = client->irq;
+       cs35l41->bus_spi = false;
+
+       i2c_set_clientdata(client, cs35l41);
+       cs35l41->regmap = devm_regmap_init_i2c(client, regmap_config);
+       if (IS_ERR(cs35l41->regmap)) {
+               ret = PTR_ERR(cs35l41->regmap);
+               dev_err(cs35l41->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       return cs35l41_probe(cs35l41, pdata);
+}
+
+static int cs35l41_i2c_remove(struct i2c_client *client)
+{
+       struct cs35l41_private *cs35l41 = i2c_get_clientdata(client);
+
+       regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+       wm_adsp2_remove(&cs35l41->dsp);
+       regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies);
+       snd_soc_unregister_codec(cs35l41->dev);
+       return 0;
+}
+
+static const struct of_device_id cs35l41_of_match[] = {
+       {.compatible = "cirrus,cs35l40"},
+       {.compatible = "cirrus,cs35l41"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+
+static struct i2c_driver cs35l41_i2c_driver = {
+       .driver = {
+               .name           = "cs35l41",
+               .of_match_table = cs35l41_of_match,
+       },
+       .id_table       = cs35l41_id_i2c,
+       .probe          = cs35l41_i2c_probe,
+       .remove         = cs35l41_i2c_remove,
+};
+
+module_i2c_driver(cs35l41_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
new file mode 100755 (executable)
index 0000000..f4bf632
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * cs35l41-spi.c -- CS35l41 SPI driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author:     David Rhodes    <david.rhodes@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+
+#include "wm_adsp.h"
+#include "cs35l41.h"
+#include <sound/cs35l41.h>
+
+static struct regmap_config cs35l41_regmap_spi = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .pad_bits = 16,
+       .reg_stride = 4,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = CS35L41_LASTREG,
+       .reg_defaults = cs35l41_reg,
+       .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+       .volatile_reg = cs35l41_volatile_reg,
+       .readable_reg = cs35l41_readable_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct spi_device_id cs35l41_id_spi[] = {
+       {"cs35l40", 0},
+       {"cs35l41", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(spi, cs35l41_id_spi);
+
+static int cs35l41_spi_probe(struct spi_device *spi)
+{
+       const struct regmap_config *regmap_config = &cs35l41_regmap_spi;
+       struct cs35l41_platform_data *pdata =
+                                       dev_get_platdata(&spi->dev);
+       struct cs35l41_private *cs35l41;
+       int ret;
+
+       cs35l41 = devm_kzalloc(&spi->dev,
+                              sizeof(struct cs35l41_private),
+                              GFP_KERNEL);
+       if (cs35l41 == NULL)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, cs35l41);
+       cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config);
+       if (IS_ERR(cs35l41->regmap)) {
+               ret = PTR_ERR(cs35l41->regmap);
+               dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       cs35l41->dev = &spi->dev;
+       cs35l41->irq = spi->irq;
+       cs35l41->bus_spi = true;
+
+       return cs35l41_probe(cs35l41, pdata);
+}
+
+static int cs35l41_spi_remove(struct spi_device *spi)
+{
+       struct cs35l41_private *cs35l41 = spi_get_drvdata(spi);
+
+       regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+       wm_adsp2_remove(&cs35l41->dsp);
+       regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies);
+       snd_soc_unregister_codec(cs35l41->dev);
+       return 0;
+}
+
+static const struct of_device_id cs35l41_of_match[] = {
+       {.compatible = "cirrus,cs35l40"},
+       {.compatible = "cirrus,cs35l41"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+
+static struct spi_driver cs35l41_spi_driver = {
+       .driver = {
+               .name           = "cs35l41",
+               .of_match_table = cs35l41_of_match,
+       },
+       .id_table       = cs35l41_id_spi,
+       .probe          = cs35l41_spi_probe,
+       .remove         = cs35l41_spi_remove,
+};
+
+module_spi_driver(cs35l41_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-tables.c
new file mode 100755 (executable)
index 0000000..e3e09f0
--- /dev/null
@@ -0,0 +1,913 @@
+/*
+ * cs35l41-tables.c -- CS35L41 ALSA SoC audio driver
+ *
+ * Copyright 2018 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *         David Rhodes <david.rhodes@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "cs35l41.h"
+
+const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = {
+       {CS35L41_TEST_KEY_CTL,                  0x00000000},
+       {CS35L41_USER_KEY_CTL,                  0x00000000},
+       {CS35L41_OTP_MEM0,                      0x00000000},
+       {CS35L41_OTP_MEM31,                     0x00000000},
+       {CS35L41_OTP_CTRL0,                     0x00006419},
+       {CS35L41_OTP_CTRL1,                     0x00000000},
+       {CS35L41_OTP_CTRL3,                     0x00000000},
+       {CS35L41_OTP_CTRL4,                     0x00000000},
+       {CS35L41_OTP_CTRL5,                     0x00000030},
+       {CS35L41_OTP_CTRL6,                     0x00000000},
+       {CS35L41_OTP_CTRL7,                     0x00000000},
+       {CS35L41_OTP_CTRL8,                     0x00000000},
+       {CS35L41_PWR_CTRL1,                     0x00000000},
+       {CS35L41_PWR_CTRL2,                     0x00003321},
+       {CS35L41_PWR_CTRL3,                     0x01000011},
+       {CS35L41_CTRL_OVRRIDE,                  0x00000002},
+       {CS35L41_AMP_OUT_MUTE,                  0x00000000},
+       {CS35L41_PROTECT_REL_ERR_IGN,           0x00000000},
+       {CS35L41_GPIO_PAD_CONTROL,              0x00000000},
+       {CS35L41_JTAG_CONTROL,                  0x00000000},
+       {CS35L41_PLL_CLK_CTRL,                  0x00000010},
+       {CS35L41_DSP_CLK_CTRL,                  0x00000003},
+       {CS35L41_GLOBAL_CLK_CTRL,               0x00000003},
+       {CS35L41_DATA_FS_SEL,                   0x00000000},
+       {CS35L41_MDSYNC_EN,                     0x00000000},
+       {CS35L41_MDSYNC_TX_ID,                  0x00000000},
+       {CS35L41_MDSYNC_PWR_CTRL,               0x00000002},
+       {CS35L41_MDSYNC_DATA_TX,                0x00000000},
+       {CS35L41_MDSYNC_TX_STATUS,              0x00000002},
+       {CS35L41_MDSYNC_DATA_RX,                0x00000000},
+       {CS35L41_MDSYNC_RX_STATUS,              0x00000002},
+       {CS35L41_MDSYNC_ERR_STATUS,             0x00000000},
+       {CS35L41_MDSYNC_SYNC_PTE2,              0x00000000},
+       {CS35L41_MDSYNC_SYNC_PTE3,              0x00000000},
+       {CS35L41_MDSYNC_SYNC_MSM_STATUS,        0x00000000},
+       {CS35L41_BSTCVRT_VCTRL1,                0x00000000},
+       {CS35L41_BSTCVRT_VCTRL2,                0x00000001},
+       {CS35L41_BSTCVRT_PEAK_CUR,              0x0000004A},
+       {CS35L41_BSTCVRT_SFT_RAMP,              0x00000003},
+       {CS35L41_BSTCVRT_COEFF,                 0x00002424},
+       {CS35L41_BSTCVRT_SLOPE_LBST,            0x00007500},
+       {CS35L41_BSTCVRT_SW_FREQ,               0x01008000},
+       {CS35L41_BSTCVRT_DCM_CTRL,              0x00002001},
+       {CS35L41_BSTCVRT_DCM_MODE_FORCE,        0x00000000},
+       {CS35L41_BSTCVRT_OVERVOLT_CTRL,         0x00000130},
+       {CS35L41_VI_VOL_POL,                    0x08000800},
+       {CS35L41_DTEMP_WARN_THLD,               0x00000002},
+       {CS35L41_DTEMP_EN,                      0x00000000},
+       {CS35L41_VPVBST_FS_SEL,                 0x00000001},
+       {CS35L41_SP_ENABLES,                    0x00000000},
+       {CS35L41_SP_RATE_CTRL,                  0x01000028},
+       {CS35L41_SP_FORMAT,                     0x18180200},
+       {CS35L41_SP_HIZ_CTRL,                   0x00000002},
+       {CS35L41_SP_FRAME_TX_SLOT,              0x03020100},
+       {CS35L41_SP_FRAME_RX_SLOT,              0x00000100},
+       {CS35L41_SP_TX_WL,                      0x00000018},
+       {CS35L41_SP_RX_WL,                      0x00000018},
+       {CS35L41_DAC_PCM1_SRC,                  0x00000008},
+       {CS35L41_ASP_TX1_SRC,                   0x00000018},
+       {CS35L41_ASP_TX2_SRC,                   0x00000019},
+       {CS35L41_ASP_TX3_SRC,                   0x00000020},
+       {CS35L41_ASP_TX4_SRC,                   0x00000021},
+       {CS35L41_DSP1_RX1_SRC,                  0x00000008},
+       {CS35L41_DSP1_RX2_SRC,                  0x00000009},
+       {CS35L41_DSP1_RX3_SRC,                  0x00000018},
+       {CS35L41_DSP1_RX4_SRC,                  0x00000019},
+       {CS35L41_DSP1_RX5_SRC,                  0x00000020},
+       {CS35L41_DSP1_RX6_SRC,                  0x00000021},
+       {CS35L41_DSP1_RX7_SRC,                  0x0000003A},
+       {CS35L41_DSP1_RX8_SRC,                  0x00000001},
+       {CS35L41_NGATE1_SRC,                    0x00000008},
+       {CS35L41_NGATE2_SRC,                    0x00000009},
+       {CS35L41_AMP_DIG_VOL_CTRL,              0x00008000},
+       {CS35L41_VPBR_CFG,                      0x02AA1905},
+       {CS35L41_VBBR_CFG,                      0x02AA1905},
+       {CS35L41_VPBR_STATUS,                   0x00000000},
+       {CS35L41_VBBR_STATUS,                   0x00000000},
+       {CS35L41_OVERTEMP_CFG,                  0x00000001},
+       {CS35L41_AMP_ERR_VOL,                   0x00000000},
+       {CS35L41_VOL_STATUS_TO_DSP,             0x00000000},
+       {CS35L41_CLASSH_CFG,                    0x000B0405},
+       {CS35L41_WKFET_CFG,                     0x00000111},
+       {CS35L41_NG_CFG,                        0x00000033},
+       {CS35L41_AMP_GAIN_CTRL,                 0x00000273},
+       {CS35L41_DAC_MSM_CFG,                   0x00580000},
+       {CS35L41_GPIO_STATUS1,                  0x00000000},
+       {CS35L41_GPIO1_CTRL1,                   0xE1000001},
+       {CS35L41_GPIO2_CTRL1,                   0xE1000001},
+       {CS35L41_MIXER_NGATE_CFG,               0x00000000},
+       {CS35L41_MIXER_NGATE_CH1_CFG,           0x00000303},
+       {CS35L41_MIXER_NGATE_CH2_CFG,           0x00000303},
+       {CS35L41_CLOCK_DETECT_1,                0x00000000},
+       {CS35L41_TIMER1_CONTROL,                0x00000000},
+       {CS35L41_TIMER1_COUNT_PRESET,           0x00000000},
+       {CS35L41_TIMER1_START_STOP,             0x00000000},
+       {CS35L41_TIMER1_STATUS,                 0x00000000},
+       {CS35L41_TIMER1_COUNT_READBACK,         0x00000000},
+       {CS35L41_TIMER1_DSP_CLK_CFG,            0x00000000},
+       {CS35L41_TIMER1_DSP_CLK_STATUS,         0x00000000},
+       {CS35L41_TIMER2_CONTROL,                0x00000000},
+       {CS35L41_TIMER2_COUNT_PRESET,           0x00000000},
+       {CS35L41_TIMER2_START_STOP,             0x00000000},
+       {CS35L41_TIMER2_STATUS,                 0x00000000},
+       {CS35L41_TIMER2_COUNT_READBACK,         0x00000000},
+       {CS35L41_TIMER2_DSP_CLK_CFG,            0x00000000},
+       {CS35L41_TIMER2_DSP_CLK_STATUS,         0x00000000},
+       {CS35L41_DFT_JTAG_CONTROL,              0x00000000},
+       {CS35L41_DIE_STS1,                      0x00000000},
+       {CS35L41_DIE_STS2,                      0x00000000},
+       {CS35L41_TEMP_CAL1,                     0x00000000},
+       {CS35L41_TEMP_CAL2,                     0x00000000},
+};
+
+bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L41_DEVID:
+       case CS35L41_REVID:
+       case CS35L41_FABID:
+       case CS35L41_RELID:
+       case CS35L41_OTPID:
+       case CS35L41_TEST_KEY_CTL:
+       case CS35L41_USER_KEY_CTL:
+       case CS35L41_OTP_CTRL0:
+       case CS35L41_OTP_CTRL3:
+       case CS35L41_OTP_CTRL4:
+       case CS35L41_OTP_CTRL5:
+       case CS35L41_OTP_CTRL6:
+       case CS35L41_OTP_CTRL7:
+       case CS35L41_OTP_CTRL8:
+       case CS35L41_PWR_CTRL1:
+       case CS35L41_PWR_CTRL2:
+       case CS35L41_PWR_CTRL3:
+       case CS35L41_CTRL_OVRRIDE:
+       case CS35L41_AMP_OUT_MUTE:
+       case CS35L41_PROTECT_REL_ERR_IGN:
+       case CS35L41_GPIO_PAD_CONTROL:
+       case CS35L41_JTAG_CONTROL:
+       case CS35L41_PLL_CLK_CTRL:
+       case CS35L41_DSP_CLK_CTRL:
+       case CS35L41_GLOBAL_CLK_CTRL:
+       case CS35L41_DATA_FS_SEL:
+       case CS35L41_MDSYNC_EN:
+       case CS35L41_MDSYNC_TX_ID:
+       case CS35L41_MDSYNC_PWR_CTRL:
+       case CS35L41_MDSYNC_DATA_TX:
+       case CS35L41_MDSYNC_TX_STATUS:
+       case CS35L41_MDSYNC_DATA_RX:
+       case CS35L41_MDSYNC_RX_STATUS:
+       case CS35L41_MDSYNC_ERR_STATUS:
+       case CS35L41_MDSYNC_SYNC_PTE2:
+       case CS35L41_MDSYNC_SYNC_PTE3:
+       case CS35L41_MDSYNC_SYNC_MSM_STATUS:
+       case CS35L41_BSTCVRT_VCTRL1:
+       case CS35L41_BSTCVRT_VCTRL2:
+       case CS35L41_BSTCVRT_PEAK_CUR:
+       case CS35L41_BSTCVRT_SFT_RAMP:
+       case CS35L41_BSTCVRT_COEFF:
+       case CS35L41_BSTCVRT_SLOPE_LBST:
+       case CS35L41_BSTCVRT_SW_FREQ:
+       case CS35L41_BSTCVRT_DCM_CTRL:
+       case CS35L41_BSTCVRT_DCM_MODE_FORCE:
+       case CS35L41_BSTCVRT_OVERVOLT_CTRL:
+       case CS35L41_VI_VOL_POL:
+       case CS35L41_DTEMP_WARN_THLD:
+       case CS35L41_DTEMP_CFG:
+       case CS35L41_DTEMP_EN:
+       case CS35L41_VPVBST_FS_SEL:
+       case CS35L41_SP_ENABLES:
+       case CS35L41_SP_RATE_CTRL:
+       case CS35L41_SP_FORMAT:
+       case CS35L41_SP_HIZ_CTRL:
+       case CS35L41_SP_FRAME_TX_SLOT:
+       case CS35L41_SP_FRAME_RX_SLOT:
+       case CS35L41_SP_TX_WL:
+       case CS35L41_SP_RX_WL:
+       case CS35L41_DAC_PCM1_SRC:
+       case CS35L41_ASP_TX1_SRC:
+       case CS35L41_ASP_TX2_SRC:
+       case CS35L41_ASP_TX3_SRC:
+       case CS35L41_ASP_TX4_SRC:
+       case CS35L41_DSP1_RX1_SRC:
+       case CS35L41_DSP1_RX2_SRC:
+       case CS35L41_DSP1_RX3_SRC:
+       case CS35L41_DSP1_RX4_SRC:
+       case CS35L41_DSP1_RX5_SRC:
+       case CS35L41_DSP1_RX6_SRC:
+       case CS35L41_DSP1_RX7_SRC:
+       case CS35L41_DSP1_RX8_SRC:
+       case CS35L41_NGATE1_SRC:
+       case CS35L41_NGATE2_SRC:
+       case CS35L41_AMP_DIG_VOL_CTRL:
+       case CS35L41_VPBR_CFG:
+       case CS35L41_VBBR_CFG:
+       case CS35L41_VPBR_STATUS:
+       case CS35L41_VBBR_STATUS:
+       case CS35L41_OVERTEMP_CFG:
+       case CS35L41_AMP_ERR_VOL:
+       case CS35L41_VOL_STATUS_TO_DSP:
+       case CS35L41_CLASSH_CFG:
+       case CS35L41_WKFET_CFG:
+       case CS35L41_NG_CFG:
+       case CS35L41_AMP_GAIN_CTRL:
+       case CS35L41_DAC_MSM_CFG:
+       case CS35L41_IRQ1_CFG:
+       case CS35L41_IRQ1_STATUS:
+       case CS35L41_IRQ1_STATUS1:
+       case CS35L41_IRQ1_STATUS2:
+       case CS35L41_IRQ1_STATUS3:
+       case CS35L41_IRQ1_STATUS4:
+       case CS35L41_IRQ1_RAW_STATUS1:
+       case CS35L41_IRQ1_RAW_STATUS2:
+       case CS35L41_IRQ1_RAW_STATUS3:
+       case CS35L41_IRQ1_RAW_STATUS4:
+       case CS35L41_IRQ1_MASK1:
+       case CS35L41_IRQ1_MASK2:
+       case CS35L41_IRQ1_MASK3:
+       case CS35L41_IRQ1_MASK4:
+       case CS35L41_IRQ1_FRC1:
+       case CS35L41_IRQ1_FRC2:
+       case CS35L41_IRQ1_FRC3:
+       case CS35L41_IRQ1_FRC4:
+       case CS35L41_IRQ1_EDGE1:
+       case CS35L41_IRQ1_EDGE4:
+       case CS35L41_IRQ1_POL1:
+       case CS35L41_IRQ1_POL2:
+       case CS35L41_IRQ1_POL3:
+       case CS35L41_IRQ1_POL4:
+       case CS35L41_IRQ1_DB3:
+       case CS35L41_IRQ2_CFG:
+       case CS35L41_IRQ2_STATUS:
+       case CS35L41_IRQ2_STATUS1:
+       case CS35L41_IRQ2_STATUS2:
+       case CS35L41_IRQ2_STATUS3:
+       case CS35L41_IRQ2_STATUS4:
+       case CS35L41_IRQ2_RAW_STATUS1:
+       case CS35L41_IRQ2_RAW_STATUS2:
+       case CS35L41_IRQ2_RAW_STATUS3:
+       case CS35L41_IRQ2_RAW_STATUS4:
+       case CS35L41_IRQ2_MASK1:
+       case CS35L41_IRQ2_MASK2:
+       case CS35L41_IRQ2_MASK3:
+       case CS35L41_IRQ2_MASK4:
+       case CS35L41_IRQ2_FRC1:
+       case CS35L41_IRQ2_FRC2:
+       case CS35L41_IRQ2_FRC3:
+       case CS35L41_IRQ2_FRC4:
+       case CS35L41_IRQ2_EDGE1:
+       case CS35L41_IRQ2_EDGE4:
+       case CS35L41_IRQ2_POL1:
+       case CS35L41_IRQ2_POL2:
+       case CS35L41_IRQ2_POL3:
+       case CS35L41_IRQ2_POL4:
+       case CS35L41_IRQ2_DB3:
+       case CS35L41_GPIO_STATUS1:
+       case CS35L41_GPIO1_CTRL1:
+       case CS35L41_GPIO2_CTRL1:
+       case CS35L41_MIXER_NGATE_CFG:
+       case CS35L41_MIXER_NGATE_CH1_CFG:
+       case CS35L41_MIXER_NGATE_CH2_CFG:
+       case CS35L41_CLOCK_DETECT_1:
+       case CS35L41_TIMER1_CONTROL:
+       case CS35L41_TIMER1_COUNT_PRESET:
+       case CS35L41_TIMER1_STATUS:
+       case CS35L41_TIMER1_COUNT_READBACK:
+       case CS35L41_TIMER1_DSP_CLK_CFG:
+       case CS35L41_TIMER1_DSP_CLK_STATUS:
+       case CS35L41_TIMER2_CONTROL:
+       case CS35L41_TIMER2_COUNT_PRESET:
+       case CS35L41_TIMER2_STATUS:
+       case CS35L41_TIMER2_COUNT_READBACK:
+       case CS35L41_TIMER2_DSP_CLK_CFG:
+       case CS35L41_TIMER2_DSP_CLK_STATUS:
+       case CS35L41_DFT_JTAG_CONTROL:
+       case CS35L41_DIE_STS1:
+       case CS35L41_DIE_STS2:
+       case CS35L41_TEMP_CAL1:
+       case CS35L41_TEMP_CAL2:
+       case CS35L41_DSP1_TIMESTAMP_COUNT:
+       case CS35L41_DSP1_SYS_ID:
+       case CS35L41_DSP1_SYS_VERSION:
+       case CS35L41_DSP1_SYS_CORE_ID:
+       case CS35L41_DSP1_SYS_AHB_ADDR:
+       case CS35L41_DSP1_SYS_XSRAM_SIZE:
+       case CS35L41_DSP1_SYS_YSRAM_SIZE:
+       case CS35L41_DSP1_SYS_PSRAM_SIZE:
+       case CS35L41_DSP1_SYS_PM_BOOT_SIZE:
+       case CS35L41_DSP1_SYS_FEATURES:
+       case CS35L41_DSP1_SYS_FIR_FILTERS:
+       case CS35L41_DSP1_SYS_LMS_FILTERS:
+       case CS35L41_DSP1_SYS_XM_BANK_SIZE:
+       case CS35L41_DSP1_SYS_YM_BANK_SIZE:
+       case CS35L41_DSP1_SYS_PM_BANK_SIZE:
+       case CS35L41_DSP1_AHBM_WIN0_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN0_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN1_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN1_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN2_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN2_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN3_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN3_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN4_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN4_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN5_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN5_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN6_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN6_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN7_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN7_CTRL1:
+       case CS35L41_DSP1_AHBM_WIN_DBG_CTRL0:
+       case CS35L41_DSP1_AHBM_WIN_DBG_CTRL1:
+       case CS35L41_DSP1_DEBUG:
+       case CS35L41_DSP1_TIMER_CTRL:
+       case CS35L41_DSP1_RX1_RATE:
+       case CS35L41_DSP1_RX2_RATE:
+       case CS35L41_DSP1_RX3_RATE:
+       case CS35L41_DSP1_RX4_RATE:
+       case CS35L41_DSP1_RX5_RATE:
+       case CS35L41_DSP1_RX6_RATE:
+       case CS35L41_DSP1_RX7_RATE:
+       case CS35L41_DSP1_RX8_RATE:
+       case CS35L41_DSP1_TX1_RATE:
+       case CS35L41_DSP1_TX2_RATE:
+       case CS35L41_DSP1_TX3_RATE:
+       case CS35L41_DSP1_TX4_RATE:
+       case CS35L41_DSP1_TX5_RATE:
+       case CS35L41_DSP1_TX6_RATE:
+       case CS35L41_DSP1_TX7_RATE:
+       case CS35L41_DSP1_TX8_RATE:
+       case CS35L41_DSP1_NMI_CTRL1:
+       case CS35L41_DSP1_NMI_CTRL2:
+       case CS35L41_DSP1_NMI_CTRL3:
+       case CS35L41_DSP1_NMI_CTRL4:
+       case CS35L41_DSP1_NMI_CTRL5:
+       case CS35L41_DSP1_NMI_CTRL6:
+       case CS35L41_DSP1_NMI_CTRL7:
+       case CS35L41_DSP1_NMI_CTRL8:
+       case CS35L41_DSP1_RESUME_CTRL:
+       case CS35L41_DSP1_IRQ1_CTRL:
+       case CS35L41_DSP1_IRQ2_CTRL:
+       case CS35L41_DSP1_IRQ3_CTRL:
+       case CS35L41_DSP1_IRQ4_CTRL:
+       case CS35L41_DSP1_IRQ5_CTRL:
+       case CS35L41_DSP1_IRQ6_CTRL:
+       case CS35L41_DSP1_IRQ7_CTRL:
+       case CS35L41_DSP1_IRQ8_CTRL:
+       case CS35L41_DSP1_IRQ9_CTRL:
+       case CS35L41_DSP1_IRQ10_CTRL:
+       case CS35L41_DSP1_IRQ11_CTRL:
+       case CS35L41_DSP1_IRQ12_CTRL:
+       case CS35L41_DSP1_IRQ13_CTRL:
+       case CS35L41_DSP1_IRQ14_CTRL:
+       case CS35L41_DSP1_IRQ15_CTRL:
+       case CS35L41_DSP1_IRQ16_CTRL:
+       case CS35L41_DSP1_IRQ17_CTRL:
+       case CS35L41_DSP1_IRQ18_CTRL:
+       case CS35L41_DSP1_IRQ19_CTRL:
+       case CS35L41_DSP1_IRQ20_CTRL:
+       case CS35L41_DSP1_IRQ21_CTRL:
+       case CS35L41_DSP1_IRQ22_CTRL:
+       case CS35L41_DSP1_IRQ23_CTRL:
+       case CS35L41_DSP1_SCRATCH1:
+       case CS35L41_DSP1_SCRATCH2:
+       case CS35L41_DSP1_SCRATCH3:
+       case CS35L41_DSP1_SCRATCH4:
+       case CS35L41_DSP1_CCM_CORE_CTRL:
+       case CS35L41_DSP1_CCM_CLK_OVERRIDE:
+       case CS35L41_DSP1_XM_MSTR_EN:
+       case CS35L41_DSP1_XM_CORE_PRI:
+       case CS35L41_DSP1_XM_AHB_PACK_PL_PRI:
+       case CS35L41_DSP1_XM_AHB_UP_PL_PRI:
+       case CS35L41_DSP1_XM_ACCEL_PL0_PRI:
+       case CS35L41_DSP1_XM_NPL0_PRI:
+       case CS35L41_DSP1_YM_MSTR_EN:
+       case CS35L41_DSP1_YM_CORE_PRI:
+       case CS35L41_DSP1_YM_AHB_PACK_PL_PRI:
+       case CS35L41_DSP1_YM_AHB_UP_PL_PRI:
+       case CS35L41_DSP1_YM_ACCEL_PL0_PRI:
+       case CS35L41_DSP1_YM_NPL0_PRI:
+       case CS35L41_DSP1_PM_MSTR_EN:
+       case CS35L41_DSP1_PM_PATCH0_ADDR:
+       case CS35L41_DSP1_PM_PATCH0_EN:
+       case CS35L41_DSP1_PM_PATCH0_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH0_DATA_HI:
+       case CS35L41_DSP1_PM_PATCH1_ADDR:
+       case CS35L41_DSP1_PM_PATCH1_EN:
+       case CS35L41_DSP1_PM_PATCH1_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH1_DATA_HI:
+       case CS35L41_DSP1_PM_PATCH2_ADDR:
+       case CS35L41_DSP1_PM_PATCH2_EN:
+       case CS35L41_DSP1_PM_PATCH2_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH2_DATA_HI:
+       case CS35L41_DSP1_PM_PATCH3_ADDR:
+       case CS35L41_DSP1_PM_PATCH3_EN:
+       case CS35L41_DSP1_PM_PATCH3_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH3_DATA_HI:
+       case CS35L41_DSP1_PM_PATCH4_ADDR:
+       case CS35L41_DSP1_PM_PATCH4_EN:
+       case CS35L41_DSP1_PM_PATCH4_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH4_DATA_HI:
+       case CS35L41_DSP1_PM_PATCH5_ADDR:
+       case CS35L41_DSP1_PM_PATCH5_EN:
+       case CS35L41_DSP1_PM_PATCH5_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH5_DATA_HI:
+       case CS35L41_DSP1_PM_PATCH6_ADDR:
+       case CS35L41_DSP1_PM_PATCH6_EN:
+       case CS35L41_DSP1_PM_PATCH6_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH6_DATA_HI:
+       case CS35L41_DSP1_PM_PATCH7_ADDR:
+       case CS35L41_DSP1_PM_PATCH7_EN:
+       case CS35L41_DSP1_PM_PATCH7_DATA_LO:
+       case CS35L41_DSP1_PM_PATCH7_DATA_HI:
+       case CS35L41_DSP1_MPU_XM_ACCESS0:
+       case CS35L41_DSP1_MPU_YM_ACCESS0:
+       case CS35L41_DSP1_MPU_WNDW_ACCESS0:
+       case CS35L41_DSP1_MPU_XREG_ACCESS0:
+       case CS35L41_DSP1_MPU_YREG_ACCESS0:
+       case CS35L41_DSP1_MPU_XM_ACCESS1:
+       case CS35L41_DSP1_MPU_YM_ACCESS1:
+       case CS35L41_DSP1_MPU_WNDW_ACCESS1:
+       case CS35L41_DSP1_MPU_XREG_ACCESS1:
+       case CS35L41_DSP1_MPU_YREG_ACCESS1:
+       case CS35L41_DSP1_MPU_XM_ACCESS2:
+       case CS35L41_DSP1_MPU_YM_ACCESS2:
+       case CS35L41_DSP1_MPU_WNDW_ACCESS2:
+       case CS35L41_DSP1_MPU_XREG_ACCESS2:
+       case CS35L41_DSP1_MPU_YREG_ACCESS2:
+       case CS35L41_DSP1_MPU_XM_ACCESS3:
+       case CS35L41_DSP1_MPU_YM_ACCESS3:
+       case CS35L41_DSP1_MPU_WNDW_ACCESS3:
+       case CS35L41_DSP1_MPU_XREG_ACCESS3:
+       case CS35L41_DSP1_MPU_YREG_ACCESS3:
+       case CS35L41_DSP1_MPU_XM_VIO_ADDR:
+       case CS35L41_DSP1_MPU_XM_VIO_STATUS:
+       case CS35L41_DSP1_MPU_YM_VIO_ADDR:
+       case CS35L41_DSP1_MPU_YM_VIO_STATUS:
+       case CS35L41_DSP1_MPU_PM_VIO_ADDR:
+       case CS35L41_DSP1_MPU_PM_VIO_STATUS:
+       case CS35L41_DSP1_MPU_LOCK_CONFIG:
+       case CS35L41_DSP1_MPU_WDT_RST_CTRL:
+       case CS35L41_DSP1_STRMARB_MSTR0_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR0_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR0_CFG2:
+       case CS35L41_DSP1_STRMARB_MSTR1_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR1_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR1_CFG2:
+       case CS35L41_DSP1_STRMARB_MSTR2_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR2_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR2_CFG2:
+       case CS35L41_DSP1_STRMARB_MSTR3_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR3_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR3_CFG2:
+       case CS35L41_DSP1_STRMARB_MSTR4_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR4_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR4_CFG2:
+       case CS35L41_DSP1_STRMARB_MSTR5_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR5_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR5_CFG2:
+       case CS35L41_DSP1_STRMARB_MSTR6_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR6_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR6_CFG2:
+       case CS35L41_DSP1_STRMARB_MSTR7_CFG0:
+       case CS35L41_DSP1_STRMARB_MSTR7_CFG1:
+       case CS35L41_DSP1_STRMARB_MSTR7_CFG2:
+       case CS35L41_DSP1_STRMARB_TX0_CFG0:
+       case CS35L41_DSP1_STRMARB_TX0_CFG1:
+       case CS35L41_DSP1_STRMARB_TX1_CFG0:
+       case CS35L41_DSP1_STRMARB_TX1_CFG1:
+       case CS35L41_DSP1_STRMARB_TX2_CFG0:
+       case CS35L41_DSP1_STRMARB_TX2_CFG1:
+       case CS35L41_DSP1_STRMARB_TX3_CFG0:
+       case CS35L41_DSP1_STRMARB_TX3_CFG1:
+       case CS35L41_DSP1_STRMARB_TX4_CFG0:
+       case CS35L41_DSP1_STRMARB_TX4_CFG1:
+       case CS35L41_DSP1_STRMARB_TX5_CFG0:
+       case CS35L41_DSP1_STRMARB_TX5_CFG1:
+       case CS35L41_DSP1_STRMARB_TX6_CFG0:
+       case CS35L41_DSP1_STRMARB_TX6_CFG1:
+       case CS35L41_DSP1_STRMARB_TX7_CFG0:
+       case CS35L41_DSP1_STRMARB_TX7_CFG1:
+       case CS35L41_DSP1_STRMARB_RX0_CFG0:
+       case CS35L41_DSP1_STRMARB_RX0_CFG1:
+       case CS35L41_DSP1_STRMARB_RX1_CFG0:
+       case CS35L41_DSP1_STRMARB_RX1_CFG1:
+       case CS35L41_DSP1_STRMARB_RX2_CFG0:
+       case CS35L41_DSP1_STRMARB_RX2_CFG1:
+       case CS35L41_DSP1_STRMARB_RX3_CFG0:
+       case CS35L41_DSP1_STRMARB_RX3_CFG1:
+       case CS35L41_DSP1_STRMARB_RX4_CFG0:
+       case CS35L41_DSP1_STRMARB_RX4_CFG1:
+       case CS35L41_DSP1_STRMARB_RX5_CFG0:
+       case CS35L41_DSP1_STRMARB_RX5_CFG1:
+       case CS35L41_DSP1_STRMARB_RX6_CFG0:
+       case CS35L41_DSP1_STRMARB_RX6_CFG1:
+       case CS35L41_DSP1_STRMARB_RX7_CFG0:
+       case CS35L41_DSP1_STRMARB_RX7_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ0_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ0_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ0_CFG2:
+       case CS35L41_DSP1_STRMARB_IRQ1_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ1_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ1_CFG2:
+       case CS35L41_DSP1_STRMARB_IRQ2_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ2_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ2_CFG2:
+       case CS35L41_DSP1_STRMARB_IRQ3_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ3_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ3_CFG2:
+       case CS35L41_DSP1_STRMARB_IRQ4_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ4_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ4_CFG2:
+       case CS35L41_DSP1_STRMARB_IRQ5_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ5_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ5_CFG2:
+       case CS35L41_DSP1_STRMARB_IRQ6_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ6_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ6_CFG2:
+       case CS35L41_DSP1_STRMARB_IRQ7_CFG0:
+       case CS35L41_DSP1_STRMARB_IRQ7_CFG1:
+       case CS35L41_DSP1_STRMARB_IRQ7_CFG2:
+       case CS35L41_DSP1_STRMARB_RESYNC_MSK:
+       case CS35L41_DSP1_STRMARB_ERR_STATUS:
+       case CS35L41_DSP1_INTPCTL_RES_STATIC:
+       case CS35L41_DSP1_INTPCTL_RES_DYN:
+       case CS35L41_DSP1_INTPCTL_NMI_CTRL:
+       case CS35L41_DSP1_INTPCTL_IRQ_INV:
+       case CS35L41_DSP1_INTPCTL_IRQ_MODE:
+       case CS35L41_DSP1_INTPCTL_IRQ_EN:
+       case CS35L41_DSP1_INTPCTL_IRQ_MSK:
+       case CS35L41_DSP1_INTPCTL_IRQ_ERR:
+       case CS35L41_DSP1_INTPCTL_IRQ_PEND:
+       case CS35L41_DSP1_INTPCTL_TESTBITS:
+       case CS35L41_DSP1_WDT_CONTROL:
+       case CS35L41_DSP1_WDT_STATUS:
+       case CS35L41_OTP_TRIM_1:
+       case CS35L41_OTP_TRIM_2:
+       case CS35L41_OTP_TRIM_3:
+       case CS35L41_OTP_TRIM_4:
+       case CS35L41_OTP_TRIM_5:
+       case CS35L41_OTP_TRIM_6:
+       case CS35L41_OTP_TRIM_7:
+       case CS35L41_OTP_TRIM_8:
+       case CS35L41_OTP_TRIM_9:
+       case CS35L41_OTP_TRIM_10:
+       case CS35L41_OTP_TRIM_11:
+       case CS35L41_OTP_TRIM_12:
+       case CS35L41_OTP_TRIM_13:
+       case CS35L41_OTP_TRIM_14:
+       case CS35L41_OTP_TRIM_15:
+       case CS35L41_OTP_TRIM_16:
+       case CS35L41_OTP_TRIM_17:
+       case CS35L41_OTP_TRIM_18:
+       case CS35L41_OTP_TRIM_19:
+       case CS35L41_OTP_TRIM_20:
+       case CS35L41_OTP_TRIM_21:
+       case CS35L41_OTP_TRIM_22:
+       case CS35L41_OTP_TRIM_23:
+       case CS35L41_OTP_TRIM_24:
+       case CS35L41_OTP_TRIM_25:
+       case CS35L41_OTP_TRIM_26:
+       case CS35L41_OTP_TRIM_27:
+       case CS35L41_OTP_TRIM_28:
+       case CS35L41_OTP_TRIM_29:
+       case CS35L41_OTP_TRIM_30:
+       case CS35L41_OTP_TRIM_31:
+       case CS35L41_OTP_TRIM_32:
+       case CS35L41_OTP_TRIM_33:
+       case CS35L41_OTP_TRIM_34:
+       case CS35L41_OTP_TRIM_35:
+       case CS35L41_OTP_TRIM_36:
+       case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+       case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+       case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+       case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+       case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+       case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+       case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+       case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+       /*test regs*/
+       case CS35L41_PLL_OVR:
+       case CS35L41_BST_TEST_DUTY:
+       case CS35L41_DIGPWM_IOCTRL:
+               return true;
+       default:
+               return false;
+       }
+}
+
+bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L41_SFT_RESET:
+       case CS35L41_FABID:
+       case CS35L41_REVID:
+       case CS35L41_IRQ1_STATUS:
+       case CS35L41_IRQ1_STATUS1:
+       case CS35L41_IRQ1_STATUS2:
+       case CS35L41_IRQ1_STATUS3:
+       case CS35L41_IRQ1_STATUS4:
+       case CS35L41_IRQ1_RAW_STATUS1:
+       case CS35L41_IRQ1_RAW_STATUS2:
+       case CS35L41_IRQ1_RAW_STATUS3:
+       case CS35L41_IRQ1_RAW_STATUS4:
+       case CS35L41_IRQ1_MASK1:
+       case CS35L41_IRQ1_MASK2:
+       case CS35L41_IRQ1_MASK3:
+       case CS35L41_IRQ1_MASK4:
+       case CS35L41_IRQ1_FRC1:
+       case CS35L41_IRQ1_FRC2:
+       case CS35L41_IRQ1_FRC3:
+       case CS35L41_IRQ1_FRC4:
+       case CS35L41_IRQ1_EDGE1:
+       case CS35L41_IRQ1_EDGE4:
+       case CS35L41_IRQ1_POL1:
+       case CS35L41_IRQ1_POL2:
+       case CS35L41_IRQ1_POL3:
+       case CS35L41_IRQ1_POL4:
+       case CS35L41_IRQ1_DB3:
+       case CS35L41_IRQ2_STATUS:
+       case CS35L41_IRQ2_STATUS1:
+       case CS35L41_IRQ2_STATUS2:
+       case CS35L41_IRQ2_STATUS3:
+       case CS35L41_IRQ2_STATUS4:
+       case CS35L41_IRQ2_RAW_STATUS1:
+       case CS35L41_IRQ2_RAW_STATUS2:
+       case CS35L41_IRQ2_RAW_STATUS3:
+       case CS35L41_IRQ2_RAW_STATUS4:
+       case CS35L41_IRQ2_MASK1:
+       case CS35L41_IRQ2_MASK2:
+       case CS35L41_IRQ2_MASK3:
+       case CS35L41_IRQ2_MASK4:
+       case CS35L41_IRQ2_FRC1:
+       case CS35L41_IRQ2_FRC2:
+       case CS35L41_IRQ2_FRC3:
+       case CS35L41_IRQ2_FRC4:
+       case CS35L41_IRQ2_EDGE1:
+       case CS35L41_IRQ2_EDGE4:
+       case CS35L41_IRQ2_POL1:
+       case CS35L41_IRQ2_POL2:
+       case CS35L41_IRQ2_POL3:
+       case CS35L41_IRQ2_POL4:
+       case CS35L41_IRQ2_DB3:
+       case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+       case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+       case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+       case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+       case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+       case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+       case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+       case CS35L41_DSP1_CCM_CORE_CTRL ... CS35L41_DSP1_WDT_STATUS:
+       case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct cs35l41_otp_packed_element_t
+                                       otp_map_1[CS35L41_NUM_OTP_ELEM] = {
+       /* addr         shift   size */
+       {0x00002030,    0,      4}, /*TRIM_OSC_FREQ_TRIM*/
+       {0x00002030,    7,      1}, /*TRIM_OSC_TRIM_DONE*/
+       {0x0000208c,    24,     6}, /*TST_DIGREG_VREF_TRIM*/
+       {0x00002090,    14,     4}, /*TST_REF_TRIM*/
+       {0x00002090,    10,     4}, /*TST_REF_TEMPCO_TRIM*/
+       {0x0000300C,    11,     4}, /*PLL_LDOA_TST_VREF_TRIM*/
+       {0x0000394C,    23,     2}, /*BST_ATEST_CM_VOFF*/
+       {0x00003950,    0,      7}, /*BST_ATRIM_IADC_OFFSET*/
+       {0x00003950,    8,      7}, /*BST_ATRIM_IADC_GAIN1*/
+       {0x00003950,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+       {0x00003950,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN1*/
+       {0x00003954,    0,      7}, /*BST_ATRIM_IADC_OFFSET2*/
+       {0x00003954,    8,      7}, /*BST_ATRIM_IADC_GAIN2*/
+       {0x00003954,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+       {0x00003954,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN2*/
+       {0x00003958,    0,      7}, /*BST_ATRIM_IADC_OFFSET3*/
+       {0x00003958,    8,      7}, /*BST_ATRIM_IADC_GAIN3*/
+       {0x00003958,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+       {0x00003958,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN3*/
+       {0x0000395C,    0,      7}, /*BST_ATRIM_IADC_OFFSET4*/
+       {0x0000395C,    8,      7}, /*BST_ATRIM_IADC_GAIN4*/
+       {0x0000395C,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+       {0x0000395C,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN4*/
+       {0x0000416C,    0,      8}, /*VMON_GAIN_OTP_VAL*/
+       {0x00004160,    0,      7}, /*VMON_OFFSET_OTP_VAL*/
+       {0x0000416C,    8,      8}, /*IMON_GAIN_OTP_VAL*/
+       {0x00004160,    16,     10}, /*IMON_OFFSET_OTP_VAL*/
+       {0x0000416C,    16,     12}, /*VMON_CM_GAIN_OTP_VAL*/
+       {0x0000416C,    28,     1}, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+       {0x00004170,    0,      6}, /*IMON_CAL_TEMPCO_OTP_VAL*/
+       {0x00004170,    6,      1}, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+       {0x00004170,    8,      6}, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+       {0x00004170,    14,     1}, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+       {0x00004170,    16,     9}, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+       {0x00004360,    0,      5}, /*TEMP_GAIN_OTP_VAL*/
+       {0x00004360,    6,      9}, /*TEMP_OFFSET_OTP_VAL*/
+       {0x00004448,    0,      8}, /*VP_SARADC_OFFSET*/
+       {0x00004448,    8,      8}, /*VP_GAIN_INDEX*/
+       {0x00004448,    16,     8}, /*VBST_SARADC_OFFSET*/
+       {0x00004448,    24,     8}, /*VBST_GAIN_INDEX*/
+       {0x0000444C,    0,      3}, /*ANA_SELINVREF*/
+       {0x00006E30,    0,      5}, /*GAIN_ERR_COEFF_0*/
+       {0x00006E30,    8,      5}, /*GAIN_ERR_COEFF_1*/
+       {0x00006E30,    16,     5}, /*GAIN_ERR_COEFF_2*/
+       {0x00006E30,    24,     5}, /*GAIN_ERR_COEFF_3*/
+       {0x00006E34,    0,      5}, /*GAIN_ERR_COEFF_4*/
+       {0x00006E34,    8,      5}, /*GAIN_ERR_COEFF_5*/
+       {0x00006E34,    16,     5}, /*GAIN_ERR_COEFF_6*/
+       {0x00006E34,    24,     5}, /*GAIN_ERR_COEFF_7*/
+       {0x00006E38,    0,      5}, /*GAIN_ERR_COEFF_8*/
+       {0x00006E38,    8,      5}, /*GAIN_ERR_COEFF_9*/
+       {0x00006E38,    16,     5}, /*GAIN_ERR_COEFF_10*/
+       {0x00006E38,    24,     5}, /*GAIN_ERR_COEFF_11*/
+       {0x00006E3C,    0,      5}, /*GAIN_ERR_COEFF_12*/
+       {0x00006E3C,    8,      5}, /*GAIN_ERR_COEFF_13*/
+       {0x00006E3C,    16,     5}, /*GAIN_ERR_COEFF_14*/
+       {0x00006E3C,    24,     5}, /*GAIN_ERR_COEFF_15*/
+       {0x00006E40,    0,      5}, /*GAIN_ERR_COEFF_16*/
+       {0x00006E40,    8,      5}, /*GAIN_ERR_COEFF_17*/
+       {0x00006E40,    16,     5}, /*GAIN_ERR_COEFF_18*/
+       {0x00006E40,    24,     5}, /*GAIN_ERR_COEFF_19*/
+       {0x00006E44,    0,      5}, /*GAIN_ERR_COEFF_20*/
+       {0x00006E48,    0,      10}, /*VOFF_GAIN_0*/
+       {0x00006E48,    10,     10}, /*VOFF_GAIN_1*/
+       {0x00006E48,    20,     10}, /*VOFF_GAIN_2*/
+       {0x00006E4C,    0,      10}, /*VOFF_GAIN_3*/
+       {0x00006E4C,    10,     10}, /*VOFF_GAIN_4*/
+       {0x00006E4C,    20,     10}, /*VOFF_GAIN_5*/
+       {0x00006E50,    0,      10}, /*VOFF_GAIN_6*/
+       {0x00006E50,    10,     10}, /*VOFF_GAIN_7*/
+       {0x00006E50,    20,     10}, /*VOFF_GAIN_8*/
+       {0x00006E54,    0,      10}, /*VOFF_GAIN_9*/
+       {0x00006E54,    10,     10}, /*VOFF_GAIN_10*/
+       {0x00006E54,    20,     10}, /*VOFF_GAIN_11*/
+       {0x00006E58,    0,      10}, /*VOFF_GAIN_12*/
+       {0x00006E58,    10,     10}, /*VOFF_GAIN_13*/
+       {0x00006E58,    20,     10}, /*VOFF_GAIN_14*/
+       {0x00006E5C,    0,      10}, /*VOFF_GAIN_15*/
+       {0x00006E5C,    10,     10}, /*VOFF_GAIN_16*/
+       {0x00006E5C,    20,     10}, /*VOFF_GAIN_17*/
+       {0x00006E60,    0,      10}, /*VOFF_GAIN_18*/
+       {0x00006E60,    10,     10}, /*VOFF_GAIN_19*/
+       {0x00006E60,    20,     10}, /*VOFF_GAIN_20*/
+       {0x00006E64,    0,      10}, /*VOFF_INT1*/
+       {0x00007418,    7,      5}, /*DS_SPK_INT1_CAP_TRIM*/
+       {0x0000741C,    0,      5}, /*DS_SPK_INT2_CAP_TRIM*/
+       {0x0000741C,    11,     4}, /*DS_SPK_LPF_CAP_TRIM*/
+       {0x0000741C,    19,     4}, /*DS_SPK_QUAN_CAP_TRIM*/
+       {0x00007434,    17,     1}, /*FORCE_CAL*/
+       {0x00007434,    18,     7}, /*CAL_OVERRIDE*/
+       {0x00007068,    0,      9}, /*MODIX*/
+       {0x0000410C,    7,      1}, /*VIMON_DLY_NOT_COMB*/
+       {0x0000400C,    0,      7}, /*VIMON_DLY*/
+       {0x00000000,    0,      1}, /*extra bit*/
+       {0x00017040,    0,      8}, /*X_COORDINATE*/
+       {0x00017040,    8,      8}, /*Y_COORDINATE*/
+       {0x00017040,    16,     8}, /*WAFER_ID*/
+       {0x00017040,    24,     8}, /*DVS*/
+       {0x00017044,    0,      24}, /*LOT_NUMBER*/
+};
+
+static const struct cs35l41_otp_packed_element_t
+                                       otp_map_2[CS35L41_NUM_OTP_ELEM] = {
+       /* addr         shift   size */
+       {0x00002030,    0,      4}, /*TRIM_OSC_FREQ_TRIM*/
+       {0x00002030,    7,      1}, /*TRIM_OSC_TRIM_DONE*/
+       {0x0000208c,    24,     6}, /*TST_DIGREG_VREF_TRIM*/
+       {0x00002090,    14,     4}, /*TST_REF_TRIM*/
+       {0x00002090,    10,     4}, /*TST_REF_TEMPCO_TRIM*/
+       {0x0000300C,    11,     4}, /*PLL_LDOA_TST_VREF_TRIM*/
+       {0x0000394C,    23,     2}, /*BST_ATEST_CM_VOFF*/
+       {0x00003950,    0,      7}, /*BST_ATRIM_IADC_OFFSET*/
+       {0x00003950,    8,      7}, /*BST_ATRIM_IADC_GAIN1*/
+       {0x00003950,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+       {0x00003950,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN1*/
+       {0x00003954,    0,      7}, /*BST_ATRIM_IADC_OFFSET2*/
+       {0x00003954,    8,      7}, /*BST_ATRIM_IADC_GAIN2*/
+       {0x00003954,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+       {0x00003954,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN2*/
+       {0x00003958,    0,      7}, /*BST_ATRIM_IADC_OFFSET3*/
+       {0x00003958,    8,      7}, /*BST_ATRIM_IADC_GAIN3*/
+       {0x00003958,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+       {0x00003958,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN3*/
+       {0x0000395C,    0,      7}, /*BST_ATRIM_IADC_OFFSET4*/
+       {0x0000395C,    8,      7}, /*BST_ATRIM_IADC_GAIN4*/
+       {0x0000395C,    16,     8}, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+       {0x0000395C,    24,     8}, /*BST_ATRIM_IPKCOMP_GAIN4*/
+       {0x0000416C,    0,      8}, /*VMON_GAIN_OTP_VAL*/
+       {0x00004160,    0,      7}, /*VMON_OFFSET_OTP_VAL*/
+       {0x0000416C,    8,      8}, /*IMON_GAIN_OTP_VAL*/
+       {0x00004160,    16,     10}, /*IMON_OFFSET_OTP_VAL*/
+       {0x0000416C,    16,     12}, /*VMON_CM_GAIN_OTP_VAL*/
+       {0x0000416C,    28,     1}, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+       {0x00004170,    0,      6}, /*IMON_CAL_TEMPCO_OTP_VAL*/
+       {0x00004170,    6,      1}, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+       {0x00004170,    8,      6}, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+       {0x00004170,    14,     1}, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+       {0x00004170,    16,     9}, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+       {0x00004360,    0,      5}, /*TEMP_GAIN_OTP_VAL*/
+       {0x00004360,    6,      9}, /*TEMP_OFFSET_OTP_VAL*/
+       {0x00004448,    0,      8}, /*VP_SARADC_OFFSET*/
+       {0x00004448,    8,      8}, /*VP_GAIN_INDEX*/
+       {0x00004448,    16,     8}, /*VBST_SARADC_OFFSET*/
+       {0x00004448,    24,     8}, /*VBST_GAIN_INDEX*/
+       {0x0000444C,    0,      3}, /*ANA_SELINVREF*/
+       {0x00006E30,    0,      5}, /*GAIN_ERR_COEFF_0*/
+       {0x00006E30,    8,      5}, /*GAIN_ERR_COEFF_1*/
+       {0x00006E30,    16,     5}, /*GAIN_ERR_COEFF_2*/
+       {0x00006E30,    24,     5}, /*GAIN_ERR_COEFF_3*/
+       {0x00006E34,    0,      5}, /*GAIN_ERR_COEFF_4*/
+       {0x00006E34,    8,      5}, /*GAIN_ERR_COEFF_5*/
+       {0x00006E34,    16,     5}, /*GAIN_ERR_COEFF_6*/
+       {0x00006E34,    24,     5}, /*GAIN_ERR_COEFF_7*/
+       {0x00006E38,    0,      5}, /*GAIN_ERR_COEFF_8*/
+       {0x00006E38,    8,      5}, /*GAIN_ERR_COEFF_9*/
+       {0x00006E38,    16,     5}, /*GAIN_ERR_COEFF_10*/
+       {0x00006E38,    24,     5}, /*GAIN_ERR_COEFF_11*/
+       {0x00006E3C,    0,      5}, /*GAIN_ERR_COEFF_12*/
+       {0x00006E3C,    8,      5}, /*GAIN_ERR_COEFF_13*/
+       {0x00006E3C,    16,     5}, /*GAIN_ERR_COEFF_14*/
+       {0x00006E3C,    24,     5}, /*GAIN_ERR_COEFF_15*/
+       {0x00006E40,    0,      5}, /*GAIN_ERR_COEFF_16*/
+       {0x00006E40,    8,      5}, /*GAIN_ERR_COEFF_17*/
+       {0x00006E40,    16,     5}, /*GAIN_ERR_COEFF_18*/
+       {0x00006E40,    24,     5}, /*GAIN_ERR_COEFF_19*/
+       {0x00006E44,    0,      5}, /*GAIN_ERR_COEFF_20*/
+       {0x00006E48,    0,      10}, /*VOFF_GAIN_0*/
+       {0x00006E48,    10,     10}, /*VOFF_GAIN_1*/
+       {0x00006E48,    20,     10}, /*VOFF_GAIN_2*/
+       {0x00006E4C,    0,      10}, /*VOFF_GAIN_3*/
+       {0x00006E4C,    10,     10}, /*VOFF_GAIN_4*/
+       {0x00006E4C,    20,     10}, /*VOFF_GAIN_5*/
+       {0x00006E50,    0,      10}, /*VOFF_GAIN_6*/
+       {0x00006E50,    10,     10}, /*VOFF_GAIN_7*/
+       {0x00006E50,    20,     10}, /*VOFF_GAIN_8*/
+       {0x00006E54,    0,      10}, /*VOFF_GAIN_9*/
+       {0x00006E54,    10,     10}, /*VOFF_GAIN_10*/
+       {0x00006E54,    20,     10}, /*VOFF_GAIN_11*/
+       {0x00006E58,    0,      10}, /*VOFF_GAIN_12*/
+       {0x00006E58,    10,     10}, /*VOFF_GAIN_13*/
+       {0x00006E58,    20,     10}, /*VOFF_GAIN_14*/
+       {0x00006E5C,    0,      10}, /*VOFF_GAIN_15*/
+       {0x00006E5C,    10,     10}, /*VOFF_GAIN_16*/
+       {0x00006E5C,    20,     10}, /*VOFF_GAIN_17*/
+       {0x00006E60,    0,      10}, /*VOFF_GAIN_18*/
+       {0x00006E60,    10,     10}, /*VOFF_GAIN_19*/
+       {0x00006E60,    20,     10}, /*VOFF_GAIN_20*/
+       {0x00006E64,    0,      10}, /*VOFF_INT1*/
+       {0x00007418,    7,      5}, /*DS_SPK_INT1_CAP_TRIM*/
+       {0x0000741C,    0,      5}, /*DS_SPK_INT2_CAP_TRIM*/
+       {0x0000741C,    11,     4}, /*DS_SPK_LPF_CAP_TRIM*/
+       {0x0000741C,    19,     4}, /*DS_SPK_QUAN_CAP_TRIM*/
+       {0x00007434,    17,     1}, /*FORCE_CAL*/
+       {0x00007434,    18,     7}, /*CAL_OVERRIDE*/
+       {0x00007068,    0,      9}, /*MODIX*/
+       {0x0000410C,    7,      1}, /*VIMON_DLY_NOT_COMB*/
+       {0x0000400C,    0,      7}, /*VIMON_DLY*/
+       {0x00004000,    11,     1}, /*VMON_POL*/
+       {0x00017040,    0,      8}, /*X_COORDINATE*/
+       {0x00017040,    8,      8}, /*Y_COORDINATE*/
+       {0x00017040,    16,     8}, /*WAFER_ID*/
+       {0x00017040,    24,     8}, /*DVS*/
+       {0x00017044,    0,      24}, /*LOT_NUMBER*/
+};
+
+const struct cs35l41_otp_map_element_t
+                               cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = {
+       {
+               .id = 0x01,
+               .map = otp_map_1,
+               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .bit_offset = 16,
+               .word_offset = 2,
+       },
+       {
+               .id = 0x02,
+               .map = otp_map_2,
+               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .bit_offset = 16,
+               .word_offset = 2,
+       },
+       {
+               .id = 0x03,
+               .map = otp_map_2,
+               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .bit_offset = 16,
+               .word_offset = 2,
+       },
+       {
+               .id = 0x06,
+               .map = otp_map_2,
+               .num_elements = CS35L41_NUM_OTP_ELEM,
+               .bit_offset = 16,
+               .word_offset = 2,
+       },
+};
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
new file mode 100755 (executable)
index 0000000..6ef0a80
--- /dev/null
@@ -0,0 +1,1702 @@
+/*
+ * cs35l41.c -- CS35l41 ALSA SoC audio driver
+ *
+ * Copyright 2018 Cirrus Logic, Inc.
+ *
+ * Author:     David Rhodes    <david.rhodes@cirrus.com>
+ *             Brian Austin    <brian.austin@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+
+#include "wm_adsp.h"
+#include "cs35l41.h"
+#include <sound/cs35l41.h>
+
+static const char * const cs35l41_supplies[] = {
+       "VA",
+       "VP",
+};
+
+struct cs35l41_pll_sysclk_config {
+       int freq;
+       int clk_cfg;
+};
+
+static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = {
+       { 32768,        0x00 },
+       { 8000,         0x01 },
+       { 11025,        0x02 },
+       { 12000,        0x03 },
+       { 16000,        0x04 },
+       { 22050,        0x05 },
+       { 24000,        0x06 },
+       { 32000,        0x07 },
+       { 44100,        0x08 },
+       { 48000,        0x09 },
+       { 88200,        0x0A },
+       { 96000,        0x0B },
+       { 128000,       0x0C },
+       { 176400,       0x0D },
+       { 192000,       0x0E },
+       { 256000,       0x0F },
+       { 352800,       0x10 },
+       { 384000,       0x11 },
+       { 512000,       0x12 },
+       { 705600,       0x13 },
+       { 750000,       0x14 },
+       { 768000,       0x15 },
+       { 1000000,      0x16 },
+       { 1024000,      0x17 },
+       { 1200000,      0x18 },
+       { 1411200,      0x19 },
+       { 1500000,      0x1A },
+       { 1536000,      0x1B },
+       { 2000000,      0x1C },
+       { 2048000,      0x1D },
+       { 2400000,      0x1E },
+       { 2822400,      0x1F },
+       { 3000000,      0x20 },
+       { 3072000,      0x21 },
+       { 3200000,      0x22 },
+       { 4000000,      0x23 },
+       { 4096000,      0x24 },
+       { 4800000,      0x25 },
+       { 5644800,      0x26 },
+       { 6000000,      0x27 },
+       { 6144000,      0x28 },
+       { 6250000,      0x29 },
+       { 6400000,      0x2A },
+       { 6500000,      0x2B },
+       { 6750000,      0x2C },
+       { 7526400,      0x2D },
+       { 8000000,      0x2E },
+       { 8192000,      0x2F },
+       { 9600000,      0x30 },
+       { 11289600,     0x31 },
+       { 12000000,     0x32 },
+       { 12288000,     0x33 },
+       { 12500000,     0x34 },
+       { 12800000,     0x35 },
+       { 13000000,     0x36 },
+       { 13500000,     0x37 },
+       { 19200000,     0x38 },
+       { 22579200,     0x39 },
+       { 24000000,     0x3A },
+       { 24576000,     0x3B },
+       { 25000000,     0x3C },
+       { 25600000,     0x3D },
+       { 26000000,     0x3E },
+       { 27000000,     0x3F },
+};
+
+static const unsigned char cs35l41_bst_k1_table[4][5] = {
+       {0x24, 0x32, 0x32, 0x4F, 0x57},
+       {0x24, 0x32, 0x32, 0x4F, 0x57},
+       {0x40, 0x32, 0x32, 0x4F, 0x57},
+       {0x40, 0x32, 0x32, 0x4F, 0x57}
+};
+
+static const unsigned char cs35l41_bst_k2_table[4][5] = {
+       {0x24, 0x49, 0x66, 0xA3, 0xEA},
+       {0x24, 0x49, 0x66, 0xA3, 0xEA},
+       {0x48, 0x49, 0x66, 0xA3, 0xEA},
+       {0x48, 0x49, 0x66, 0xA3, 0xEA}
+};
+
+static const unsigned char cs35l41_bst_slope_table[4] = {
+                                       0x75, 0x6B, 0x3B, 0x28};
+
+static int cs35l41_dsp_power_ev(struct snd_soc_dapm_widget *w,
+                      struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (cs35l41->halo_booted == false)
+                       wm_halo_early_event(w, kcontrol, event);
+               else
+                       cs35l41->dsp.booted = true;
+
+               return 0;
+       case SND_SOC_DAPM_PRE_PMD:
+               if (cs35l41->halo_booted == false) {
+                       wm_halo_early_event(w, kcontrol, event);
+                       wm_halo_event(w, kcontrol, event);
+               }
+       default:
+               return 0;
+       }
+}
+
+static int cs35l41_dsp_load_ev(struct snd_soc_dapm_widget *w,
+                      struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (cs35l41->halo_booted == false) {
+                       wm_halo_event(w, kcontrol, event);
+                       cs35l41->halo_booted = true;
+               }
+
+               regmap_write(cs35l41->regmap, CS35L41_CSPL_COMMAND,
+                               (CS35L41_CSPL_CMD_UNMUTE));
+
+               return 0;
+       case SND_SOC_DAPM_PRE_PMD:
+               regmap_write(cs35l41->regmap, CS35L41_CSPL_COMMAND,
+                               CS35L41_CSPL_CMD_MUTE);
+       default:
+               return 0;
+       }
+}
+
+static int cs35l41_halo_booted_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = cs35l41->halo_booted;
+
+       return 0;
+}
+
+static int cs35l41_halo_booted_put(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+
+       cs35l41->halo_booted = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 25, 0);
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+
+static const struct snd_kcontrol_new dre_ctrl =
+       SOC_DAPM_SINGLE("DRE Switch", CS35L41_PWR_CTRL3, 20, 1, 0);
+
+static const char * const cs35l41_pcm_sftramp_text[] =  {
+       "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"};
+
+static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp,
+                           CS35L41_AMP_DIG_VOL_CTRL, 0,
+                           cs35l41_pcm_sftramp_text);
+
+static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"};
+static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32};
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum,
+                               CS35L41_DAC_PCM1_SRC,
+                               0, CS35L41_ASP_SOURCE_MASK,
+                               cs35l41_pcm_source_texts,
+                               cs35l41_pcm_source_values);
+
+static const struct snd_kcontrol_new pcm_source_mux =
+       SOC_DAPM_ENUM("PCM Source", cs35l41_pcm_source_enum);
+
+static const char * const cs35l41_tx_input_texts[] = {"Zero", "ASPRX1",
+                                                       "ASPRX2", "VMON",
+                                                       "IMON", "VPMON",
+                                                       "DSPTX1", "DSPTX2"};
+static const unsigned int cs35l41_tx_input_values[] = {0x00,
+                                               CS35L41_INPUT_SRC_ASPRX1,
+                                               CS35L41_INPUT_SRC_ASPRX2,
+                                               CS35L41_INPUT_SRC_VMON,
+                                               CS35L41_INPUT_SRC_IMON,
+                                               CS35L41_INPUT_SRC_VPMON,
+                                               CS35L41_INPUT_DSP_TX1,
+                                               CS35L41_INPUT_DSP_TX2};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx1_enum,
+                               CS35L41_ASP_TX1_SRC,
+                               0, CS35L41_ASP_SOURCE_MASK,
+                               cs35l41_tx_input_texts,
+                               cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx1_mux =
+       SOC_DAPM_ENUM("ASPTX1 SRC", cs35l41_asptx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx2_enum,
+                               CS35L41_ASP_TX2_SRC,
+                               0, CS35L41_ASP_SOURCE_MASK,
+                               cs35l41_tx_input_texts,
+                               cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx2_mux =
+       SOC_DAPM_ENUM("ASPTX2 SRC", cs35l41_asptx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx3_enum,
+                               CS35L41_ASP_TX3_SRC,
+                               0, CS35L41_ASP_SOURCE_MASK,
+                               cs35l41_tx_input_texts,
+                               cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx3_mux =
+       SOC_DAPM_ENUM("ASPTX3 SRC", cs35l41_asptx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum,
+                               CS35L41_ASP_TX4_SRC,
+                               0, CS35L41_ASP_SOURCE_MASK,
+                               cs35l41_tx_input_texts,
+                               cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx4_mux =
+       SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum);
+
+static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
+       SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL,
+                     3, 0x4D0, 0x390, dig_vol_tlv),
+       SOC_SINGLE_TLV("AMP PCM Gain", CS35L41_AMP_GAIN_CTRL, 5, 0x14, 0,
+                       amp_gain_tlv),
+       SOC_SINGLE_RANGE("ASPTX1 Slot Position", CS35L41_SP_FRAME_TX_SLOT, 0,
+                        0, 7, 0),
+       SOC_SINGLE_RANGE("ASPTX2 Slot Position", CS35L41_SP_FRAME_TX_SLOT, 8,
+                        0, 7, 0),
+       SOC_SINGLE_RANGE("ASPTX3 Slot Position", CS35L41_SP_FRAME_TX_SLOT, 16,
+                        0, 7, 0),
+       SOC_SINGLE_RANGE("ASPTX4 Slot Position", CS35L41_SP_FRAME_TX_SLOT, 24,
+                        0, 7, 0),
+       SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp),
+       SOC_SINGLE_EXT("DSP Booted", SND_SOC_NOPM, 0, 1, 0,
+                       cs35l41_halo_booted_get, cs35l41_halo_booted_put),
+       WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+};
+
+static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) {
+               if (cs35l41_otp_map_map[i].id == otp_id)
+                       return &cs35l41_otp_map_map[i];
+       }
+
+       return NULL;
+}
+
+static int cs35l41_otp_unpack(void *data)
+{
+       struct cs35l41_private *cs35l41 = data;
+       u32 otp_mem[32];
+       int i;
+       int bit_offset, word_offset;
+       unsigned int bit_sum = 8;
+       u32 otp_val, otp_id_reg;
+       const struct cs35l41_otp_map_element_t *otp_map_match;
+       const struct cs35l41_otp_packed_element_t *otp_map;
+       int ret;
+       struct spi_device *spi = NULL;
+       u32 orig_spi_freq = 0;
+
+       ret = regmap_read(cs35l41->regmap, CS35L41_OTPID, &otp_id_reg);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Read OTP ID failed\n");
+               return -EINVAL;
+       }
+
+       otp_map_match = cs35l41_find_otp_map(otp_id_reg);
+
+       if (otp_map_match == NULL) {
+               dev_err(cs35l41->dev, "OTP Map matching ID %d not found\n",
+                               otp_id_reg);
+               return -EINVAL;
+       }
+
+       if (cs35l41->bus_spi) {
+               spi = to_spi_device(cs35l41->dev);
+               orig_spi_freq = spi->max_speed_hz;
+               spi->max_speed_hz = CS35L41_SPI_MAX_FREQ_OTP;
+               spi_setup(spi);
+       }
+
+       ret = regmap_bulk_read(cs35l41->regmap, CS35L41_OTP_MEM0, otp_mem,
+                                               CS35L41_OTP_SIZE_WORDS);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Read OTP Mem failed\n");
+               return -EINVAL;
+       }
+
+       if (cs35l41->bus_spi) {
+               spi->max_speed_hz = orig_spi_freq;
+               spi_setup(spi);
+       }
+
+       otp_map = otp_map_match->map;
+
+       bit_offset = otp_map_match->bit_offset;
+       word_offset = otp_map_match->word_offset;
+
+       ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000055);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Write Unlock key failed 1/2\n");
+               return -EINVAL;
+       }
+       ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000AA);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Write Unlock key failed 2/2\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < otp_map_match->num_elements; i++) {
+               dev_dbg(cs35l41->dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n",
+                                       bit_offset, word_offset, bit_sum % 32);
+               if (bit_offset + otp_map[i].size - 1 >= 32) {
+                       otp_val = (otp_mem[word_offset] &
+                                       GENMASK(31, bit_offset)) >>
+                                       bit_offset;
+                       otp_val |= (otp_mem[++word_offset] &
+                                       GENMASK(bit_offset +
+                                               otp_map[i].size - 33, 0)) <<
+                                       (32 - bit_offset);
+                       bit_offset += otp_map[i].size - 32;
+               } else {
+
+                       otp_val = (otp_mem[word_offset] &
+                               GENMASK(bit_offset + otp_map[i].size - 1,
+                                       bit_offset)) >> bit_offset;
+                       bit_offset += otp_map[i].size;
+               }
+               bit_sum += otp_map[i].size;
+
+               if (bit_offset == 32) {
+                       bit_offset = 0;
+                       word_offset++;
+               }
+
+               if (otp_map[i].reg != 0) {
+                       ret = regmap_update_bits(cs35l41->regmap,
+                                               otp_map[i].reg,
+                                               GENMASK(otp_map[i].shift +
+                                                       otp_map[i].size - 1,
+                                               otp_map[i].shift),
+                                               otp_val << otp_map[i].shift);
+                       if (ret < 0) {
+                               dev_err(cs35l41->dev, "Write OTP val failed\n");
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000CC);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Write Lock key failed 1/2\n");
+               return -EINVAL;
+       }
+       ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000033);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Write Lock key failed 2/2\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static irqreturn_t cs35l41_irq(int irq, void *data)
+{
+       struct cs35l41_private *cs35l41 = data;
+       unsigned int status[4];
+       unsigned int masks[4];
+
+       regmap_bulk_read(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                               status, ARRAY_SIZE(status));
+       regmap_bulk_read(cs35l41->regmap, CS35L41_IRQ1_MASK1,
+                       masks, ARRAY_SIZE(masks));
+
+       /* Check to see if unmasked bits are active */
+       if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) &&
+               !(status[2] & ~masks[2]) && !(status[3] & ~masks[3]))
+               return IRQ_NONE;
+
+       if (status[0] & CS35L41_PUP_DONE_MASK) {
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_PUP_DONE_MASK,
+                                       CS35L41_PUP_DONE_MASK);
+               complete(&cs35l41->global_pup_done);
+       }
+
+       if (status[0] & CS35L41_PDN_DONE_MASK) {
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_PDN_DONE_MASK,
+                                       CS35L41_PDN_DONE_MASK);
+               complete(&cs35l41->global_pdn_done);
+       }
+
+       /*
+        * The following interrupts require a
+        * protection release cycle to get the
+        * speaker out of Safe-Mode.
+        */
+       if (status[0] & CS35L41_AMP_SHORT_ERR) {
+               dev_crit(cs35l41->dev, "Amp short error\n");
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_AMP_SHORT_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_AMP_SHORT_ERR_RLS,
+                                       CS35L41_AMP_SHORT_ERR_RLS);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_AMP_SHORT_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_AMP_SHORT_ERR,
+                                       CS35L41_AMP_SHORT_ERR);
+       }
+
+       if (status[0] & CS35L41_TEMP_WARN) {
+               dev_crit(cs35l41->dev, "Over temperature warning\n");
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_TEMP_WARN_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_TEMP_WARN_ERR_RLS,
+                                       CS35L41_TEMP_WARN_ERR_RLS);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_TEMP_WARN_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_TEMP_WARN,
+                                       CS35L41_TEMP_WARN);
+       }
+
+       if (status[0] & CS35L41_TEMP_ERR) {
+               dev_crit(cs35l41->dev, "Over temperature error\n");
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_TEMP_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_TEMP_ERR_RLS,
+                                       CS35L41_TEMP_ERR_RLS);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_TEMP_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_TEMP_ERR,
+                                       CS35L41_TEMP_ERR);
+       }
+
+       if (status[0] & CS35L41_BST_OVP_ERR) {
+               dev_crit(cs35l41->dev, "VBST Over Voltage error\n");
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_OVP_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_OVP_ERR_RLS,
+                                       CS35L41_BST_OVP_ERR_RLS);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_OVP_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_BST_OVP_ERR,
+                                       CS35L41_BST_OVP_ERR);
+       }
+
+       if (status[0] & CS35L41_BST_DCM_UVP_ERR) {
+               dev_crit(cs35l41->dev, "DCM VBST Under Voltage Error\n");
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_UVP_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_UVP_ERR_RLS,
+                                       CS35L41_BST_UVP_ERR_RLS);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_UVP_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_BST_DCM_UVP_ERR,
+                                       CS35L41_BST_DCM_UVP_ERR);
+       }
+
+       if (status[0] & CS35L41_BST_SHORT_ERR) {
+               dev_crit(cs35l41->dev, "LBST error: powering off!\n");
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_SHORT_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_SHORT_ERR_RLS,
+                                       CS35L41_BST_SHORT_ERR_RLS);
+               regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
+                                       CS35L41_BST_SHORT_ERR_RLS, 0);
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+                                       CS35L41_BST_SHORT_ERR,
+                                       CS35L41_BST_SHORT_ERR);
+       }
+
+       if (status[3] & CS35L41_OTP_BOOT_DONE) {
+               regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4,
+                               CS35L41_OTP_BOOT_DONE, CS35L41_OTP_BOOT_DONE);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static const struct reg_sequence cs35l41_pup_patch[] = {
+       {0x00000040, 0x00000055},
+       {0x00000040, 0x000000AA},
+       {0x00002084, 0x002F1AA0},
+       {0x00000040, 0x000000CC},
+       {0x00000040, 0x00000033},
+};
+
+static const struct reg_sequence cs35l41_pdn_patch[] = {
+       {0x00000040, 0x00000055},
+       {0x00000040, 0x000000AA},
+       {0x00002084, 0x002F1AA3},
+       {0x00000040, 0x000000CC},
+       {0x00000040, 0x00000033},
+};
+
+static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+       unsigned int reg;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_multi_reg_write_bypassed(cs35l41->regmap,
+                                       cs35l41_pup_patch,
+                                       ARRAY_SIZE(cs35l41_pup_patch));
+
+               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1,
+                               CS35L41_GLOBAL_EN_MASK,
+                               1 << CS35L41_GLOBAL_EN_SHIFT);
+
+               usleep_range(1000, 1100);
+
+               regmap_read(cs35l41->regmap, CS35L41_IRQ1_RAW_STATUS3, &reg);
+               if (reg & CS35L41_PLL_UNLOCK)
+                       dev_warn(cs35l41->dev, "PLL Unlocked\n");
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1,
+                               CS35L41_GLOBAL_EN_MASK, 0);
+
+               usleep_range(1000, 1100);
+
+               regmap_multi_reg_write_bypassed(cs35l41->regmap,
+                                       cs35l41_pdn_patch,
+                                       ARRAY_SIZE(cs35l41_pdn_patch));
+               break;
+       default:
+               dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
+
+       SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+       SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100,
+                               SND_SOC_NOPM, 0, 0, cs35l41_dsp_power_ev,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+                               cs35l41_dsp_load_ev,
+                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_OUTPUT("SPK"),
+
+       SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0),
+       SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, CS35L41_SP_ENABLES, 17, 0),
+       SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L41_SP_ENABLES, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 0, CS35L41_SP_ENABLES, 1, 0),
+       SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 0, CS35L41_SP_ENABLES, 2, 0),
+       SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 0, CS35L41_SP_ENABLES, 3, 0),
+
+       SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L41_PWR_CTRL2, 12, 0),
+       SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L41_PWR_CTRL2, 13, 0),
+       SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L41_PWR_CTRL2, 8, 0),
+       SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L41_PWR_CTRL2, 9, 0),
+       SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, CS35L41_PWR_CTRL2, 10, 0),
+       SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0),
+
+       SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0,
+                               cs35l41_main_amp_event,
+                               SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+
+       SND_SOC_DAPM_INPUT("VP"),
+       SND_SOC_DAPM_INPUT("VBST"),
+       SND_SOC_DAPM_INPUT("ISENSE"),
+       SND_SOC_DAPM_INPUT("VSENSE"),
+       SND_SOC_DAPM_INPUT("TEMP"),
+
+       SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux),
+       SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux),
+       SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux),
+       SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux),
+       SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux),
+       SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl),
+};
+
+static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
+
+       { "DSP1", NULL, "ASPRX1" },
+       { "DSP1", NULL, "ASPRX2" },
+       { "DSP1", NULL, "DSP1 Preloader" },
+       { "DSP1 Preload", NULL, "DSP1 Preloader" },
+
+       {"ASP TX1 Source", "VMON", "VMON ADC"},
+       {"ASP TX1 Source", "IMON", "IMON ADC"},
+       {"ASP TX1 Source", "VPMON", "VPMON ADC"},
+       {"ASP TX1 Source", "DSPTX1", "DSP1"},
+       {"ASP TX1 Source", "DSPTX2", "DSP1"},
+       {"ASP TX1 Source", "ASPRX1", "ASPRX1" },
+       {"ASP TX1 Source", "ASPRX2", "ASPRX2" },
+       {"ASP TX1 Source", "Zero", "ASPRX1" },
+       {"ASP TX2 Source", "VMON", "VMON ADC"},
+       {"ASP TX2 Source", "IMON", "IMON ADC"},
+       {"ASP TX2 Source", "VPMON", "VPMON ADC"},
+       {"ASP TX2 Source", "DSPTX1", "DSP1"},
+       {"ASP TX2 Source", "DSPTX2", "DSP1"},
+       {"ASP TX2 Source", "ASPRX1", "ASPRX1" },
+       {"ASP TX2 Source", "ASPRX2", "ASPRX2" },
+       {"ASP TX2 Source", "Zero", "ASPRX1" },
+       {"ASP TX3 Source", "VMON", "VMON ADC"},
+       {"ASP TX3 Source", "IMON", "IMON ADC"},
+       {"ASP TX3 Source", "VPMON", "VPMON ADC"},
+       {"ASP TX3 Source", "DSPTX1", "DSP1"},
+       {"ASP TX3 Source", "DSPTX2", "DSP1"},
+       {"ASP TX3 Source", "ASPRX1", "ASPRX1" },
+       {"ASP TX3 Source", "ASPRX2", "ASPRX2" },
+       {"ASP TX3 Source", "Zero", "ASPRX1" },
+       {"ASP TX4 Source", "VMON", "VMON ADC"},
+       {"ASP TX4 Source", "IMON", "IMON ADC"},
+       {"ASP TX4 Source", "VPMON", "VPMON ADC"},
+       {"ASP TX4 Source", "DSPTX1", "DSP1"},
+       {"ASP TX4 Source", "DSPTX2", "DSP1"},
+       {"ASP TX4 Source", "ASPRX1", "ASPRX1" },
+       {"ASP TX4 Source", "ASPRX2", "ASPRX2" },
+       {"ASP TX4 Source", "Zero", "ASPRX1" },
+       {"ASPTX1", NULL, "ASP TX1 Source"},
+       {"ASPTX2", NULL, "ASP TX2 Source"},
+       {"ASPTX3", NULL, "ASP TX3 Source"},
+       {"ASPTX4", NULL, "ASP TX4 Source"},
+       {"AMP Capture", NULL, "ASPTX1"},
+       {"AMP Capture", NULL, "ASPTX2"},
+       {"AMP Capture", NULL, "ASPTX3"},
+       {"AMP Capture", NULL, "ASPTX4"},
+
+       {"VMON ADC", NULL, "ASPRX1"},
+       {"IMON ADC", NULL, "ASPRX1"},
+       {"VPMON ADC", NULL, "ASPRX1"},
+       {"TEMPMON ADC", NULL, "ASPRX1"},
+       {"VBSTMON ADC", NULL, "ASPRX1"},
+
+       {"DSP1", NULL, "IMON ADC"},
+       {"DSP1", NULL, "VMON ADC"},
+       {"DSP1", NULL, "VBSTMON ADC"},
+       {"DSP1", NULL, "VPMON ADC"},
+       {"DSP1", NULL, "TEMPMON ADC"},
+
+       {"ASPRX1", NULL, "AMP Playback"},
+       {"ASPRX2", NULL, "AMP Playback"},
+       {"DRE", "DRE Switch", "CLASS H"},
+       {"Main AMP", NULL, "CLASS H"},
+       {"Main AMP", NULL, "DRE"},
+       {"SPK", NULL, "Main AMP"},
+
+       {"PCM Source", "ASP", "ASPRX1"},
+       {"PCM Source", "DSP", "DSP1"},
+       {"CLASS H", NULL, "PCM Source"},
+
+};
+
+static const struct wm_adsp_region cs35l41_dsp1_regions[] = {
+       { .type = WMFW_HALO_PM_PACKED,  .base = CS35L41_DSP1_PMEM_0 },
+       { .type = WMFW_HALO_XM_PACKED,  .base = CS35L41_DSP1_XMEM_PACK_0 },
+       { .type = WMFW_HALO_YM_PACKED,  .base = CS35L41_DSP1_YMEM_PACK_0 },
+       {. type = WMFW_ADSP2_XM,        .base = CS35L41_DSP1_XMEM_UNPACK24_0},
+       {. type = WMFW_ADSP2_YM,        .base = CS35L41_DSP1_YMEM_UNPACK24_0},
+};
+
+static int cs35l41_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct cs35l41_private *cs35l41 =
+                       snd_soc_codec_get_drvdata(codec_dai->codec);
+       unsigned int asp_fmt, lrclk_fmt, sclk_fmt, slave_mode;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               slave_mode = 1;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               slave_mode = 0;
+               break;
+       default:
+               dev_warn(cs35l41->dev, "cs35l41_set_dai_fmt: Mixed master mode unsupported\n");
+               return -EINVAL;
+       }
+
+       regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_SCLK_MSTR_MASK,
+                               slave_mode << CS35L41_SCLK_MSTR_SHIFT);
+       regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_LRCLK_MSTR_MASK,
+                               slave_mode << CS35L41_LRCLK_MSTR_SHIFT);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               asp_fmt = 0;
+               cs35l41->i2s_mode = false;
+               cs35l41->tdm_mode = true;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               asp_fmt = 2;
+               cs35l41->i2s_mode = true;
+               cs35l41->tdm_mode = false;
+               break;
+       default:
+               dev_warn(cs35l41->dev, "cs35l41_set_dai_fmt: Invalid or unsupported DAI format\n");
+               return -EINVAL;
+       }
+
+       regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                                       CS35L41_ASP_FMT_MASK,
+                                       asp_fmt << CS35L41_ASP_FMT_SHIFT);
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_IF:
+               lrclk_fmt = 1;
+               sclk_fmt = 0;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               lrclk_fmt = 0;
+               sclk_fmt = 1;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               lrclk_fmt = 1;
+               sclk_fmt = 1;
+               break;
+       case SND_SOC_DAIFMT_NB_NF:
+               lrclk_fmt = 0;
+               sclk_fmt = 0;
+               break;
+       default:
+               dev_warn(cs35l41->dev, "cs35l41_set_dai_fmt: Invalid DAI clock INV\n");
+               return -EINVAL;
+       }
+
+       regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_LRCLK_INV_MASK,
+                               lrclk_fmt << CS35L41_LRCLK_INV_SHIFT);
+       regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_SCLK_INV_MASK,
+                               sclk_fmt << CS35L41_SCLK_INV_SHIFT);
+
+       return 0;
+}
+
+struct cs35l41_global_fs_config {
+       int rate;
+       int fs_cfg;
+};
+
+static const struct cs35l41_global_fs_config cs35l41_fs_rates[] = {
+       { 12000,        0x01 },
+       { 24000,        0x02 },
+       { 48000,        0x03 },
+       { 96000,        0x04 },
+       { 192000,       0x05 },
+       { 11025,        0x09 },
+       { 22050,        0x0A },
+       { 44100,        0x0B },
+       { 88200,        0x0C },
+       { 176400,       0x0D },
+       { 8000,         0x11 },
+       { 16000,        0x12 },
+       { 32000,        0x13 },
+};
+
+static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(dai->codec);
+       int i;
+       unsigned int rate = params_rate(params);
+       u8 asp_width, asp_wl;
+
+       for (i = 0; i < ARRAY_SIZE(cs35l41_fs_rates); i++) {
+               if (rate == cs35l41_fs_rates[i].rate)
+                       break;
+       }
+       regmap_update_bits(cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL,
+                       CS35L41_GLOBAL_FS_MASK,
+                       cs35l41_fs_rates[i].fs_cfg << CS35L41_GLOBAL_FS_SHIFT);
+
+       asp_wl = params_width(params);
+       asp_width = params_physical_width(params);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_ASP_WIDTH_RX_MASK,
+                               asp_width << CS35L41_ASP_WIDTH_RX_SHIFT);
+               regmap_update_bits(cs35l41->regmap, CS35L41_SP_RX_WL,
+                               CS35L41_ASP_RX_WL_MASK,
+                               asp_wl << CS35L41_ASP_RX_WL_SHIFT);
+               if (cs35l41->i2s_mode || cs35l41->tdm_mode) {
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_SP_FRAME_RX_SLOT,
+                                       CS35L41_ASP_RX1_SLOT_MASK,
+                                       ((cs35l41->pdata.right_channel) ? 1 : 0)
+                                        << CS35L41_ASP_RX1_SLOT_SHIFT);
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_SP_FRAME_RX_SLOT,
+                                       CS35L41_ASP_RX2_SLOT_MASK,
+                                       ((cs35l41->pdata.right_channel) ? 0 : 1)
+                                        << CS35L41_ASP_RX2_SLOT_SHIFT);
+               }
+       } else {
+               regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_ASP_WIDTH_TX_MASK,
+                               asp_width << CS35L41_ASP_WIDTH_TX_SHIFT);
+               regmap_update_bits(cs35l41->regmap, CS35L41_SP_TX_WL,
+                               CS35L41_ASP_TX_WL_MASK,
+                               asp_wl << CS35L41_ASP_TX_WL_SHIFT);
+       }
+
+       return 0;
+}
+
+static int cs35l41_get_clk_config(int freq)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs35l41_pll_sysclk); i++) {
+               if (cs35l41_pll_sysclk[i].freq == freq)
+                       return cs35l41_pll_sysclk[i].clk_cfg;
+       }
+
+       return -EINVAL;
+}
+
+static const unsigned int cs35l41_src_rates[] = {
+       8000, 12000, 11025, 16000, 22050, 24000, 32000,
+       44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l41_constraints = {
+       .count  = ARRAY_SIZE(cs35l41_src_rates),
+       .list   = cs35l41_src_rates,
+};
+
+static int cs35l41_pcm_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       if (substream->runtime)
+               return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                               SNDRV_PCM_HW_PARAM_RATE, &cs35l41_constraints);
+       return 0;
+}
+
+static int cs35l41_codec_set_sysclk(struct snd_soc_codec *codec,
+                               int clk_id, int source, unsigned int freq,
+                               int dir)
+{
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+
+       cs35l41->extclk_freq = freq;
+
+       switch (clk_id) {
+       case 0:
+               cs35l41->clksrc = CS35L41_PLLSRC_SCLK;
+               break;
+       case 1:
+               cs35l41->clksrc = CS35L41_PLLSRC_LRCLK;
+               break;
+       case 2:
+               cs35l41->clksrc = CS35L41_PLLSRC_PDMCLK;
+               break;
+       case 3:
+               cs35l41->clksrc = CS35L41_PLLSRC_SELF;
+               break;
+       case 4:
+               cs35l41->clksrc = CS35L41_PLLSRC_MCLK;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid CLK Config\n");
+               return -EINVAL;
+       }
+
+       cs35l41->extclk_cfg = cs35l41_get_clk_config(freq);
+
+       if (cs35l41->extclk_cfg < 0) {
+               dev_err(codec->dev, "Invalid CLK Config: %d, freq: %u\n",
+                       cs35l41->extclk_cfg, freq);
+               return -EINVAL;
+       }
+
+       regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+                       CS35L41_PLL_OPENLOOP_MASK,
+                       1 << CS35L41_PLL_OPENLOOP_SHIFT);
+       regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+                       CS35L41_REFCLK_FREQ_MASK,
+                       cs35l41->extclk_cfg << CS35L41_REFCLK_FREQ_SHIFT);
+       regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+                       CS35L41_PLL_CLK_EN_MASK,
+                       0 << CS35L41_PLL_CLK_EN_SHIFT);
+       regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+                       CS35L41_PLL_CLK_SEL_MASK, cs35l41->clksrc);
+       regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+                       CS35L41_PLL_OPENLOOP_MASK,
+                       0 << CS35L41_PLL_OPENLOOP_SHIFT);
+       regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+                       CS35L41_PLL_CLK_EN_MASK,
+                       1 << CS35L41_PLL_CLK_EN_SHIFT);
+
+       return 0;
+}
+
+static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai,
+                                       int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+
+       if (cs35l41_get_clk_config(freq) < 0) {
+               dev_err(codec->dev, "Invalid CLK Config freq: %u\n", freq);
+               return -EINVAL;
+       }
+
+       if (clk_id == CS35L41_PLLSRC_SCLK)
+               cs35l41->sclk = freq;
+
+       return 0;
+}
+
+static int cs35l41_boost_config(struct cs35l41_private *cs35l41,
+               int boost_ind, int boost_cap, int boost_ipk)
+{
+       int ret;
+       unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled;
+       struct regmap *regmap = cs35l41->regmap;
+       struct device *dev = cs35l41->dev;
+
+       switch (boost_ind) {
+       case 1000:      /* 1.0 uH */
+               bst_lbst_val = 0;
+               break;
+       case 1200:      /* 1.2 uH */
+               bst_lbst_val = 1;
+               break;
+       case 1500:      /* 1.5 uH */
+               bst_lbst_val = 2;
+               break;
+       case 2200:      /* 2.2 uH */
+               bst_lbst_val = 3;
+               break;
+       default:
+               dev_err(dev, "Invalid boost inductor value: %d nH\n",
+                               boost_ind);
+               return -EINVAL;
+       }
+
+       switch (boost_cap) {
+       case 0 ... 19:
+               bst_cbst_range = 0;
+               break;
+       case 20 ... 50:
+               bst_cbst_range = 1;
+               break;
+       case 51 ... 100:
+               bst_cbst_range = 2;
+               break;
+       case 101 ... 200:
+               bst_cbst_range = 3;
+               break;
+       default:        /* 201 uF and greater */
+               bst_cbst_range = 4;
+       }
+
+       ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF,
+                       CS35L41_BST_K1_MASK,
+                       cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range]
+                               << CS35L41_BST_K1_SHIFT);
+       if (ret) {
+               dev_err(dev, "Failed to write boost K1 coefficient\n");
+               return ret;
+       }
+
+       ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF,
+                       CS35L41_BST_K2_MASK,
+                       cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range]
+                               << CS35L41_BST_K2_SHIFT);
+       if (ret) {
+               dev_err(dev, "Failed to write boost K2 coefficient\n");
+               return ret;
+       }
+
+       ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST,
+                       CS35L41_BST_SLOPE_MASK,
+                       cs35l41_bst_slope_table[bst_lbst_val]
+                               << CS35L41_BST_SLOPE_SHIFT);
+       if (ret) {
+               dev_err(dev, "Failed to write boost slope coefficient\n");
+               return ret;
+       }
+
+       ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST,
+                       CS35L41_BST_LBST_VAL_MASK,
+                       bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT);
+       if (ret) {
+               dev_err(dev, "Failed to write boost inductor value\n");
+               return ret;
+       }
+
+       if ((boost_ipk < 1600) || (boost_ipk > 4500)) {
+               dev_err(dev, "Invalid boost inductor peak current: %d mA\n",
+                               boost_ipk);
+               return -EINVAL;
+       }
+       bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10;
+
+       ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR,
+                       CS35L41_BST_IPK_MASK,
+                       bst_ipk_scaled << CS35L41_BST_IPK_SHIFT);
+       if (ret) {
+               dev_err(dev, "Failed to write boost inductor peak current\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cs35l41_codec_probe(struct snd_soc_codec *codec)
+{
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+       struct classh_cfg *classh = &cs35l41->pdata.classh_config;
+       int ret;
+
+       /* Set Platform Data */
+       /* Required */
+       if (cs35l41->pdata.bst_ipk &&
+                       cs35l41->pdata.bst_ind && cs35l41->pdata.bst_cap) {
+               ret = cs35l41_boost_config(cs35l41, cs35l41->pdata.bst_ind,
+                                       cs35l41->pdata.bst_cap,
+                                       cs35l41->pdata.bst_ipk);
+               if (ret) {
+                       dev_err(cs35l41->dev, "Error in Boost DT config\n");
+                       return ret;
+               }
+       } else {
+               dev_err(cs35l41->dev, "Incomplete Boost component DT config\n");
+               return -EINVAL;
+       }
+
+       /* Optional */
+       if (cs35l41->pdata.sclk_frc)
+               regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_SCLK_FRC_MASK,
+                               cs35l41->pdata.sclk_frc <<
+                               CS35L41_SCLK_FRC_SHIFT);
+
+       if (cs35l41->pdata.lrclk_frc)
+               regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+                               CS35L41_LRCLK_FRC_MASK,
+                               cs35l41->pdata.lrclk_frc <<
+                               CS35L41_LRCLK_FRC_SHIFT);
+
+       if (cs35l41->pdata.amp_gain_zc)
+               regmap_update_bits(cs35l41->regmap, CS35L41_AMP_GAIN_CTRL,
+                               CS35L41_AMP_GAIN_ZC_MASK,
+                               cs35l41->pdata.amp_gain_zc <<
+                               CS35L41_AMP_GAIN_ZC_SHIFT);
+
+       if (cs35l41->pdata.bst_vctrl)
+               regmap_update_bits(cs35l41->regmap, CS35L41_BSTCVRT_VCTRL1,
+                               CS35L41_BST_CTL_MASK, cs35l41->pdata.bst_vctrl);
+
+       if (cs35l41->pdata.temp_warn_thld)
+               regmap_update_bits(cs35l41->regmap, CS35L41_DTEMP_WARN_THLD,
+                               CS35L41_TEMP_THLD_MASK,
+                               cs35l41->pdata.temp_warn_thld);
+
+       if (cs35l41->pdata.dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK &&
+           cs35l41->pdata.dout_hiz >= 0)
+               regmap_update_bits(cs35l41->regmap, CS35L41_SP_HIZ_CTRL,
+                               CS35L41_ASP_DOUT_HIZ_MASK,
+                               cs35l41->pdata.dout_hiz);
+
+       if (cs35l41->pdata.ng_enable) {
+               regmap_update_bits(cs35l41->regmap,
+                               CS35L41_MIXER_NGATE_CH1_CFG,
+                               CS35L41_NG_ENABLE_MASK,
+                               CS35L41_NG_ENABLE_MASK);
+               regmap_update_bits(cs35l41->regmap,
+                               CS35L41_MIXER_NGATE_CH2_CFG,
+                               CS35L41_NG_ENABLE_MASK,
+                               CS35L41_NG_ENABLE_MASK);
+
+               if (cs35l41->pdata.ng_pcm_thld) {
+                       regmap_update_bits(cs35l41->regmap,
+                               CS35L41_MIXER_NGATE_CH1_CFG,
+                               CS35L41_NG_THLD_MASK,
+                               cs35l41->pdata.ng_pcm_thld);
+                       regmap_update_bits(cs35l41->regmap,
+                               CS35L41_MIXER_NGATE_CH2_CFG,
+                               CS35L41_NG_THLD_MASK,
+                               cs35l41->pdata.ng_pcm_thld);
+               }
+
+               if (cs35l41->pdata.ng_delay) {
+                       regmap_update_bits(cs35l41->regmap,
+                               CS35L41_MIXER_NGATE_CH1_CFG,
+                               CS35L41_NG_DELAY_MASK,
+                               cs35l41->pdata.ng_delay <<
+                               CS35L41_NG_DELAY_SHIFT);
+                       regmap_update_bits(cs35l41->regmap,
+                               CS35L41_MIXER_NGATE_CH2_CFG,
+                               CS35L41_NG_DELAY_MASK,
+                               cs35l41->pdata.ng_delay <<
+                               CS35L41_NG_DELAY_SHIFT);
+               }
+       }
+
+       if (classh->classh_algo_enable) {
+               if (classh->classh_bst_override)
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_BSTCVRT_VCTRL2,
+                                       CS35L41_BST_CTL_SEL_MASK,
+                                       CS35L41_BST_CTL_SEL_REG);
+               if (classh->classh_bst_max_limit)
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_BSTCVRT_VCTRL2,
+                                       CS35L41_BST_LIM_MASK,
+                                       classh->classh_bst_max_limit <<
+                                       CS35L41_BST_LIM_SHIFT);
+               if (classh->classh_mem_depth)
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_CLASSH_CFG,
+                                       CS35L41_CH_MEM_DEPTH_MASK,
+                                       classh->classh_mem_depth <<
+                                       CS35L41_CH_MEM_DEPTH_SHIFT);
+               if (classh->classh_headroom)
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_CLASSH_CFG,
+                                       CS35L41_CH_HDRM_CTL_MASK,
+                                       classh->classh_headroom <<
+                                       CS35L41_CH_HDRM_CTL_SHIFT);
+               if (classh->classh_release_rate)
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_CLASSH_CFG,
+                                       CS35L41_CH_REL_RATE_MASK,
+                                       classh->classh_release_rate <<
+                                       CS35L41_CH_REL_RATE_SHIFT);
+               if (classh->classh_wk_fet_delay)
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_WKFET_CFG,
+                                       CS35L41_CH_WKFET_DLY_MASK,
+                                       classh->classh_wk_fet_delay <<
+                                       CS35L41_CH_WKFET_DLY_SHIFT);
+               if (classh->classh_wk_fet_thld)
+                       regmap_update_bits(cs35l41->regmap,
+                                       CS35L41_WKFET_CFG,
+                                       CS35L41_CH_WKFET_THLD_MASK,
+                                       classh->classh_wk_fet_thld <<
+                                       CS35L41_CH_WKFET_THLD_SHIFT);
+       }
+
+       wm_adsp2_codec_probe(&cs35l41->dsp, codec);
+
+       return 0;
+}
+
+static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41)
+{
+       struct irq_cfg *irq_gpio_cfg1 = &cs35l41->pdata.irq_config1;
+       struct irq_cfg *irq_gpio_cfg2 = &cs35l41->pdata.irq_config2;
+       int irq_pol = IRQF_TRIGGER_NONE;
+
+       if (irq_gpio_cfg1->is_present) {
+               if (irq_gpio_cfg1->irq_pol_inv)
+                       regmap_update_bits(cs35l41->regmap,
+                                               CS35L41_GPIO1_CTRL1,
+                                               CS35L41_GPIO_POL_MASK,
+                                               CS35L41_GPIO_POL_MASK);
+               if (irq_gpio_cfg1->irq_out_en)
+                       regmap_update_bits(cs35l41->regmap,
+                                               CS35L41_GPIO1_CTRL1,
+                                               CS35L41_GPIO_DIR_MASK,
+                                               0);
+               if (irq_gpio_cfg1->irq_src_sel)
+                       regmap_update_bits(cs35l41->regmap,
+                                               CS35L41_GPIO_PAD_CONTROL,
+                                               CS35L41_GPIO1_CTRL_MASK,
+                                               irq_gpio_cfg1->irq_src_sel <<
+                                               CS35L41_GPIO1_CTRL_SHIFT);
+       }
+
+       if (irq_gpio_cfg2->is_present) {
+               if (irq_gpio_cfg2->irq_pol_inv)
+                       regmap_update_bits(cs35l41->regmap,
+                                               CS35L41_GPIO2_CTRL1,
+                                               CS35L41_GPIO_POL_MASK,
+                                               CS35L41_GPIO_POL_MASK);
+               if (irq_gpio_cfg2->irq_out_en)
+                       regmap_update_bits(cs35l41->regmap,
+                                               CS35L41_GPIO2_CTRL1,
+                                               CS35L41_GPIO_DIR_MASK,
+                                               0);
+               if (irq_gpio_cfg2->irq_src_sel)
+                       regmap_update_bits(cs35l41->regmap,
+                                               CS35L41_GPIO_PAD_CONTROL,
+                                               CS35L41_GPIO2_CTRL_MASK,
+                                               irq_gpio_cfg2->irq_src_sel <<
+                                               CS35L41_GPIO2_CTRL_SHIFT);
+       }
+
+       if (irq_gpio_cfg2->irq_src_sel == CS35L41_GPIO_CTRL_ACTV_LO)
+               irq_pol = IRQF_TRIGGER_LOW;
+       else if (irq_gpio_cfg2->irq_src_sel == CS35L41_GPIO_CTRL_ACTV_HI)
+               irq_pol = IRQF_TRIGGER_HIGH;
+
+       return irq_pol;
+}
+
+static int cs35l41_codec_remove(struct snd_soc_codec *codec)
+{
+       struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec);
+
+       wm_adsp2_codec_remove(&cs35l41->dsp, codec);
+       return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l41_ops = {
+       .startup = cs35l41_pcm_startup,
+       .set_fmt = cs35l41_set_dai_fmt,
+       .hw_params = cs35l41_pcm_hw_params,
+       .set_sysclk = cs35l41_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs35l41_dai[] = {
+       {
+               .name = "cs35l41-pcm",
+               .id = 0,
+               .playback = {
+                       .stream_name = "AMP Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_KNOT,
+                       .formats = CS35L41_RX_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AMP Capture",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = SNDRV_PCM_RATE_KNOT,
+                       .formats = CS35L41_TX_FORMATS,
+               },
+               .ops = &cs35l41_ops,
+               .symmetric_rates = 1,
+       },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l41 = {
+       .probe = cs35l41_codec_probe,
+       .remove = cs35l41_codec_remove,
+       .component_driver = {
+               .dapm_widgets = cs35l41_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets),
+               .dapm_routes = cs35l41_audio_map,
+               .num_dapm_routes = ARRAY_SIZE(cs35l41_audio_map),
+
+               .controls = cs35l41_aud_controls,
+               .num_controls = ARRAY_SIZE(cs35l41_aud_controls),
+       },
+       .set_sysclk = cs35l41_codec_set_sysclk,
+       .ignore_pmdown_time = false,
+};
+
+static int cs35l41_handle_of_data(struct device *dev,
+                               struct cs35l41_platform_data *pdata)
+{
+       struct device_node *np = dev->of_node;
+       unsigned int val;
+       int ret;
+       struct device_node *sub_node;
+       struct classh_cfg *classh_config = &pdata->classh_config;
+       struct irq_cfg *irq_gpio1_config = &pdata->irq_config1;
+       struct irq_cfg *irq_gpio2_config = &pdata->irq_config2;
+
+       if (!np)
+               return 0;
+
+       pdata->right_channel = of_property_read_bool(np,
+                                       "cirrus,right-channel-amp");
+       pdata->sclk_frc = of_property_read_bool(np,
+                                       "cirrus,sclk-force-output");
+       pdata->lrclk_frc = of_property_read_bool(np,
+                                       "cirrus,lrclk-force-output");
+       pdata->amp_gain_zc = of_property_read_bool(np,
+                                       "cirrus,amp-gain-zc");
+
+       if (of_property_read_u32(np, "cirrus,temp-warn_threshold", &val) >= 0)
+               pdata->temp_warn_thld = val | CS35L41_VALID_PDATA;
+
+       ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val);
+       if (ret >= 0) {
+               if (val < 2550 || val > 11000) {
+                       dev_err(dev,
+                               "Invalid Boost Voltage %u mV\n", val);
+                       return -EINVAL;
+               }
+               pdata->bst_vctrl = ((val - 2550) / 100) + 1;
+       }
+
+       ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val);
+       if (ret >= 0)
+               pdata->bst_ipk = val;
+
+       ret = of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val);
+       if (ret >= 0)
+               pdata->bst_ind = val;
+
+       ret = of_property_read_u32(np, "cirrus,boost-cap-microfarad", &val);
+       if (ret >= 0)
+               pdata->bst_cap = val;
+
+       ret = of_property_read_u32(np, "cirrus,asp-sdout-hiz", &val);
+       if (ret >= 0)
+               pdata->dout_hiz = val;
+       else
+               pdata->dout_hiz = -1;
+
+       pdata->ng_enable = of_property_read_bool(np,
+                                       "cirrus,noise-gate-enable");
+       if (of_property_read_u32(np, "cirrus,noise-gate-threshold", &val) >= 0)
+               pdata->ng_pcm_thld = val | CS35L41_VALID_PDATA;
+       if (of_property_read_u32(np, "cirrus,noise-gate-delay", &val) >= 0)
+               pdata->ng_delay = val | CS35L41_VALID_PDATA;
+
+       sub_node = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+       classh_config->classh_algo_enable = sub_node ? true : false;
+
+       if (classh_config->classh_algo_enable) {
+               classh_config->classh_bst_override =
+                       of_property_read_bool(sub_node,
+                               "cirrus,classh-bst-overide");
+
+               ret = of_property_read_u32(sub_node,
+                                          "cirrus,classh-bst-max-limit",
+                                          &val);
+               if (ret >= 0) {
+                       val |= CS35L41_VALID_PDATA;
+                       classh_config->classh_bst_max_limit = val;
+               }
+
+               ret = of_property_read_u32(sub_node, "cirrus,classh-mem-depth",
+                                          &val);
+               if (ret >= 0) {
+                       val |= CS35L41_VALID_PDATA;
+                       classh_config->classh_mem_depth = val;
+               }
+
+               ret = of_property_read_u32(sub_node,
+                                       "cirrus,classh-release-rate", &val);
+               if (ret >= 0)
+                       classh_config->classh_release_rate = val;
+
+               ret = of_property_read_u32(sub_node, "cirrus,classh-headroom",
+                                          &val);
+               if (ret >= 0) {
+                       val |= CS35L41_VALID_PDATA;
+                       classh_config->classh_headroom = val;
+               }
+
+               ret = of_property_read_u32(sub_node,
+                                       "cirrus,classh-wk-fet-delay", &val);
+               if (ret >= 0) {
+                       val |= CS35L41_VALID_PDATA;
+                       classh_config->classh_wk_fet_delay = val;
+               }
+
+               ret = of_property_read_u32(sub_node,
+                                       "cirrus,classh-wk-fet-thld", &val);
+               if (ret >= 0)
+                       classh_config->classh_wk_fet_thld = val;
+       }
+       of_node_put(sub_node);
+
+       /* GPIO1 Pin Config */
+       sub_node = of_get_child_by_name(np, "cirrus,gpio-config1");
+       irq_gpio1_config->is_present = sub_node ? true : false;
+       if (irq_gpio1_config->is_present) {
+               irq_gpio1_config->irq_pol_inv = of_property_read_bool(sub_node,
+                                               "cirrus,gpio-polarity-invert");
+               irq_gpio1_config->irq_out_en = of_property_read_bool(sub_node,
+                                               "cirrus,gpio-output-enable");
+               ret = of_property_read_u32(sub_node, "cirrus,gpio-src-select",
+                                       &val);
+               if (ret >= 0) {
+                       val |= CS35L41_VALID_PDATA;
+                       irq_gpio1_config->irq_src_sel = val;
+               }
+       }
+       of_node_put(sub_node);
+
+       /* GPIO2 Pin Config */
+       sub_node = of_get_child_by_name(np, "cirrus,gpio-config2");
+       irq_gpio2_config->is_present = sub_node ? true : false;
+       if (irq_gpio2_config->is_present) {
+               irq_gpio2_config->irq_pol_inv = of_property_read_bool(sub_node,
+                                               "cirrus,gpio-polarity-invert");
+               irq_gpio2_config->irq_out_en = of_property_read_bool(sub_node,
+                                               "cirrus,gpio-output-enable");
+               ret = of_property_read_u32(sub_node, "cirrus,gpio-src-select",
+                                       &val);
+               if (ret >= 0) {
+                       val |= CS35L41_VALID_PDATA;
+                       irq_gpio2_config->irq_src_sel = val;
+               }
+       }
+       of_node_put(sub_node);
+
+       return 0;
+}
+
+static const struct reg_sequence cs35l41_reva0_errata_patch[] = {
+       {0x00000040,                    0x00005555},
+       {0x00000040,                    0x0000AAAA},
+       {0x00003854,                    0x05180240},
+       {CS35L41_OTP_TRIM_30,           0x9091A1C8},
+       {0x00003014,                    0x0200EE0E},
+       {CS35L41_BSTCVRT_DCM_CTRL,      0x00000051},
+       {0x00000054,                    0x00000004},
+       {CS35L41_IRQ1_DB3,              0x00000000},
+       {CS35L41_IRQ2_DB3,              0x00000000},
+       {0x00000040,                    0x0000CCCC},
+       {0x00000040,                    0x00003333},
+};
+
+static int cs35l41_dsp_init(struct cs35l41_private *cs35l41)
+{
+       struct wm_adsp *dsp;
+       int ret;
+
+       dsp = &cs35l41->dsp;
+       dsp->part = "cs35l41";
+       dsp->num = 1;
+       dsp->type = WMFW_HALO;
+       dsp->rev = 0;
+       dsp->dev = cs35l41->dev;
+       dsp->regmap = cs35l41->regmap;
+       dsp->suffix = "";
+
+       dsp->base = CS35L41_DSP1_CTRL_BASE;
+       dsp->base_sysinfo = CS35L41_DSP1_SYS_ID;
+       dsp->mem = cs35l41_dsp1_regions;
+       dsp->num_mems = ARRAY_SIZE(cs35l41_dsp1_regions);
+       dsp->unlock_all = true;
+
+       dsp->n_rx_channels = CS35L41_DSP_N_RX_RATES;
+       dsp->n_tx_channels = CS35L41_DSP_N_TX_RATES;
+       ret = wm_halo_init(dsp, &cs35l41->rate_lock);
+       cs35l41->halo_booted = false;
+
+       regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC,
+                                       CS35L41_INPUT_SRC_VPMON);
+       regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC,
+                                       CS35L41_INPUT_SRC_CLASSH);
+       regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC,
+                                       CS35L41_INPUT_SRC_TEMPMON);
+       regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC,
+                                       CS35L41_INPUT_SRC_RSVD);
+
+       return ret;
+}
+
+int cs35l41_probe(struct cs35l41_private *cs35l41,
+                               struct cs35l41_platform_data *pdata)
+{
+       int ret;
+       u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match;
+       int timeout = 100;
+       int irq_pol = 0;
+       printk(" ============================================================================================================================================================================\n");
+       for (i = 0; i < ARRAY_SIZE(cs35l41_supplies); i++)
+               cs35l41->supplies[i].supply = cs35l41_supplies[i];
+
+       cs35l41->num_supplies = ARRAY_SIZE(cs35l41_supplies);
+
+       ret = devm_regulator_bulk_get(cs35l41->dev, cs35l41->num_supplies,
+                                       cs35l41->supplies);
+       if (ret != 0) {
+               dev_err(cs35l41->dev,
+                       "Failed to request core supplies: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (pdata) {
+               cs35l41->pdata = *pdata;
+       } else if (cs35l41->dev->of_node) {
+               ret = cs35l41_handle_of_data(cs35l41->dev, &cs35l41->pdata);
+               if (ret != 0)
+                       return ret;
+       } else {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(cs35l41->num_supplies, cs35l41->supplies);
+       if (ret != 0) {
+               dev_err(cs35l41->dev,
+                       "Failed to enable core supplies: %d\n", ret);
+               return ret;
+       }
+
+       /* returning NULL can be an option if in stereo mode */
+       cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset",
+                                                       GPIOD_OUT_LOW);
+       if (IS_ERR(cs35l41->reset_gpio)) {
+               ret = PTR_ERR(cs35l41->reset_gpio);
+               cs35l41->reset_gpio = NULL;
+               if (ret == -EBUSY) {
+                       dev_info(cs35l41->dev,
+                                "Reset line busy, assuming shared reset\n");
+               } else {
+                       dev_err(cs35l41->dev,
+                               "Failed to get reset GPIO: %d\n", ret);
+                       goto err;
+               }
+       }
+       if (cs35l41->reset_gpio) {
+               /* satisfy minimum reset pulse width spec */
+               usleep_range(2000, 2100);
+               gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+       }
+
+       usleep_range(2000, 2100);
+
+       ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Get Device ID failed\n");
+               goto err;
+       }
+
+       ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "Get Revision ID failed\n");
+               goto err;
+       }
+
+       mtl_revid = reg_revid & CS35L41_MTLREVID_MASK;
+
+       /* CS35L41 will have even MTLREVID
+       *  CS35L41R will have odd MTLREVID
+       */
+       chipid_match = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
+       if (regid != chipid_match) {
+               dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n",
+                       regid, chipid_match);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       irq_pol = cs35l41_irq_gpio_config(cs35l41);
+
+       init_completion(&cs35l41->global_pdn_done);
+       init_completion(&cs35l41->global_pup_done);
+
+       ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL,
+                               cs35l41_irq, IRQF_ONESHOT | irq_pol,
+                               "cs35l41", cs35l41);
+
+       /* CS35L41 needs INT for PDN_DONE */
+       if (ret != 0) {
+               dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret);
+               goto err;
+       }
+
+       /* Set interrupt masks for critical errors */
+       regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
+                       CS35L41_INT1_MASK_DEFAULT);
+
+       switch (reg_revid) {
+       case CS35L41_REVID_A0:
+               ret = regmap_register_patch(cs35l41->regmap,
+                               cs35l41_reva0_errata_patch,
+                               ARRAY_SIZE(cs35l41_reva0_errata_patch));
+               if (ret < 0) {
+                       dev_err(cs35l41->dev,
+                               "Failed to apply A0 errata patch %d\n", ret);
+                       goto err;
+               }
+       }
+
+       do {
+               if (timeout == 0) {
+                       dev_err(cs35l41->dev,
+                               "Timeout waiting for OTP_BOOT_DONE\n");
+                       ret = -EBUSY;
+                       goto err;
+               }
+               usleep_range(1000, 1100);
+               regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS4, &int_status);
+               timeout--;
+       } while (!(int_status & CS35L41_OTP_BOOT_DONE));
+
+       regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status);
+       if (int_status & CS35L41_OTP_BOOT_ERR) {
+               dev_err(cs35l41->dev, "OTP Boot error\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = cs35l41_otp_unpack(cs35l41);
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "OTP Unpack failed\n");
+               goto err;
+       }
+
+       mutex_init(&cs35l41->rate_lock);
+
+       cs35l41_dsp_init(cs35l41);
+
+       ret =  snd_soc_register_codec(cs35l41->dev, &soc_codec_dev_cs35l41,
+                                       cs35l41_dai, ARRAY_SIZE(cs35l41_dai));
+       if (ret < 0) {
+               dev_err(cs35l41->dev, "%s: Register codec failed\n", __func__);
+               goto err;
+       }
+
+       dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n",
+                       regid, reg_revid);
+
+err:
+       regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies);
+       return ret;
+}
+
+MODULE_DESCRIPTION("ASoC CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
new file mode 100755 (executable)
index 0000000..a51cf7d
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+ * cs35l41.h -- CS35L41 ALSA SoC audio driver
+ *
+ * Copyright 2018 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *         David Rhodes <david.rhodes@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L41_H__
+#define __CS35L41_H__
+
+#include <linux/regmap.h>
+
+#define CS35L41_FIRSTREG               0x00000000
+#define CS35L41_LASTREG                        0x03804FE8
+#define CS35L41_DEVID                  0x00000000
+#define CS35L41_REVID                  0x00000004
+#define CS35L41_FABID                  0x00000008
+#define CS35L41_RELID                  0x0000000C
+#define CS35L41_OTPID                  0x00000010
+#define CS35L41_SFT_RESET              0x00000020
+#define CS35L41_TEST_KEY_CTL           0x00000040
+#define CS35L41_USER_KEY_CTL           0x00000044
+#define CS35L41_OTP_MEM0               0x00000400
+#define CS35L41_OTP_MEM31              0x0000047C
+#define CS35L41_OTP_CTRL0              0x00000500
+#define CS35L41_OTP_CTRL1              0x00000504
+#define CS35L41_OTP_CTRL3              0x00000508
+#define CS35L41_OTP_CTRL4              0x0000050C
+#define CS35L41_OTP_CTRL5              0x00000510
+#define CS35L41_OTP_CTRL6              0x00000514
+#define CS35L41_OTP_CTRL7              0x00000518
+#define CS35L41_OTP_CTRL8              0x0000051C
+#define CS35L41_PWR_CTRL1              0x00002014
+#define CS35L41_PWR_CTRL2              0x00002018
+#define CS35L41_PWR_CTRL3              0x0000201C
+#define CS35L41_CTRL_OVRRIDE           0x00002020
+#define CS35L41_AMP_OUT_MUTE           0x00002024
+#define CS35L41_PROTECT_REL_ERR_IGN    0x00002034
+#define CS35L41_GPIO_PAD_CONTROL       0x0000242C
+#define CS35L41_JTAG_CONTROL           0x00002438
+#define CS35L41_PLL_CLK_CTRL           0x00002C04
+#define CS35L41_DSP_CLK_CTRL           0x00002C08
+#define CS35L41_GLOBAL_CLK_CTRL                0x00002C0C
+#define CS35L41_DATA_FS_SEL            0x00002C10
+#define CS35L41_MDSYNC_EN              0x00003400
+#define CS35L41_MDSYNC_TX_ID           0x00003408
+#define CS35L41_MDSYNC_PWR_CTRL                0x0000340C
+#define CS35L41_MDSYNC_DATA_TX         0x00003410
+#define CS35L41_MDSYNC_TX_STATUS       0x00003414
+#define CS35L41_MDSYNC_DATA_RX         0x0000341C
+#define CS35L41_MDSYNC_RX_STATUS       0x00003420
+#define CS35L41_MDSYNC_ERR_STATUS      0x00003424
+#define CS35L41_MDSYNC_SYNC_PTE2       0x00003528
+#define CS35L41_MDSYNC_SYNC_PTE3       0x0000352C
+#define CS35L41_MDSYNC_SYNC_MSM_STATUS 0x0000353C
+#define CS35L41_BSTCVRT_VCTRL1         0x00003800
+#define CS35L41_BSTCVRT_VCTRL2         0x00003804
+#define CS35L41_BSTCVRT_PEAK_CUR       0x00003808
+#define CS35L41_BSTCVRT_SFT_RAMP       0x0000380C
+#define CS35L41_BSTCVRT_COEFF          0x00003810
+#define CS35L41_BSTCVRT_SLOPE_LBST     0x00003814
+#define CS35L41_BSTCVRT_SW_FREQ                0x00003818
+#define CS35L41_BSTCVRT_DCM_CTRL       0x0000381C
+#define CS35L41_BSTCVRT_DCM_MODE_FORCE 0x00003820
+#define CS35L41_BSTCVRT_OVERVOLT_CTRL  0x00003830
+#define CS35L41_VI_VOL_POL             0x00004000
+#define CS35L41_DTEMP_WARN_THLD                0x00004220
+#define CS35L41_DTEMP_CFG              0x00004224
+#define CS35L41_DTEMP_EN               0x00004308
+#define CS35L41_VPVBST_FS_SEL          0x00004400
+#define CS35L41_SP_ENABLES             0x00004800
+#define CS35L41_SP_RATE_CTRL           0x00004804
+#define CS35L41_SP_FORMAT              0x00004808
+#define CS35L41_SP_HIZ_CTRL            0x0000480C
+#define CS35L41_SP_FRAME_TX_SLOT       0x00004810
+#define CS35L41_SP_FRAME_RX_SLOT       0x00004820
+#define CS35L41_SP_TX_WL               0x00004830
+#define CS35L41_SP_RX_WL               0x00004840
+#define CS35L41_DAC_PCM1_SRC           0x00004C00
+#define CS35L41_ASP_TX1_SRC            0x00004C20
+#define CS35L41_ASP_TX2_SRC            0x00004C24
+#define CS35L41_ASP_TX3_SRC            0x00004C28
+#define CS35L41_ASP_TX4_SRC            0x00004C2C
+#define CS35L41_DSP1_RX1_SRC           0x00004C40
+#define CS35L41_DSP1_RX2_SRC           0x00004C44
+#define CS35L41_DSP1_RX3_SRC           0x00004C48
+#define CS35L41_DSP1_RX4_SRC           0x00004C4C
+#define CS35L41_DSP1_RX5_SRC           0x00004C50
+#define CS35L41_DSP1_RX6_SRC           0x00004C54
+#define CS35L41_DSP1_RX7_SRC           0x00004C58
+#define CS35L41_DSP1_RX8_SRC           0x00004C5C
+#define CS35L41_NGATE1_SRC             0x00004C60
+#define CS35L41_NGATE2_SRC             0x00004C64
+#define CS35L41_AMP_DIG_VOL_CTRL       0x00006000
+#define CS35L41_VPBR_CFG               0x00006404
+#define CS35L41_VBBR_CFG               0x00006408
+#define CS35L41_VPBR_STATUS            0x0000640C
+#define CS35L41_VBBR_STATUS            0x00006410
+#define CS35L41_OVERTEMP_CFG           0x00006414
+#define CS35L41_AMP_ERR_VOL            0x00006418
+#define CS35L41_VOL_STATUS_TO_DSP      0x00006450
+#define CS35L41_CLASSH_CFG             0x00006800
+#define CS35L41_WKFET_CFG              0x00006804
+#define CS35L41_NG_CFG                 0x00006808
+#define CS35L41_AMP_GAIN_CTRL          0x00006C04
+#define CS35L41_DAC_MSM_CFG            0x00007400
+#define CS35L41_IRQ1_CFG               0x00010000
+#define CS35L41_IRQ1_STATUS            0x00010004
+#define CS35L41_IRQ1_STATUS1           0x00010010
+#define CS35L41_IRQ1_STATUS2           0x00010014
+#define CS35L41_IRQ1_STATUS3           0x00010018
+#define CS35L41_IRQ1_STATUS4           0x0001001C
+#define CS35L41_IRQ1_RAW_STATUS1       0x00010090
+#define CS35L41_IRQ1_RAW_STATUS2       0x00010094
+#define CS35L41_IRQ1_RAW_STATUS3       0x00010098
+#define CS35L41_IRQ1_RAW_STATUS4       0x0001009C
+#define CS35L41_IRQ1_MASK1             0x00010110
+#define CS35L41_IRQ1_MASK2             0x00010114
+#define CS35L41_IRQ1_MASK3             0x00010118
+#define CS35L41_IRQ1_MASK4             0x0001011C
+#define CS35L41_IRQ1_FRC1              0x00010190
+#define CS35L41_IRQ1_FRC2              0x00010194
+#define CS35L41_IRQ1_FRC3              0x00010198
+#define CS35L41_IRQ1_FRC4              0x0001019C
+#define CS35L41_IRQ1_EDGE1             0x00010210
+#define CS35L41_IRQ1_EDGE4             0x0001021C
+#define CS35L41_IRQ1_POL1              0x00010290
+#define CS35L41_IRQ1_POL2              0x00010294
+#define CS35L41_IRQ1_POL3              0x00010298
+#define CS35L41_IRQ1_POL4              0x0001029C
+#define CS35L41_IRQ1_DB3               0x00010318
+#define CS35L41_IRQ2_CFG               0x00010800
+#define CS35L41_IRQ2_STATUS            0x00010804
+#define CS35L41_IRQ2_STATUS1           0x00010810
+#define CS35L41_IRQ2_STATUS2           0x00010814
+#define CS35L41_IRQ2_STATUS3           0x00010818
+#define CS35L41_IRQ2_STATUS4           0x0001081C
+#define CS35L41_IRQ2_RAW_STATUS1       0x00010890
+#define CS35L41_IRQ2_RAW_STATUS2       0x00010894
+#define CS35L41_IRQ2_RAW_STATUS3       0x00010898
+#define CS35L41_IRQ2_RAW_STATUS4       0x0001089C
+#define CS35L41_IRQ2_MASK1             0x00010910
+#define CS35L41_IRQ2_MASK2             0x00010914
+#define CS35L41_IRQ2_MASK3             0x00010918
+#define CS35L41_IRQ2_MASK4             0x0001091C
+#define CS35L41_IRQ2_FRC1              0x00010990
+#define CS35L41_IRQ2_FRC2              0x00010994
+#define CS35L41_IRQ2_FRC3              0x00010998
+#define CS35L41_IRQ2_FRC4              0x0001099C
+#define CS35L41_IRQ2_EDGE1             0x00010A10
+#define CS35L41_IRQ2_EDGE4             0x00010A1C
+#define CS35L41_IRQ2_POL1              0x00010A90
+#define CS35L41_IRQ2_POL2              0x00010A94
+#define CS35L41_IRQ2_POL3              0x00010A98
+#define CS35L41_IRQ2_POL4              0x00010A9C
+#define CS35L41_IRQ2_DB3               0x00010B18
+#define CS35L41_GPIO_STATUS1           0x00011000
+#define CS35L41_GPIO1_CTRL1            0x00011008
+#define CS35L41_GPIO2_CTRL1            0x0001100C
+#define CS35L41_MIXER_NGATE_CFG                0x00012000
+#define CS35L41_MIXER_NGATE_CH1_CFG    0x00012004
+#define CS35L41_MIXER_NGATE_CH2_CFG    0x00012008
+#define CS35L41_DSP_MBOX_1             0x00013000
+#define CS35L41_DSP_MBOX_2             0x00013004
+#define CS35L41_DSP_MBOX_3             0x00013008
+#define CS35L41_DSP_MBOX_4             0x0001300C
+#define CS35L41_DSP_MBOX_5             0x00013010
+#define CS35L41_DSP_MBOX_6             0x00013014
+#define CS35L41_DSP_MBOX_7             0x00013018
+#define CS35L41_DSP_MBOX_8             0x0001301C
+#define CS35L41_DSP_VIRT1_MBOX_1       0x00013020
+#define CS35L41_DSP_VIRT1_MBOX_2       0x00013024
+#define CS35L41_DSP_VIRT1_MBOX_3       0x00013028
+#define CS35L41_DSP_VIRT1_MBOX_4       0x0001302C
+#define CS35L41_DSP_VIRT1_MBOX_5       0x00013030
+#define CS35L41_DSP_VIRT1_MBOX_6       0x00013034
+#define CS35L41_DSP_VIRT1_MBOX_7       0x00013038
+#define CS35L41_DSP_VIRT1_MBOX_8       0x0001303C
+#define CS35L41_DSP_VIRT2_MBOX_1       0x00013040
+#define CS35L41_DSP_VIRT2_MBOX_2       0x00013044
+#define CS35L41_DSP_VIRT2_MBOX_3       0x00013048
+#define CS35L41_DSP_VIRT2_MBOX_4       0x0001304C
+#define CS35L41_DSP_VIRT2_MBOX_5       0x00013050
+#define CS35L41_DSP_VIRT2_MBOX_6       0x00013054
+#define CS35L41_DSP_VIRT2_MBOX_7       0x00013058
+#define CS35L41_DSP_VIRT2_MBOX_8       0x0001305C
+#define CS35L41_CLOCK_DETECT_1         0x00014000
+#define CS35L41_TIMER1_CONTROL         0x00015000
+#define CS35L41_TIMER1_COUNT_PRESET    0x00015004
+#define CS35L41_TIMER1_START_STOP      0x0001500C
+#define CS35L41_TIMER1_STATUS          0x00015010
+#define CS35L41_TIMER1_COUNT_READBACK  0x00015014
+#define CS35L41_TIMER1_DSP_CLK_CFG     0x00015018
+#define CS35L41_TIMER1_DSP_CLK_STATUS  0x0001501C
+#define CS35L41_TIMER2_CONTROL         0x00015100
+#define CS35L41_TIMER2_COUNT_PRESET    0x00015104
+#define CS35L41_TIMER2_START_STOP      0x0001510C
+#define CS35L41_TIMER2_STATUS          0x00015110
+#define CS35L41_TIMER2_COUNT_READBACK  0x00015114
+#define CS35L41_TIMER2_DSP_CLK_CFG     0x00015118
+#define CS35L41_TIMER2_DSP_CLK_STATUS  0x0001511C
+#define CS35L41_DFT_JTAG_CONTROL       0x00016000
+#define CS35L41_DIE_STS1               0x00017040
+#define CS35L41_DIE_STS2               0x00017044
+#define CS35L41_TEMP_CAL1              0x00017048
+#define CS35L41_TEMP_CAL2              0x0001704C
+#define CS35L41_DSP1_XMEM_PACK_0       0x02000000
+#define CS35L41_DSP1_XMEM_PACK_3068    0x02002FF0
+#define CS35L41_DSP1_XMEM_UNPACK32_0   0x02400000
+#define CS35L41_DSP1_XMEM_UNPACK32_2046        0x02401FF8
+#define CS35L41_DSP1_TIMESTAMP_COUNT   0x025C0800
+#define CS35L41_DSP1_SYS_ID            0x025E0000
+#define CS35L41_DSP1_SYS_VERSION       0x025E0004
+#define CS35L41_DSP1_SYS_CORE_ID       0x025E0008
+#define CS35L41_DSP1_SYS_AHB_ADDR      0x025E000C
+#define CS35L41_DSP1_SYS_XSRAM_SIZE    0x025E0010
+#define CS35L41_DSP1_SYS_YSRAM_SIZE    0x025E0018
+#define CS35L41_DSP1_SYS_PSRAM_SIZE    0x025E0020
+#define CS35L41_DSP1_SYS_PM_BOOT_SIZE  0x025E0028
+#define CS35L41_DSP1_SYS_FEATURES      0x025E002C
+#define CS35L41_DSP1_SYS_FIR_FILTERS   0x025E0030
+#define CS35L41_DSP1_SYS_LMS_FILTERS   0x025E0034
+#define CS35L41_DSP1_SYS_XM_BANK_SIZE  0x025E0038
+#define CS35L41_DSP1_SYS_YM_BANK_SIZE  0x025E003C
+#define CS35L41_DSP1_SYS_PM_BANK_SIZE  0x025E0040
+#define CS35L41_DSP1_AHBM_WIN0_CTRL0   0x025E2000
+#define CS35L41_DSP1_AHBM_WIN0_CTRL1   0x025E2004
+#define CS35L41_DSP1_AHBM_WIN1_CTRL0   0x025E2008
+#define CS35L41_DSP1_AHBM_WIN1_CTRL1   0x025E200C
+#define CS35L41_DSP1_AHBM_WIN2_CTRL0   0x025E2010
+#define CS35L41_DSP1_AHBM_WIN2_CTRL1   0x025E2014
+#define CS35L41_DSP1_AHBM_WIN3_CTRL0   0x025E2018
+#define CS35L41_DSP1_AHBM_WIN3_CTRL1   0x025E201C
+#define CS35L41_DSP1_AHBM_WIN4_CTRL0   0x025E2020
+#define CS35L41_DSP1_AHBM_WIN4_CTRL1   0x025E2024
+#define CS35L41_DSP1_AHBM_WIN5_CTRL0   0x025E2028
+#define CS35L41_DSP1_AHBM_WIN5_CTRL1   0x025E202C
+#define CS35L41_DSP1_AHBM_WIN6_CTRL0   0x025E2030
+#define CS35L41_DSP1_AHBM_WIN6_CTRL1   0x025E2034
+#define CS35L41_DSP1_AHBM_WIN7_CTRL0   0x025E2038
+#define CS35L41_DSP1_AHBM_WIN7_CTRL1   0x025E203C
+#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL0        0x025E2040
+#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL1        0x025E2044
+#define CS35L41_DSP1_XMEM_UNPACK24_0   0x02800000
+#define CS35L41_DSP1_XMEM_UNPACK24_4093        0x02803FF4
+#define CS35L41_DSP1_CTRL_BASE         0x02B80000
+#define CS35L41_DSP1_CORE_SOFT_RESET   0x02B80010
+#define CS35L41_DSP1_DEBUG             0x02B80040
+#define CS35L41_DSP1_TIMER_CTRL                0x02B80048
+#define CS35L41_DSP1_STREAM_ARB_CTRL   0x02B80050
+#define CS35L41_DSP1_RX1_RATE          0x02B80080
+#define CS35L41_DSP1_RX2_RATE          0x02B80088
+#define CS35L41_DSP1_RX3_RATE          0x02B80090
+#define CS35L41_DSP1_RX4_RATE          0x02B80098
+#define CS35L41_DSP1_RX5_RATE          0x02B800A0
+#define CS35L41_DSP1_RX6_RATE          0x02B800A8
+#define CS35L41_DSP1_RX7_RATE          0x02B800B0
+#define CS35L41_DSP1_RX8_RATE          0x02B800B8
+#define CS35L41_DSP1_TX1_RATE          0x02B80280
+#define CS35L41_DSP1_TX2_RATE          0x02B80288
+#define CS35L41_DSP1_TX3_RATE          0x02B80290
+#define CS35L41_DSP1_TX4_RATE          0x02B80298
+#define CS35L41_DSP1_TX5_RATE          0x02B802A0
+#define CS35L41_DSP1_TX6_RATE          0x02B802A8
+#define CS35L41_DSP1_TX7_RATE          0x02B802B0
+#define CS35L41_DSP1_TX8_RATE          0x02B802B8
+#define CS35L41_DSP1_NMI_CTRL1         0x02B80480
+#define CS35L41_DSP1_NMI_CTRL2         0x02B80488
+#define CS35L41_DSP1_NMI_CTRL3         0x02B80490
+#define CS35L41_DSP1_NMI_CTRL4         0x02B80498
+#define CS35L41_DSP1_NMI_CTRL5         0x02B804A0
+#define CS35L41_DSP1_NMI_CTRL6         0x02B804A8
+#define CS35L41_DSP1_NMI_CTRL7         0x02B804B0
+#define CS35L41_DSP1_NMI_CTRL8         0x02B804B8
+#define CS35L41_DSP1_RESUME_CTRL       0x02B80500
+#define CS35L41_DSP1_IRQ1_CTRL         0x02B80508
+#define CS35L41_DSP1_IRQ2_CTRL         0x02B80510
+#define CS35L41_DSP1_IRQ3_CTRL         0x02B80518
+#define CS35L41_DSP1_IRQ4_CTRL         0x02B80520
+#define CS35L41_DSP1_IRQ5_CTRL         0x02B80528
+#define CS35L41_DSP1_IRQ6_CTRL         0x02B80530
+#define CS35L41_DSP1_IRQ7_CTRL         0x02B80538
+#define CS35L41_DSP1_IRQ8_CTRL         0x02B80540
+#define CS35L41_DSP1_IRQ9_CTRL         0x02B80548
+#define CS35L41_DSP1_IRQ10_CTRL                0x02B80550
+#define CS35L41_DSP1_IRQ11_CTRL                0x02B80558
+#define CS35L41_DSP1_IRQ12_CTRL                0x02B80560
+#define CS35L41_DSP1_IRQ13_CTRL                0x02B80568
+#define CS35L41_DSP1_IRQ14_CTRL                0x02B80570
+#define CS35L41_DSP1_IRQ15_CTRL                0x02B80578
+#define CS35L41_DSP1_IRQ16_CTRL                0x02B80580
+#define CS35L41_DSP1_IRQ17_CTRL                0x02B80588
+#define CS35L41_DSP1_IRQ18_CTRL                0x02B80590
+#define CS35L41_DSP1_IRQ19_CTRL                0x02B80598
+#define CS35L41_DSP1_IRQ20_CTRL                0x02B805A0
+#define CS35L41_DSP1_IRQ21_CTRL                0x02B805A8
+#define CS35L41_DSP1_IRQ22_CTRL                0x02B805B0
+#define CS35L41_DSP1_IRQ23_CTRL                0x02B805B8
+#define CS35L41_DSP1_SCRATCH1          0x02B805C0
+#define CS35L41_DSP1_SCRATCH2          0x02B805C8
+#define CS35L41_DSP1_SCRATCH3          0x02B805D0
+#define CS35L41_DSP1_SCRATCH4          0x02B805D8
+#define CS35L41_DSP1_CCM_CORE_CTRL     0x02BC1000
+#define CS35L41_DSP1_CCM_CLK_OVERRIDE  0x02BC1008
+#define CS35L41_DSP1_XM_MSTR_EN                0x02BC2000
+#define CS35L41_DSP1_XM_CORE_PRI       0x02BC2008
+#define CS35L41_DSP1_XM_AHB_PACK_PL_PRI        0x02BC2010
+#define CS35L41_DSP1_XM_AHB_UP_PL_PRI  0x02BC2018
+#define CS35L41_DSP1_XM_ACCEL_PL0_PRI  0x02BC2020
+#define CS35L41_DSP1_XM_NPL0_PRI       0x02BC2078
+#define CS35L41_DSP1_YM_MSTR_EN                0x02BC20C0
+#define CS35L41_DSP1_YM_CORE_PRI       0x02BC20C8
+#define CS35L41_DSP1_YM_AHB_PACK_PL_PRI        0x02BC20D0
+#define CS35L41_DSP1_YM_AHB_UP_PL_PRI  0x02BC20D8
+#define CS35L41_DSP1_YM_ACCEL_PL0_PRI  0x02BC20E0
+#define CS35L41_DSP1_YM_NPL0_PRI       0x02BC2138
+#define CS35L41_DSP1_PM_MSTR_EN                0x02BC2180
+#define CS35L41_DSP1_PM_PATCH0_ADDR    0x02BC2188
+#define CS35L41_DSP1_PM_PATCH0_EN      0x02BC218C
+#define CS35L41_DSP1_PM_PATCH0_DATA_LO 0x02BC2190
+#define CS35L41_DSP1_PM_PATCH0_DATA_HI 0x02BC2194
+#define CS35L41_DSP1_PM_PATCH1_ADDR    0x02BC2198
+#define CS35L41_DSP1_PM_PATCH1_EN      0x02BC219C
+#define CS35L41_DSP1_PM_PATCH1_DATA_LO 0x02BC21A0
+#define CS35L41_DSP1_PM_PATCH1_DATA_HI 0x02BC21A4
+#define CS35L41_DSP1_PM_PATCH2_ADDR    0x02BC21A8
+#define CS35L41_DSP1_PM_PATCH2_EN      0x02BC21AC
+#define CS35L41_DSP1_PM_PATCH2_DATA_LO 0x02BC21B0
+#define CS35L41_DSP1_PM_PATCH2_DATA_HI 0x02BC21B4
+#define CS35L41_DSP1_PM_PATCH3_ADDR    0x02BC21B8
+#define CS35L41_DSP1_PM_PATCH3_EN      0x02BC21BC
+#define CS35L41_DSP1_PM_PATCH3_DATA_LO 0x02BC21C0
+#define CS35L41_DSP1_PM_PATCH3_DATA_HI 0x02BC21C4
+#define CS35L41_DSP1_PM_PATCH4_ADDR    0x02BC21C8
+#define CS35L41_DSP1_PM_PATCH4_EN      0x02BC21CC
+#define CS35L41_DSP1_PM_PATCH4_DATA_LO 0x02BC21D0
+#define CS35L41_DSP1_PM_PATCH4_DATA_HI 0x02BC21D4
+#define CS35L41_DSP1_PM_PATCH5_ADDR    0x02BC21D8
+#define CS35L41_DSP1_PM_PATCH5_EN      0x02BC21DC
+#define CS35L41_DSP1_PM_PATCH5_DATA_LO 0x02BC21E0
+#define CS35L41_DSP1_PM_PATCH5_DATA_HI 0x02BC21E4
+#define CS35L41_DSP1_PM_PATCH6_ADDR    0x02BC21E8
+#define CS35L41_DSP1_PM_PATCH6_EN      0x02BC21EC
+#define CS35L41_DSP1_PM_PATCH6_DATA_LO 0x02BC21F0
+#define CS35L41_DSP1_PM_PATCH6_DATA_HI 0x02BC21F4
+#define CS35L41_DSP1_PM_PATCH7_ADDR    0x02BC21F8
+#define CS35L41_DSP1_PM_PATCH7_EN      0x02BC21FC
+#define CS35L41_DSP1_PM_PATCH7_DATA_LO 0x02BC2200
+#define CS35L41_DSP1_PM_PATCH7_DATA_HI 0x02BC2204
+#define CS35L41_DSP1_MPU_XM_ACCESS0    0x02BC3000
+#define CS35L41_DSP1_MPU_YM_ACCESS0    0x02BC3004
+#define CS35L41_DSP1_MPU_WNDW_ACCESS0  0x02BC3008
+#define CS35L41_DSP1_MPU_XREG_ACCESS0  0x02BC300C
+#define CS35L41_DSP1_MPU_YREG_ACCESS0  0x02BC3014
+#define CS35L41_DSP1_MPU_XM_ACCESS1    0x02BC3018
+#define CS35L41_DSP1_MPU_YM_ACCESS1    0x02BC301C
+#define CS35L41_DSP1_MPU_WNDW_ACCESS1  0x02BC3020
+#define CS35L41_DSP1_MPU_XREG_ACCESS1  0x02BC3024
+#define CS35L41_DSP1_MPU_YREG_ACCESS1  0x02BC302C
+#define CS35L41_DSP1_MPU_XM_ACCESS2    0x02BC3030
+#define CS35L41_DSP1_MPU_YM_ACCESS2    0x02BC3034
+#define CS35L41_DSP1_MPU_WNDW_ACCESS2  0x02BC3038
+#define CS35L41_DSP1_MPU_XREG_ACCESS2  0x02BC303C
+#define CS35L41_DSP1_MPU_YREG_ACCESS2  0x02BC3044
+#define CS35L41_DSP1_MPU_XM_ACCESS3    0x02BC3048
+#define CS35L41_DSP1_MPU_YM_ACCESS3    0x02BC304C
+#define CS35L41_DSP1_MPU_WNDW_ACCESS3  0x02BC3050
+#define CS35L41_DSP1_MPU_XREG_ACCESS3  0x02BC3054
+#define CS35L41_DSP1_MPU_YREG_ACCESS3  0x02BC305C
+#define CS35L41_DSP1_MPU_XM_VIO_ADDR   0x02BC3100
+#define CS35L41_DSP1_MPU_XM_VIO_STATUS 0x02BC3104
+#define CS35L41_DSP1_MPU_YM_VIO_ADDR   0x02BC3108
+#define CS35L41_DSP1_MPU_YM_VIO_STATUS 0x02BC310C
+#define CS35L41_DSP1_MPU_PM_VIO_ADDR   0x02BC3110
+#define CS35L41_DSP1_MPU_PM_VIO_STATUS 0x02BC3114
+#define CS35L41_DSP1_MPU_LOCK_CONFIG   0x02BC3140
+#define CS35L41_DSP1_MPU_WDT_RST_CTRL  0x02BC3180
+#define CS35L41_DSP1_STRMARB_MSTR0_CFG0        0x02BC5000
+#define CS35L41_DSP1_STRMARB_MSTR0_CFG1        0x02BC5004
+#define CS35L41_DSP1_STRMARB_MSTR0_CFG2        0x02BC5008
+#define CS35L41_DSP1_STRMARB_MSTR1_CFG0        0x02BC5010
+#define CS35L41_DSP1_STRMARB_MSTR1_CFG1        0x02BC5014
+#define CS35L41_DSP1_STRMARB_MSTR1_CFG2        0x02BC5018
+#define CS35L41_DSP1_STRMARB_MSTR2_CFG0        0x02BC5020
+#define CS35L41_DSP1_STRMARB_MSTR2_CFG1        0x02BC5024
+#define CS35L41_DSP1_STRMARB_MSTR2_CFG2        0x02BC5028
+#define CS35L41_DSP1_STRMARB_MSTR3_CFG0        0x02BC5030
+#define CS35L41_DSP1_STRMARB_MSTR3_CFG1        0x02BC5034
+#define CS35L41_DSP1_STRMARB_MSTR3_CFG2        0x02BC5038
+#define CS35L41_DSP1_STRMARB_MSTR4_CFG0        0x02BC5040
+#define CS35L41_DSP1_STRMARB_MSTR4_CFG1        0x02BC5044
+#define CS35L41_DSP1_STRMARB_MSTR4_CFG2        0x02BC5048
+#define CS35L41_DSP1_STRMARB_MSTR5_CFG0        0x02BC5050
+#define CS35L41_DSP1_STRMARB_MSTR5_CFG1        0x02BC5054
+#define CS35L41_DSP1_STRMARB_MSTR5_CFG2        0x02BC5058
+#define CS35L41_DSP1_STRMARB_MSTR6_CFG0        0x02BC5060
+#define CS35L41_DSP1_STRMARB_MSTR6_CFG1        0x02BC5064
+#define CS35L41_DSP1_STRMARB_MSTR6_CFG2        0x02BC5068
+#define CS35L41_DSP1_STRMARB_MSTR7_CFG0        0x02BC5070
+#define CS35L41_DSP1_STRMARB_MSTR7_CFG1        0x02BC5074
+#define CS35L41_DSP1_STRMARB_MSTR7_CFG2        0x02BC5078
+#define CS35L41_DSP1_STRMARB_TX0_CFG0  0x02BC5200
+#define CS35L41_DSP1_STRMARB_TX0_CFG1  0x02BC5204
+#define CS35L41_DSP1_STRMARB_TX1_CFG0  0x02BC5208
+#define CS35L41_DSP1_STRMARB_TX1_CFG1  0x02BC520C
+#define CS35L41_DSP1_STRMARB_TX2_CFG0  0x02BC5210
+#define CS35L41_DSP1_STRMARB_TX2_CFG1  0x02BC5214
+#define CS35L41_DSP1_STRMARB_TX3_CFG0  0x02BC5218
+#define CS35L41_DSP1_STRMARB_TX3_CFG1  0x02BC521C
+#define CS35L41_DSP1_STRMARB_TX4_CFG0  0x02BC5220
+#define CS35L41_DSP1_STRMARB_TX4_CFG1  0x02BC5224
+#define CS35L41_DSP1_STRMARB_TX5_CFG0  0x02BC5228
+#define CS35L41_DSP1_STRMARB_TX5_CFG1  0x02BC522C
+#define CS35L41_DSP1_STRMARB_TX6_CFG0  0x02BC5230
+#define CS35L41_DSP1_STRMARB_TX6_CFG1  0x02BC5234
+#define CS35L41_DSP1_STRMARB_TX7_CFG0  0x02BC5238
+#define CS35L41_DSP1_STRMARB_TX7_CFG1  0x02BC523C
+#define CS35L41_DSP1_STRMARB_RX0_CFG0  0x02BC5400
+#define CS35L41_DSP1_STRMARB_RX0_CFG1  0x02BC5404
+#define CS35L41_DSP1_STRMARB_RX1_CFG0  0x02BC5408
+#define CS35L41_DSP1_STRMARB_RX1_CFG1  0x02BC540C
+#define CS35L41_DSP1_STRMARB_RX2_CFG0  0x02BC5410
+#define CS35L41_DSP1_STRMARB_RX2_CFG1  0x02BC5414
+#define CS35L41_DSP1_STRMARB_RX3_CFG0  0x02BC5418
+#define CS35L41_DSP1_STRMARB_RX3_CFG1  0x02BC541C
+#define CS35L41_DSP1_STRMARB_RX4_CFG0  0x02BC5420
+#define CS35L41_DSP1_STRMARB_RX4_CFG1  0x02BC5424
+#define CS35L41_DSP1_STRMARB_RX5_CFG0  0x02BC5428
+#define CS35L41_DSP1_STRMARB_RX5_CFG1  0x02BC542C
+#define CS35L41_DSP1_STRMARB_RX6_CFG0  0x02BC5430
+#define CS35L41_DSP1_STRMARB_RX6_CFG1  0x02BC5434
+#define CS35L41_DSP1_STRMARB_RX7_CFG0  0x02BC5438
+#define CS35L41_DSP1_STRMARB_RX7_CFG1  0x02BC543C
+#define CS35L41_DSP1_STRMARB_IRQ0_CFG0 0x02BC5600
+#define CS35L41_DSP1_STRMARB_IRQ0_CFG1 0x02BC5604
+#define CS35L41_DSP1_STRMARB_IRQ0_CFG2 0x02BC5608
+#define CS35L41_DSP1_STRMARB_IRQ1_CFG0 0x02BC5610
+#define CS35L41_DSP1_STRMARB_IRQ1_CFG1 0x02BC5614
+#define CS35L41_DSP1_STRMARB_IRQ1_CFG2 0x02BC5618
+#define CS35L41_DSP1_STRMARB_IRQ2_CFG0 0x02BC5620
+#define CS35L41_DSP1_STRMARB_IRQ2_CFG1 0x02BC5624
+#define CS35L41_DSP1_STRMARB_IRQ2_CFG2 0x02BC5628
+#define CS35L41_DSP1_STRMARB_IRQ3_CFG0 0x02BC5630
+#define CS35L41_DSP1_STRMARB_IRQ3_CFG1 0x02BC5634
+#define CS35L41_DSP1_STRMARB_IRQ3_CFG2 0x02BC5638
+#define CS35L41_DSP1_STRMARB_IRQ4_CFG0 0x02BC5640
+#define CS35L41_DSP1_STRMARB_IRQ4_CFG1 0x02BC5644
+#define CS35L41_DSP1_STRMARB_IRQ4_CFG2 0x02BC5648
+#define CS35L41_DSP1_STRMARB_IRQ5_CFG0 0x02BC5650
+#define CS35L41_DSP1_STRMARB_IRQ5_CFG1 0x02BC5654
+#define CS35L41_DSP1_STRMARB_IRQ5_CFG2 0x02BC5658
+#define CS35L41_DSP1_STRMARB_IRQ6_CFG0 0x02BC5660
+#define CS35L41_DSP1_STRMARB_IRQ6_CFG1 0x02BC5664
+#define CS35L41_DSP1_STRMARB_IRQ6_CFG2 0x02BC5668
+#define CS35L41_DSP1_STRMARB_IRQ7_CFG0 0x02BC5670
+#define CS35L41_DSP1_STRMARB_IRQ7_CFG1 0x02BC5674
+#define CS35L41_DSP1_STRMARB_IRQ7_CFG2 0x02BC5678
+#define CS35L41_DSP1_STRMARB_RESYNC_MSK        0x02BC5A00
+#define CS35L41_DSP1_STRMARB_ERR_STATUS        0x02BC5A08
+#define CS35L41_DSP1_INTPCTL_RES_STATIC        0x02BC6000
+#define CS35L41_DSP1_INTPCTL_RES_DYN   0x02BC6004
+#define CS35L41_DSP1_INTPCTL_NMI_CTRL  0x02BC6008
+#define CS35L41_DSP1_INTPCTL_IRQ_INV   0x02BC6010
+#define CS35L41_DSP1_INTPCTL_IRQ_MODE  0x02BC6014
+#define CS35L41_DSP1_INTPCTL_IRQ_EN    0x02BC6018
+#define CS35L41_DSP1_INTPCTL_IRQ_MSK   0x02BC601C
+#define CS35L41_DSP1_INTPCTL_IRQ_FLUSH 0x02BC6020
+#define CS35L41_DSP1_INTPCTL_IRQ_MSKCLR        0x02BC6024
+#define CS35L41_DSP1_INTPCTL_IRQ_FRC   0x02BC6028
+#define CS35L41_DSP1_INTPCTL_IRQ_MSKSET        0x02BC602C
+#define CS35L41_DSP1_INTPCTL_IRQ_ERR   0x02BC6030
+#define CS35L41_DSP1_INTPCTL_IRQ_PEND  0x02BC6034
+#define CS35L41_DSP1_INTPCTL_IRQ_GEN   0x02BC6038
+#define CS35L41_DSP1_INTPCTL_TESTBITS  0x02BC6040
+#define CS35L41_DSP1_WDT_CONTROL       0x02BC7000
+#define CS35L41_DSP1_WDT_STATUS                0x02BC7008
+#define CS35L41_DSP1_YMEM_PACK_0       0x02C00000
+#define CS35L41_DSP1_YMEM_PACK_1532    0x02C017F0
+#define CS35L41_DSP1_YMEM_UNPACK32_0   0x03000000
+#define CS35L41_DSP1_YMEM_UNPACK32_1022        0x03000FF8
+#define CS35L41_DSP1_YMEM_UNPACK24_0   0x03400000
+#define CS35L41_DSP1_YMEM_UNPACK24_2045        0x03401FF4
+#define CS35L41_DSP1_PMEM_0            0x03800000
+#define CS35L41_DSP1_PMEM_5114         0x03804FE8
+
+/*test regs for emulation bringup*/
+#define CS35L41_PLL_OVR                        0x00003018
+#define CS35L41_BST_TEST_DUTY          0x00003900
+#define CS35L41_DIGPWM_IOCTRL          0x0000706C
+
+/*registers populated by OTP*/
+#define CS35L41_OTP_TRIM_1     0x0000208c
+#define CS35L41_OTP_TRIM_2     0x00002090
+#define CS35L41_OTP_TRIM_3     0x00003010
+#define CS35L41_OTP_TRIM_4     0x0000300C
+#define CS35L41_OTP_TRIM_5     0x0000394C
+#define CS35L41_OTP_TRIM_6     0x00003950
+#define CS35L41_OTP_TRIM_7     0x00003954
+#define CS35L41_OTP_TRIM_8     0x00003958
+#define CS35L41_OTP_TRIM_9     0x0000395C
+#define CS35L41_OTP_TRIM_10    0x0000416C
+#define CS35L41_OTP_TRIM_11    0x00004160
+#define CS35L41_OTP_TRIM_12    0x00004170
+#define CS35L41_OTP_TRIM_13    0x00004360
+#define CS35L41_OTP_TRIM_14    0x00004448
+#define CS35L41_OTP_TRIM_15    0x0000444C
+#define CS35L41_OTP_TRIM_16    0x00006E30
+#define CS35L41_OTP_TRIM_17    0x00006E34
+#define CS35L41_OTP_TRIM_18    0x00006E38
+#define CS35L41_OTP_TRIM_19    0x00006E3C
+#define CS35L41_OTP_TRIM_20    0x00006E40
+#define CS35L41_OTP_TRIM_21    0x00006E44
+#define CS35L41_OTP_TRIM_22    0x00006E48
+#define CS35L41_OTP_TRIM_23    0x00006E4C
+#define CS35L41_OTP_TRIM_24    0x00006E50
+#define CS35L41_OTP_TRIM_25    0x00006E54
+#define CS35L41_OTP_TRIM_26    0x00006E58
+#define CS35L41_OTP_TRIM_27    0x00006E5C
+#define CS35L41_OTP_TRIM_28    0x00006E60
+#define CS35L41_OTP_TRIM_29    0x00006E64
+#define CS35L41_OTP_TRIM_30    0x00007418
+#define CS35L41_OTP_TRIM_31    0x0000741C
+#define CS35L41_OTP_TRIM_32    0x00007434
+#define CS35L41_OTP_TRIM_33    0x00007068
+#define CS35L41_OTP_TRIM_34    0x0000410C
+#define CS35L41_OTP_TRIM_35    0x0000400C
+#define CS35L41_OTP_TRIM_36    0x00002030
+
+#define CS35L41_MAX_CACHE_REG          0x0000006F
+#define CS35L41_OTP_SIZE_WORDS         32
+#define CS35L41_NUM_OTP_ELEM           100
+#define CS35L41_NUM_OTP_MAPS           4
+
+#define CS35L41_VALID_PDATA            0x80000000
+
+#define CS35L41_SCLK_MSTR_MASK         0x10
+#define CS35L41_SCLK_MSTR_SHIFT                4
+#define CS35L41_LRCLK_MSTR_MASK                0x01
+#define CS35L41_LRCLK_MSTR_SHIFT       0
+#define CS35L41_SCLK_INV_MASK          0x40
+#define CS35L41_SCLK_INV_SHIFT         6
+#define CS35L41_LRCLK_INV_MASK         0x04
+#define CS35L41_LRCLK_INV_SHIFT                2
+#define CS35L41_SCLK_FRC_MASK          0x20
+#define CS35L41_SCLK_FRC_SHIFT         5
+#define CS35L41_LRCLK_FRC_MASK         0x02
+#define CS35L41_LRCLK_FRC_SHIFT                1
+
+#define CS35L41_AMP_GAIN_ZC_MASK       0x0400
+#define CS35L41_AMP_GAIN_ZC_SHIFT      10
+
+#define CS35L41_BST_CTL_MASK           0xFF
+#define CS35L41_BST_CTL_SEL_MASK       0x03
+#define CS35L41_BST_CTL_SEL_REG                0x00
+#define CS35L41_BST_CTL_SEL_CLASSH     0x01
+#define CS35L41_BST_IPK_MASK           0x7F
+#define CS35L41_BST_IPK_SHIFT          0
+#define CS35L41_BST_LIM_MASK           0x4
+#define CS35L41_BST_LIM_SHIFT          2
+#define CS35L41_BST_K1_MASK            0x000000FF
+#define CS35L41_BST_K1_SHIFT           0
+#define CS35L41_BST_K2_MASK            0x0000FF00
+#define CS35L41_BST_K2_SHIFT           8
+#define CS35L41_BST_SLOPE_MASK         0x0000FF00
+#define CS35L41_BST_SLOPE_SHIFT                8
+#define CS35L41_BST_LBST_VAL_MASK      0x00000003
+#define CS35L41_BST_LBST_VAL_SHIFT     0
+
+#define CS35L41_TEMP_THLD_MASK         0x03
+#define CS35L41_VMON_IMON_VOL_MASK     0x07FF07FF
+#define CS35L41_PDM_MODE_MASK          0x01
+#define CS35L41_PDM_MODE_SHIFT         0
+
+#define CS35L41_CH_MEM_DEPTH_MASK      0x07
+#define CS35L41_CH_MEM_DEPTH_SHIFT     0
+#define CS35L41_CH_HDRM_CTL_MASK       0x007F0000
+#define CS35L41_CH_HDRM_CTL_SHIFT      16
+#define CS35L41_CH_REL_RATE_MASK       0xFF00
+#define CS35L41_CH_REL_RATE_SHIFT      8
+#define CS35L41_CH_WKFET_DLY_MASK      0x001C
+#define CS35L41_CH_WKFET_DLY_SHIFT     2
+#define CS35L41_CH_WKFET_THLD_MASK     0x0F00
+#define CS35L41_CH_WKFET_THLD_SHIFT    8
+
+#define CS35L41_NG_ENABLE_MASK         0x00010000
+#define CS35L41_NG_ENABLE_SHIFT                16
+#define CS35L41_NG_THLD_MASK           0x7
+#define CS35L41_NG_THLD_SHIFT          0
+#define CS35L41_NG_DELAY_MASK          0x0F00
+#define CS35L41_NG_DELAY_SHIFT         8
+
+#define CS35L41_ASP_FMT_MASK           0x0700
+#define CS35L41_ASP_FMT_SHIFT          8
+#define CS35L41_ASP_DOUT_HIZ_MASK      0x03
+#define CS35L41_ASP_DOUT_HIZ_SHIFT     0
+#define CS35L41_ASP_WIDTH_16           0x10
+#define CS35L41_ASP_WIDTH_24           0x18
+#define CS35L41_ASP_WIDTH_32           0x20
+#define CS35L41_ASP_WIDTH_TX_MASK      0xFF0000
+#define CS35L41_ASP_WIDTH_TX_SHIFT     16
+#define CS35L41_ASP_WIDTH_RX_MASK      0xFF000000
+#define CS35L41_ASP_WIDTH_RX_SHIFT     24
+#define CS35L41_ASP_RX1_SLOT_MASK      0x3F
+#define CS35L41_ASP_RX1_SLOT_SHIFT     0
+#define CS35L41_ASP_RX2_SLOT_MASK      0x3F00
+#define CS35L41_ASP_RX2_SLOT_SHIFT     8
+#define CS35L41_ASP_RX_WL_MASK         0x3F
+#define CS35L41_ASP_TX_WL_MASK         0x3F
+#define CS35L41_ASP_RX_WL_SHIFT                0
+#define CS35L41_ASP_TX_WL_SHIFT                0
+#define CS35L41_ASP_SOURCE_MASK                0x7F
+
+#define CS35L41_INPUT_SRC_ASPRX1       0x08
+#define CS35L41_INPUT_SRC_ASPRX2       0x09
+#define CS35L41_INPUT_SRC_VMON         0x18
+#define CS35L41_INPUT_SRC_IMON         0x19
+#define CS35L41_INPUT_SRC_CLASSH       0x21
+#define CS35L41_INPUT_SRC_VPMON                0x28
+#define CS35L41_INPUT_SRC_VBSTMON      0x29
+#define CS35L41_INPUT_SRC_TEMPMON      0x3A
+#define CS35L41_INPUT_SRC_RSVD         0x3B
+#define CS35L41_INPUT_DSP_TX1          0x32
+#define CS35L41_INPUT_DSP_TX2          0x33
+
+#define CS35L41_PLL_CLK_SEL_MASK       0x07
+#define CS35L41_PLL_CLK_SEL_SHIFT      0
+#define CS35L41_PLL_CLK_EN_MASK                0x10
+#define CS35L41_PLL_CLK_EN_SHIFT       4
+#define CS35L41_PLL_OPENLOOP_MASK      0x0800
+#define CS35L41_PLL_OPENLOOP_SHIFT     11
+#define CS35L41_PLLSRC_SCLK            0
+#define CS35L41_PLLSRC_LRCLK           1
+#define CS35L41_PLLSRC_SELF            3
+#define CS35L41_PLLSRC_PDMCLK          4
+#define CS35L41_PLLSRC_MCLK            5
+#define CS35L41_PLLSRC_SWIRE           7
+#define CS35L41_REFCLK_FREQ_MASK       0x7E0
+#define CS35L41_REFCLK_FREQ_SHIFT      5
+
+#define CS35L41_GLOBAL_FS_MASK         0x1F
+#define CS35L41_GLOBAL_FS_SHIFT                0
+
+#define CS35L41_GLOBAL_EN_MASK         0x01
+#define CS35L41_GLOBAL_EN_SHIFT                0
+
+#define CS35L41_PDN_DONE_MASK          0x00800000
+#define CS35L41_PDN_DONE_SHIFT         23
+#define CS35L41_PUP_DONE_MASK          0x01000000
+#define CS35L41_PUP_DONE_SHIFT         24
+
+#define CS35L36_PUP_DONE_IRQ_UNMASK    0x5F
+#define CS35L36_PUP_DONE_IRQ_MASK      0xBF
+
+#define CS35L41_AMP_SHORT_ERR          0x80000000
+#define CS35L41_BST_SHORT_ERR          0x0100
+#define CS35L41_TEMP_WARN              0x8000
+#define CS35L41_TEMP_ERR               0x00020000
+#define CS35L41_BST_OVP_ERR            0x40
+#define CS35L41_BST_DCM_UVP_ERR                0x80
+#define CS35L41_OTP_BOOT_DONE          0x02
+#define CS35L41_PLL_UNLOCK             0x10
+#define CS35L41_OTP_BOOT_ERR           0x80000000
+
+#define CS35L41_AMP_SHORT_ERR_RLS      0x02
+#define CS35L41_BST_SHORT_ERR_RLS      0x04
+#define CS35L41_BST_OVP_ERR_RLS                0x08
+#define CS35L41_BST_UVP_ERR_RLS                0x10
+#define CS35L41_TEMP_WARN_ERR_RLS      0x20
+#define CS35L41_TEMP_ERR_RLS           0x40
+
+#define CS35L41_INT1_MASK_DEFAULT      0x7FFCFE3F
+#define CS35L41_INT1_UNMASK_PUP                0xFEFFFFFF
+#define CS35L41_INT1_UNMASK_PDN                0xFF7FFFFF
+
+#define CS35L41_GPIO_DIR_MASK          0x80000000
+#define CS35L41_GPIO1_CTRL_MASK                0x00030000
+#define CS35L41_GPIO1_CTRL_SHIFT       16
+#define CS35L41_GPIO2_CTRL_MASK                0x07000000
+#define CS35L41_GPIO2_CTRL_SHIFT       24
+#define CS35L41_GPIO_CTRL_ACTV_LO      4
+#define CS35L41_GPIO_CTRL_ACTV_HI      5
+#define CS35L41_GPIO_POL_MASK          0x1000
+#define CS35L41_GPIO_POL_SHIFT         12
+
+#define CS35L41_CHIP_ID                        0x35a40
+#define CS35L41R_CHIP_ID               0x35b40
+#define CS35L41_MTLREVID_MASK          0x0F
+#define CS35L41_REVID_A0               0xA0
+
+#define CS35L41_DSP_N_RX_RATES         8
+#define CS35L41_DSP_N_TX_RATES         8
+#define CS35L41_HALO_CORE_RESET                0x00000200
+
+#define CS35L41_SPI_MAX_FREQ_OTP       4000000
+
+#define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+
+bool cs35l41_readable_reg(struct device *dev, unsigned int reg);
+bool cs35l41_volatile_reg(struct device *dev, unsigned int reg);
+
+struct cs35l41_otp_packed_element_t {
+       u32 reg;
+       u8 shift;
+       u8 size;
+};
+
+struct cs35l41_otp_map_element_t {
+       u32 id;
+       u32 num_elements;
+       const struct cs35l41_otp_packed_element_t *map;
+       u32 bit_offset;
+       u32 word_offset;
+};
+
+extern const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG];
+extern const struct cs35l41_otp_map_element_t
+                               cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS];
+
+#define CS35L41_CSPL_CMD_NONE                  0
+#define CS35L41_CSPL_CMD_MUTE                  1
+#define CS35L41_CSPL_CMD_UNMUTE                        2
+#define CS35L41_CSPL_CMD_REINIT                        3
+
+#define CS35L41_CSPL_XM_STRUCT_ADDR            0x0280020c
+#define CS35L41_CSPL_COMMAND                   0x02800210
+#define CS35L41_CSPL_CAL_STRUCT_ADDR           0x02800224
+#define CS35L41_AMB_TEMP_DEFAULT               25
+
+#endif /*__CS35L41_H__*/
old mode 100644 (file)
new mode 100755 (executable)
index 2eadfd1..29b040a
@@ -1493,7 +1493,12 @@ static int cs47l35_open(struct snd_compr_stream *stream)
        struct cs47l35 *cs47l35 = snd_soc_platform_get_drvdata(rtd->platform);
        struct madera_priv *priv = &cs47l35->core;
        struct madera *madera = priv->madera;
-       int n_adsp;
+       int n_adsp, channel;
+
+       channel = 0;
+       dev_dbg(madera->dev,
+                       "Open compr stream '%s' for DAI %d '%s'\n",
+                       stream->name, rtd->codec_dai->id, rtd->codec_dai->name);
 
        if (strcmp(rtd->codec_dai->name, "cs47l35-dsp-voicectrl") == 0) {
                n_adsp = 2;
@@ -1506,7 +1511,7 @@ static int cs47l35_open(struct snd_compr_stream *stream)
                return -EINVAL;
        }
 
-       return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+       return wm_adsp_compr_open(&priv->adsp[n_adsp], stream, channel);
 }
 
 static irqreturn_t cs47l35_adsp2_irq(int irq, void *data)
@@ -1515,11 +1520,11 @@ static irqreturn_t cs47l35_adsp2_irq(int irq, void *data)
        struct madera_priv *priv = &cs47l35->core;
        struct madera *madera = priv->madera;
        struct madera_voice_trigger_info trig_info;
-       int serviced = 0;
+       int serviced = 0, channel = 0;
        int i, ret;
 
        for (i = 0; i < CS47L35_NUM_ADSP; ++i) {
-               ret = wm_adsp_compr_handle_irq(&priv->adsp[i]);
+               ret = wm_adsp_compr_handle_irq(&priv->adsp[i], channel);
                if (ret != -ENODEV)
                        serviced++;
                if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
diff --git a/sound/soc/codecs/madera-slimbus.c b/sound/soc/codecs/madera-slimbus.c
new file mode 100755 (executable)
index 0000000..15cc39b
--- /dev/null
@@ -0,0 +1,490 @@
+#include <linux/delay.h>
+#include <linux/device.h>
+
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+#include <linux/mfd/madera/pdata.h>
+
+#include "madera-slimbus.h"
+#include "madera.h"
+
+/* As there is no sane way to recover an entry point to the slimbus core
+ * from anything other than a slim_device probe, simply use this as a bridge
+ * between ASoC and the slimbus core to get our slimbus generic device
+ * associated with our ASoC specific slimbus code
+ */
+static struct slim_device *stashed_slim_dev;
+
+static struct mutex slim_tx_lock;
+static struct mutex slim_rx_lock;
+
+static int madera_slim_get_la(struct slim_device *slim, u8 *la)
+{
+       int ret;
+       const unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+       if (slim == NULL)
+               return -EINVAL;
+
+       do {
+               ret = slim_get_logical_addr(slim, slim->e_addr, 6, la);
+               if (!ret)
+                       break;
+               /* Give SLIMBUS time to report present and be ready. */
+               usleep_range(1000, 1100);
+               dev_info(&slim->dev, "retrying get logical addr\n");
+       } while time_before(jiffies, timeout);
+
+       dev_info(&slim->dev, "LA %d\n", *la);
+       return 0;
+}
+
+static void madera_slim_fixup_prop(struct slim_ch *prop,
+                                  u32 samplerate, u32 sampleszbits)
+{
+       prop->prot = SLIM_AUTO_ISO;
+       prop->baser = SLIM_RATE_4000HZ;
+       prop->dataf = SLIM_CH_DATAF_NOT_DEFINED;
+       prop->auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+       prop->ratem = samplerate / 4000;
+       prop->sampleszbits = sampleszbits;
+}
+
+#define TX_STREAM_1 128
+#define TX_STREAM_2 132
+#define TX_STREAM_3 131
+
+#define RX_STREAM_1 144
+#define RX_STREAM_2 146
+#define RX_STREAM_3 148
+
+static u32 rx_porth1[2], rx_porth2[2], rx_porth3[2];
+static u32 tx_porth1[3], tx_porth2[2], tx_porth3[1];
+static u16 rx_handles1[] = { RX_STREAM_1, RX_STREAM_1 + 1 };
+static u16 rx_handles2[] = { RX_STREAM_2, RX_STREAM_2 + 1 };
+static u16 rx_handles3[] = { RX_STREAM_3, RX_STREAM_3 + 1 };
+static u16 tx_handles1[] = { TX_STREAM_1, TX_STREAM_1 + 1, TX_STREAM_1 + 2};
+static u16 tx_handles2[] = { TX_STREAM_2, TX_STREAM_2 + 1 };
+static u16 tx_handles3[] = { TX_STREAM_3 };
+static u16 rx_group1, rx_group2, rx_group3;
+static u16 tx_group1, tx_group2, tx_group3;
+
+int madera_slim_tx_ev(struct snd_soc_dapm_widget *w,
+                     struct snd_kcontrol *kcontrol,
+                     int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct madera_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct madera *madera = priv->madera;
+       struct slim_ch prop;
+       int ret = 0, i;
+       u32 *porth;
+       u16 *handles, *group;
+       int chcnt;
+
+       if (stashed_slim_dev == NULL) {
+               dev_err(madera->dev, "slim device undefined.\n");
+               return -EINVAL;
+       }
+       mutex_lock(&slim_tx_lock);
+       switch (w->shift) {
+       case MADERA_SLIMTX1_ENA_SHIFT:
+               dev_dbg(madera->dev, "TX1\n");
+               chcnt = priv->tx_chan_map_num[0];
+               if (chcnt > ARRAY_SIZE(tx_porth1)) {
+                       dev_err(madera->dev, "ERROR: Too many TX channels\n");
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               porth = tx_porth1;
+               handles = tx_handles1;
+               group = &tx_group1;
+               break;
+       case MADERA_SLIMTX5_ENA_SHIFT:
+               dev_dbg(madera->dev, "TX2\n");
+               chcnt = priv->tx_chan_map_num[1];
+               if (chcnt > ARRAY_SIZE(tx_porth2)) {
+                       dev_err(madera->dev, "ERROR: Too many TX channels\n");
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               porth = tx_porth2;
+               handles = tx_handles2;
+               group = &tx_group2;
+               break;
+       case MADERA_SLIMTX4_ENA_SHIFT:
+               dev_dbg(codec->dev, "TX3\n");
+               chcnt = priv->tx_chan_map_num[2];
+               if (chcnt > ARRAY_SIZE(tx_porth3)) {
+                       dev_err(madera->dev, "ERROR: Too many TX channels\n");
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               porth = tx_porth3;
+               handles = tx_handles3;
+               group = &tx_group3;
+               break;
+       default:
+               goto exit;
+       }
+       madera_slim_fixup_prop(&prop, 48000, 16);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+       case SND_SOC_DAPM_POST_PMU:
+               dev_dbg(madera->dev, "Start slimbus TX\n");
+               ret = slim_define_ch(stashed_slim_dev, &prop,
+                                    handles, chcnt, true, group);
+               if (ret != 0) {
+                       dev_err(madera->dev, "slim_define_ch() failed: %d\n",
+                               ret);
+                       goto exit;
+               }
+
+               for (i = 0; i < chcnt; i++) {
+                       ret = slim_connect_src(stashed_slim_dev,
+                                               porth[i], handles[i]);
+                       if (ret != 0) {
+                               dev_err(madera->dev, "src connect fail %d:%d\n",
+                                       i, ret);
+                               goto exit;
+                       }
+               }
+
+               ret = slim_control_ch(stashed_slim_dev, *group,
+                                       SLIM_CH_ACTIVATE, true);
+               if (ret != 0) {
+                       dev_err(madera->dev, "Failed to activate: %d\n", ret);
+                       goto exit;
+               }
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+       case SND_SOC_DAPM_PRE_PMD:
+               dev_dbg(madera->dev, "Stop slimbus Tx\n");
+               ret = slim_control_ch(stashed_slim_dev, *group,
+                                       SLIM_CH_REMOVE, true);
+               if (ret != 0)
+                       dev_err(madera->dev, "Failed to remove tx: %d\n", ret);
+
+               break;
+       default:
+               break;
+       }
+
+exit:
+       mutex_unlock(&slim_tx_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(madera_slim_tx_ev);
+
+int madera_slim_rx_ev(struct snd_soc_dapm_widget *w,
+                     struct snd_kcontrol *kcontrol,
+                     int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct madera_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct madera *madera = priv->madera;
+       struct slim_ch prop;
+       int ret = 0, i;
+       u32 *porth;
+       u16 *handles, *group;
+       int chcnt;
+       u32 rx_sampleszbits = 16, rx_samplerate = 48000;
+
+       if (stashed_slim_dev == NULL) {
+               dev_err(madera->dev, "slim device undefined.\n");
+               return -EINVAL;
+       }
+       mutex_lock(&slim_rx_lock);
+       switch (w->shift) {
+       case MADERA_SLIMRX1_ENA_SHIFT:
+               dev_dbg(madera->dev, "RX1\n");
+               chcnt = priv->rx_chan_map_num[0];
+               if (chcnt > ARRAY_SIZE(rx_porth1)) {
+                       dev_err(madera->dev, "ERROR: Too many RX channels\n");
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               porth = rx_porth1;
+               handles = rx_handles1;
+               group = &rx_group1;
+               rx_sampleszbits = priv->rx1_sampleszbits;
+               rx_samplerate = priv->rx1_samplerate;
+               break;
+       case MADERA_SLIMRX5_ENA_SHIFT:
+               dev_dbg(madera->dev, "RX2\n");
+               chcnt = priv->rx_chan_map_num[1];
+               if (chcnt > ARRAY_SIZE(rx_porth2)) {
+                       dev_err(madera->dev, "ERROR: Too many RX channels\n");
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               porth = rx_porth2;
+               handles = rx_handles2;
+               group = &rx_group2;
+               rx_sampleszbits = priv->rx2_sampleszbits;
+               rx_samplerate = priv->rx2_samplerate;
+               break;
+       case MADERA_SLIMRX3_ENA_SHIFT:
+               dev_dbg(codec->dev, "RX3\n");
+               chcnt = priv->rx_chan_map_num[2];
+               if (chcnt > ARRAY_SIZE(rx_porth3)) {
+                       dev_err(madera->dev, "ERROR: Too many RX channels\n");
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               porth = rx_porth3;
+               handles = rx_handles3;
+               group = &rx_group3;
+               break;
+       default:
+               goto exit;
+       }
+       madera_slim_fixup_prop(&prop, rx_samplerate, rx_sampleszbits);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+       case SND_SOC_DAPM_POST_PMU:
+               dev_dbg(madera->dev, "Start slimbus RX, rate=%d, bit=%d\n",
+                       rx_samplerate, rx_sampleszbits);
+               ret = slim_define_ch(stashed_slim_dev, &prop,
+                                    handles, chcnt, true, group);
+               if (ret != 0) {
+                       dev_err(madera->dev, "slim_define_ch() failed: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               for (i = 0; i < chcnt; i++) {
+                       ret = slim_connect_sink(stashed_slim_dev, &porth[i], 1,
+                                               handles[i]);
+                       if (ret != 0) {
+                               dev_err(madera->dev, "snk connect fail %d:%d\n",
+                                       i, ret);
+                               goto exit;
+                       }
+               }
+
+               ret = slim_control_ch(stashed_slim_dev, *group,
+                                       SLIM_CH_ACTIVATE, true);
+               if (ret != 0) {
+                       dev_err(madera->dev, "Failed to activate: %d\n", ret);
+                       goto exit;
+               }
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+       case SND_SOC_DAPM_PRE_PMD:
+               dev_dbg(madera->dev, "Stop slimbus Rx\n");
+               ret = slim_control_ch(stashed_slim_dev, *group,
+                                       SLIM_CH_REMOVE, true);
+               if (ret != 0)
+                       dev_err(madera->dev, "Failed to remove rx: %d\n", ret);
+
+               break;
+       default:
+               break;
+       }
+
+exit:
+       mutex_unlock(&slim_rx_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(madera_slim_rx_ev);
+
+int madera_set_channel_map(struct snd_soc_dai *dai,
+                          unsigned int tx_num, unsigned int *tx_slot,
+                          unsigned int rx_num, unsigned int *rx_slot)
+{
+       struct madera_priv *priv = snd_soc_codec_get_drvdata(dai->codec);
+       struct madera *madera = priv->madera;
+
+       u8 laddr;
+       int i, ret;
+       u32 *tx_porth, *rx_porth;
+       u16 *tx_handles, *rx_handles;
+       int tx_chcnt, rx_chcnt, tx_idx_step, rx_idx_step;
+       int tx_stream_idx, rx_stream_idx;
+       int *tx_priv_counter, *rx_priv_counter;
+       u16 *tx_chan_map_slot, *rx_chan_map_slot;
+
+       if (stashed_slim_dev == NULL) {
+               dev_err(madera->dev, "%s No slim device available\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (!priv->slim_logic_addr) {
+               madera_slim_get_la(stashed_slim_dev, &laddr);
+               priv->slim_logic_addr = laddr;
+       } else {
+               laddr = priv->slim_logic_addr;
+       }
+
+       switch (dai->id) {
+       case 4: /* cs47l35-slim1 */
+               tx_porth = tx_porth1;
+               tx_handles = tx_handles1;
+               tx_chcnt = ARRAY_SIZE(tx_porth1);
+               tx_idx_step = 8;
+               tx_stream_idx = TX_STREAM_1;
+               tx_priv_counter = &priv->tx_chan_map_num[0];
+               tx_chan_map_slot = priv->tx_chan_map_slot[0];
+
+               rx_porth = rx_porth1;
+               rx_handles = rx_handles1;
+               rx_chcnt = ARRAY_SIZE(rx_porth1);
+               rx_idx_step = 0;
+               rx_stream_idx = RX_STREAM_1;
+               rx_priv_counter = &priv->rx_chan_map_num[0];
+               rx_chan_map_slot = priv->rx_chan_map_slot[0];
+               break;
+       case 5: /* cs47l35-slim2 */
+               tx_porth = tx_porth2;
+               tx_handles = tx_handles2;
+               tx_chcnt = ARRAY_SIZE(tx_porth2);
+               tx_idx_step = 12;
+               tx_stream_idx = TX_STREAM_2;
+               tx_priv_counter = &priv->tx_chan_map_num[1];
+               tx_chan_map_slot = priv->tx_chan_map_slot[1];
+
+               rx_porth = rx_porth2;
+               rx_handles = rx_handles2;
+               rx_chcnt = ARRAY_SIZE(rx_porth2);
+               rx_idx_step = 4;
+               rx_stream_idx = RX_STREAM_2;
+               rx_priv_counter = &priv->rx_chan_map_num[1];
+               rx_chan_map_slot = priv->rx_chan_map_slot[1];
+               break;
+       default:
+               dev_err(madera->dev, "set_channel_map unknown dai->id %d\n",
+                       dai->id);
+               return -EINVAL;
+       }
+
+       if (rx_num > rx_chcnt)
+               rx_num = rx_chcnt;
+       if (rx_num > 0)
+               *rx_priv_counter = rx_num;
+
+       if (tx_num > tx_chcnt)
+               tx_num = tx_chcnt;
+       if (tx_num > 0)
+               *tx_priv_counter = tx_num;
+
+       /* This actually allocates the channel or refcounts it if there... */
+       for (i = 0; i < rx_num; i++) {
+               slim_get_slaveport(laddr, i + rx_idx_step, &rx_porth[i],
+                                  SLIM_SINK);
+
+               ret = slim_query_ch(stashed_slim_dev, rx_stream_idx + i,
+                                   &rx_handles[i]);
+               if (ret != 0) {
+                       dev_err(madera->dev, "slim_alloc_ch() failed: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               rx_chan_map_slot[i] = rx_slot[i] = rx_stream_idx + i;
+       }
+
+       for (i = 0; i < tx_num; i++) {
+               slim_get_slaveport(laddr, i + tx_idx_step, &tx_porth[i],
+                                  SLIM_SRC);
+
+               ret = slim_query_ch(stashed_slim_dev, tx_stream_idx + i,
+                                   &tx_handles[i]);
+               if (ret != 0) {
+                       dev_err(madera->dev, "slim_alloc_ch() failed: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               tx_chan_map_slot[i] = tx_slot[i] = tx_stream_idx + i;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(madera_set_channel_map);
+
+int madera_get_channel_map(struct snd_soc_dai *dai,
+                          unsigned int *tx_num, unsigned int *tx_slot,
+                          unsigned int *rx_num, unsigned int *rx_slot)
+{
+       struct madera_priv *priv = snd_soc_codec_get_drvdata(dai->codec);
+       struct madera *madera = priv->madera;
+       int i;
+       int tx_chan_map_num, rx_chan_map_num;
+       u16 *tx_chan_map_slot, *rx_chan_map_slot;
+
+       switch (dai->id) {
+       case 4: /* cs47l35-slim1 */
+               tx_chan_map_num = priv->tx_chan_map_num[0];
+               rx_chan_map_num = priv->rx_chan_map_num[0];
+               tx_chan_map_slot = priv->tx_chan_map_slot[0];
+               rx_chan_map_slot = priv->rx_chan_map_slot[0];
+               break;
+       case 5: /* cs47l35-slim2 */
+               tx_chan_map_num = priv->tx_chan_map_num[1];
+               rx_chan_map_num = priv->rx_chan_map_num[1];
+               tx_chan_map_slot = priv->tx_chan_map_slot[1];
+               rx_chan_map_slot = priv->rx_chan_map_slot[1];
+               break;
+       default:
+               dev_err(madera->dev, "get_channel_map unknown dai->id %d\n",
+                       dai->id);
+               return -EINVAL;
+       }
+
+       if (!rx_chan_map_num || !tx_chan_map_num)
+               return -EINVAL;
+
+       *rx_num = rx_chan_map_num;
+       *tx_num = tx_chan_map_num;
+
+       for (i = 0; i < rx_chan_map_num; i++)
+               rx_slot[i] = rx_chan_map_slot[i];
+
+       for (i = 0; i < tx_chan_map_num; i++)
+               tx_slot[i] = tx_chan_map_slot[i];
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(madera_get_channel_map);
+
+static const struct slim_device_id madera_slim_id[] = {
+       { "madera-slim-audio", 0 },
+       { },
+};
+
+static int madera_slim_audio_probe(struct slim_device *slim)
+{
+       stashed_slim_dev = slim;
+       dev_info(&slim->dev, "%s SLIM PROBE\n", __func__);
+       mutex_init(&slim_tx_lock);
+       mutex_init(&slim_rx_lock);
+
+       return 0;
+}
+
+static struct slim_driver madera_slim_audio = {
+       .driver = {
+               .name = "madera-slim-audio",
+               .owner = THIS_MODULE,
+       },
+       .probe = madera_slim_audio_probe,
+       .id_table = madera_slim_id,
+};
+
+static int __init madera_slim_audio_init(void)
+{
+       return slim_driver_register(&madera_slim_audio);
+}
+module_init(madera_slim_audio_init);
+
+static void __exit madera_slim_audio_exit(void)
+{
+       slim_driver_unregister(&madera_slim_audio);
+}
+module_exit(madera_slim_audio_exit);
diff --git a/sound/soc/codecs/madera-slimbus.h b/sound/soc/codecs/madera-slimbus.h
new file mode 100755 (executable)
index 0000000..7d8f980
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef MADERA_SLIMBUS_H
+#define MADERA_SLIMBUS_H
+
+#include <sound/soc.h>
+
+#define MADERA_SLIMBUS_MAX_CHANNELS 8
+#define CONFIG_SND_SOC_MADERA_SLIMBUS 1
+#ifdef CONFIG_SND_SOC_MADERA_SLIMBUS
+#include <linux/slimbus/slimbus.h>
+
+int madera_slim_tx_ev(struct snd_soc_dapm_widget *w,
+                     struct snd_kcontrol *kcontrol,
+                     int event);
+int madera_slim_rx_ev(struct snd_soc_dapm_widget *w,
+                     struct snd_kcontrol *kcontrol,
+                     int event);
+int madera_set_channel_map(struct snd_soc_dai *dai,
+                          unsigned int tx_num, unsigned int *tx_slot,
+                          unsigned int rx_num, unsigned int *rx_slot);
+int madera_get_channel_map(struct snd_soc_dai *dai,
+                          unsigned int *tx_num, unsigned int *tx_slot,
+                          unsigned int *rx_num, unsigned int *rx_slot);
+#else
+static inline int madera_slim_tx_ev(struct snd_soc_dapm_widget *w,
+                                   struct snd_kcontrol *kcontrol,
+                                   int event, int dai_id)
+{
+       return 0;
+}
+
+static inline int madera_slim_rx_ev(struct snd_soc_dapm_widget *w,
+                                   struct snd_kcontrol *kcontrol,
+                                   int event, int dai_id)
+{
+       return 0;
+}
+
+static inline int madera_set_channel_map(struct snd_soc_dai *dai,
+                                        unsigned int tx_num,
+                                        unsigned int *tx_slot,
+                                        unsigned int rx_num,
+                                        unsigned int *rx_slot)
+{
+       return 0;
+}
+
+static inline int madera_get_channel_map(struct snd_soc_dai *dai,
+                                        unsigned int *tx_num,
+                                        unsigned int *tx_slot,
+                                        unsigned int *rx_num,
+                                        unsigned int *rx_slot)
+{
+       return 0;
+}
+#endif
+
+#endif
old mode 100644 (file)
new mode 100755 (executable)
index 1516252..74b7fbf
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/list.h>
+#include <linux/of.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
-#include <linux/vmalloc.h>
 #include <linux/workqueue.h>
 #include <linux/debugfs.h>
 #include <sound/core.h>
  */
 #define WM_ADSP_FW_EVENT_SHUTDOWN            0x000001
 
+/*
+ * HALO system info
+ */
+#define HALO_SYS_INFO_XM_SRAM_SIZE           0x00010
+#define HALO_SYS_INFO_YM_SRAM_SIZE           0x00018
+#define HALO_SYS_INFO_XM_BANK_SIZE           0x00038
+#define HALO_SYS_INFO_YM_BANK_SIZE           0x0003c
+#define HALO_AHBM_WINDOW_DEBUG_0             0x02040
+#define HALO_AHBM_WINDOW_DEBUG_1             0x02044
+
+/*
+ * HALO core
+ */
+#define HALO_SAMPLE_RATE_RX1                 0x00080
+#define HALO_SAMPLE_RATE_TX1                 0x00280
+#define HALO_SCRATCH1                        0x005c0
+#define HALO_CCM_CORE_CONTROL                0x41000
+
+/*
+ * HALO Lock support
+ */
+#define HALO_MPU_UNLOCK_CODE_0               0x5555
+#define HALO_MPU_UNLOCK_CODE_1               0xaaaa
+
+/*
+ * HALO MPU banks
+ */
+#define HALO_MPU_XMEM_ACCESS_0               0x43000
+#define HALO_MPU_YMEM_ACCESS_0               0x43004
+#define HALO_MPU_WINDOW_ACCESS_0             0x43008
+#define HALO_MPU_XREG_ACCESS_0               0x4300C
+#define HALO_MPU_YREG_ACCESS_0               0x43014
+#define HALO_MPU_XMEM_ACCESS_1               0x43018
+#define HALO_MPU_YMEM_ACCESS_1               0x4301C
+#define HALO_MPU_WINDOW_ACCESS_1             0x43020
+#define HALO_MPU_XREG_ACCESS_1               0x43024
+#define HALO_MPU_YREG_ACCESS_1               0x4302C
+#define HALO_MPU_XMEM_ACCESS_2               0x43030
+#define HALO_MPU_YMEM_ACCESS_2               0x43034
+#define HALO_MPU_WINDOW_ACCESS_2             0x43038
+#define HALO_MPU_XREG_ACCESS_2               0x4303C
+#define HALO_MPU_YREG_ACCESS_2               0x43044
+#define HALO_MPU_XMEM_ACCESS_3               0x43048
+#define HALO_MPU_YMEM_ACCESS_3               0x4304C
+#define HALO_MPU_WINDOW_ACCESS_3             0x43050
+#define HALO_MPU_XREG_ACCESS_3               0x43054
+#define HALO_MPU_YREG_ACCESS_3               0x4305C
+#define HALO_MPU_XM_VIO_ADDR                 0x43100
+#define HALO_MPU_XM_VIO_STATUS               0x43104
+#define HALO_MPU_YM_VIO_ADDR                 0x43108
+#define HALO_MPU_YM_VIO_STATUS               0x4310C
+#define HALO_MPU_PM_VIO_ADDR                 0x43110
+#define HALO_MPU_PM_VIO_STATUS               0x43114
+#define HALO_MPU_LOCK_CONFIG                 0x43140
+
+/*
+ * HALO stream arb
+ */
+#define HALO_STREAM_ARB_MSTR0_CONFIG_0       0x45000
+#define HALO_STREAM_ARB_MSTR0_CONFIG_1       0x45004
+#define HALO_STREAM_ARB_MSTR0_CONFIG_2       0x45008
+#define HALO_STREAM_ARB_MSTR1_CONFIG_0       0x45010
+#define HALO_STREAM_ARB_MSTR1_CONFIG_1       0x45014
+#define HALO_STREAM_ARB_MSTR1_CONFIG_2       0x45018
+#define HALO_STREAM_ARB_MSTR2_CONFIG_0       0x45020
+#define HALO_STREAM_ARB_MSTR2_CONFIG_1       0x45024
+#define HALO_STREAM_ARB_MSTR2_CONFIG_2       0x45028
+#define HALO_STREAM_ARB_MSTR3_CONFIG_0       0x45030
+#define HALO_STREAM_ARB_MSTR3_CONFIG_1       0x45034
+#define HALO_STREAM_ARB_MSTR3_CONFIG_2       0x45038
+#define HALO_STREAM_ARB_MSTR4_CONFIG_0       0x45040
+#define HALO_STREAM_ARB_MSTR4_CONFIG_1       0x45044
+#define HALO_STREAM_ARB_MSTR4_CONFIG_2       0x45048
+#define HALO_STREAM_ARB_MSTR5_CONFIG_0       0x45050
+#define HALO_STREAM_ARB_MSTR5_CONFIG_1       0x45054
+#define HALO_STREAM_ARB_MSTR5_CONFIG_2       0x45058
+
+#define HALO_STREAM_ARB_TX1_CONFIG_0         0x45200
+#define HALO_STREAM_ARB_TX1_CONFIG_1         0x45204
+#define HALO_STREAM_ARB_TX2_CONFIG_0         0x45208
+#define HALO_STREAM_ARB_TX2_CONFIG_1         0x4520C
+#define HALO_STREAM_ARB_TX3_CONFIG_0         0x45210
+#define HALO_STREAM_ARB_TX3_CONFIG_1         0x45214
+#define HALO_STREAM_ARB_TX4_CONFIG_0         0x45218
+#define HALO_STREAM_ARB_TX4_CONFIG_1         0x4521C
+#define HALO_STREAM_ARB_TX5_CONFIG_0         0x45220
+#define HALO_STREAM_ARB_TX5_CONFIG_1         0x45224
+#define HALO_STREAM_ARB_TX6_CONFIG_0         0x45228
+#define HALO_STREAM_ARB_TX6_CONFIG_1         0x4522C
+#define HALO_STREAM_ARB_TX7_CONFIG_0         0x45230
+#define HALO_STREAM_ARB_TX7_CONFIG_1         0x45234
+#define HALO_STREAM_ARB_TX8_CONFIG_0         0x45238
+#define HALO_STREAM_ARB_TX8_CONFIG_1         0x4523C
+#define HALO_STREAM_ARB_RX1_CONFIG_0         0x45400
+#define HALO_STREAM_ARB_RX1_CONFIG_1         0x45404
+#define HALO_STREAM_ARB_RX2_CONFIG_0         0x45408
+#define HALO_STREAM_ARB_RX2_CONFIG_1         0x4540C
+#define HALO_STREAM_ARB_RX3_CONFIG_0         0x45410
+#define HALO_STREAM_ARB_RX3_CONFIG_1         0x45414
+#define HALO_STREAM_ARB_RX4_CONFIG_0         0x45418
+#define HALO_STREAM_ARB_RX4_CONFIG_1         0x4541C
+#define HALO_STREAM_ARB_RX5_CONFIG_0         0x45420
+#define HALO_STREAM_ARB_RX5_CONFIG_1         0x45424
+#define HALO_STREAM_ARB_RX6_CONFIG_0         0x45428
+#define HALO_STREAM_ARB_RX6_CONFIG_1         0x4542C
+#define HALO_STREAM_ARB_RX7_CONFIG_0         0x45430
+#define HALO_STREAM_ARB_RX7_CONFIG_1         0x45434
+#define HALO_STREAM_ARB_RX8_CONFIG_0         0x45438
+#define HALO_STREAM_ARB_RX8_CONFIG_1         0x4543C
+
+#define HALO_STREAM_ARB_IRQ0_CONFIG_0        0x45600
+#define HALO_STREAM_ARB_IRQ0_CONFIG_1        0x45604
+#define HALO_STREAM_ARB_IRQ0_CONFIG_2        0x45608
+#define HALO_STREAM_ARB_IRQ1_CONFIG_0        0x45610
+#define HALO_STREAM_ARB_IRQ1_CONFIG_1        0x45614
+#define HALO_STREAM_ARB_IRQ1_CONFIG_2        0x45618
+#define HALO_STREAM_ARB_IRQ2_CONFIG_0        0x45620
+#define HALO_STREAM_ARB_IRQ2_CONFIG_1        0x45624
+#define HALO_STREAM_ARB_IRQ2_CONFIG_2        0x45628
+#define HALO_STREAM_ARB_IRQ3_CONFIG_0        0x45630
+#define HALO_STREAM_ARB_IRQ3_CONFIG_1        0x45634
+#define HALO_STREAM_ARB_IRQ3_CONFIG_2        0x45638
+#define HALO_STREAM_ARB_IRQ4_CONFIG_0        0x45640
+#define HALO_STREAM_ARB_IRQ4_CONFIG_1        0x45644
+#define HALO_STREAM_ARB_IRQ4_CONFIG_2        0x45648
+#define HALO_STREAM_ARB_IRQ5_CONFIG_0        0x45650
+#define HALO_STREAM_ARB_IRQ5_CONFIG_1        0x45654
+#define HALO_STREAM_ARB_IRQ5_CONFIG_2        0x45658
+#define HALO_STREAM_ARB_IRQ6_CONFIG_0        0x45660
+#define HALO_STREAM_ARB_IRQ6_CONFIG_1        0x45664
+#define HALO_STREAM_ARB_IRQ6_CONFIG_2        0x45668
+#define HALO_STREAM_ARB_IRQ7_CONFIG_0        0x45670
+#define HALO_STREAM_ARB_IRQ7_CONFIG_1        0x45674
+#define HALO_STREAM_ARB_IRQ7_CONFIG_2        0x45678
+
+#define HALO_INTP_CTL_NMI_CONTROL            0x46008
+
+/*
+ * HALO_AHBM_WINDOW_DEBUG_1
+ */
+#define HALO_AHBM_CORE_ERR_ADDR_MASK         0x0fffff00
+#define HALO_AHBM_CORE_ERR_ADDR_SHIFT                 8
+#define HALO_AHBM_ADDR_ERR_MASK              0x00000080
+#define HALO_AHBM_LOCKED_ERR_MASK            0x00000040
+#define HALO_AHBM_SIZE_ERR_MASK              0x00000020
+#define HALO_AHBM_MODE_ERR_MASK              0x00000010
+#define HALO_AHBM_AHB_ERR_MASK               0x00000001
+
+/*
+ * HALO_SAMPLE_RATE_[RX|TX]n
+ */
+#define HALO_DSP_RATE_SHIFT                  0
+#define HALO_DSP_RATE_MASK                   0x1f
+
+/*
+ * HALO_CCM_CORE_CONTROL
+ */
+#define HALO_CORE_EN                        0x00000001
+#define HALO_CORE_EN_MASK                   0x00000001
+#define HALO_CORE_EN_SHIFT                  0
+#define HALO_CORE_EN_WIDTH                  1
+#define HALO_CORE_RESET                      0x00000200
+
+/*
+ * HALO_MPU_?M_VIO_STATUS
+ */
+#define HALO_MPU_VIO_STS_MASK               0x007e0000
+#define HALO_MPU_VIO_STS_SHIFT                      17
+#define HALO_MPU_VIO_ERR_MASK               0x00010000
+#define HALO_MPU_VIO_ERR_SHIFT                      16
+#define HALO_MPU_VIO_ERR_WR_MASK            0x00008000
+#define HALO_MPU_VIO_ERR_WR_SHIFT                   15
+#define HALO_MPU_VIO_ERR_SRC_MASK           0x00007fff
+#define HALO_MPU_VIO_ERR_SRC_SHIFT                   0
+
+#define HALO_MPU_VIO_SRAM                   0x01
+#define HALO_MPU_VIO_REG                    0x02
+#define HALO_MPU_VIO_AHB                    0x04
+#define HALO_MPU_VIO_EREG                   0x08
+#define HALO_MPU_VIO_EXTERNAL_MEM           0x10
+#define HALO_MPU_VIO_NON_EXIST              0x20
+
+/*
+ * HALO_STREAM_ARB_MSTRn_CONFIG_0
+ */
+#define HALO_STREAM_ARB_MSTR_EN_MASK         0x1
+
+/*
+ * HALO_STREAM_ARB_[TX|RX]n_CONFIG_0
+ * HALO_STREAM_ARB_IRQn_CONFIG_0
+ */
+#define HALO_STREAM_ARB_MSTR_SEL_DEFAULT     0xfc
+
+static const unsigned int halo_mpu_access[18] = {
+       HALO_MPU_WINDOW_ACCESS_0,
+       HALO_MPU_XREG_ACCESS_0,
+       HALO_MPU_YREG_ACCESS_0,
+       HALO_MPU_XMEM_ACCESS_1,
+       HALO_MPU_YMEM_ACCESS_1,
+       HALO_MPU_WINDOW_ACCESS_1,
+       HALO_MPU_XREG_ACCESS_1,
+       HALO_MPU_YREG_ACCESS_1,
+       HALO_MPU_XMEM_ACCESS_2,
+       HALO_MPU_YMEM_ACCESS_2,
+       HALO_MPU_WINDOW_ACCESS_2,
+       HALO_MPU_XREG_ACCESS_2,
+       HALO_MPU_YREG_ACCESS_2,
+       HALO_MPU_XMEM_ACCESS_3,
+       HALO_MPU_YMEM_ACCESS_3,
+       HALO_MPU_WINDOW_ACCESS_3,
+       HALO_MPU_XREG_ACCESS_3,
+       HALO_MPU_YREG_ACCESS_3,
+};
+
 struct wm_adsp_buf {
        struct list_head list;
        void *buf;
+       size_t len;
 };
 
 static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
@@ -231,14 +446,15 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
 
        if (buf == NULL)
                return NULL;
-
-       buf->buf = vmalloc(len);
+       if (src != NULL)
+               buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
+       else
+               buf->buf = kzalloc(len, GFP_KERNEL | GFP_DMA);
        if (!buf->buf) {
                kfree(buf);
                return NULL;
        }
-       memcpy(buf->buf, src, len);
-
+       buf->len = len;
        if (list)
                list_add_tail(&buf->list, list);
 
@@ -252,7 +468,23 @@ static void wm_adsp_buf_free(struct list_head *list)
                                                           struct wm_adsp_buf,
                                                           list);
                list_del(&buf->list);
-               vfree(buf->buf);
+               kfree(buf->buf);
+               kfree(buf);
+       }
+}
+
+static void wm_adsp_buf_flash(struct list_head *list, void *dest)
+{
+       int offset = 0;
+
+       while (!list_empty(list)) {
+               struct wm_adsp_buf *buf = list_first_entry(list,
+                                                          struct wm_adsp_buf,
+                                                          list);
+               memcpy(dest + offset, buf->buf, buf->len);
+               offset += buf->len;
+               list_del(&buf->list);
+               kfree(buf->buf);
                kfree(buf);
        }
 }
@@ -298,6 +530,12 @@ struct wm_adsp_system_config_xm_hdr {
        __be32 build_job_number;
 };
 
+struct wm_halo_system_config_xm_hdr {
+       __be32 halo_heartbeat;
+       __be32 build_job_name[3];
+       __be32 build_job_number;
+};
+
 struct wm_adsp_alg_xm_struct {
        __be32 magic;
        __be32 smoothing;
@@ -343,19 +581,7 @@ struct wm_adsp_compr_buf {
        u32 irq_count;
        int read_index;
        int avail;
-};
-
-struct wm_adsp_compr {
-       struct wm_adsp *dsp;
-       struct wm_adsp_compr_buf *buf;
-
-       struct snd_compr_stream *stream;
-       struct snd_compressed_buffer size;
-
-       u32 *raw_buf;
-       unsigned int copied_total;
-
-       unsigned int sample_rate;
+       int num;
 };
 
 #define WM_ADSP_DATA_WORD_SIZE         3
@@ -366,6 +592,7 @@ struct wm_adsp_compr {
 #define WM_ADSP_MAX_FRAGMENT_SIZE      (4096 * WM_ADSP_DATA_WORD_SIZE)
 
 #define WM_ADSP_ALG_XM_STRUCT_MAGIC    0x49aec7
+#define WM_ADSP_ALG_XM2_STRUCT_MAGIC   0x2e07b0
 
 #define HOST_BUFFER_FIELD(field) \
        (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))
@@ -383,13 +610,7 @@ struct wm_adsp_buffer_region {
        unsigned int base_addr;
 };
 
-struct wm_adsp_buffer_region_def {
-       unsigned int mem_type;
-       unsigned int base_offset;
-       unsigned int size_offset;
-};
-
-static const struct wm_adsp_buffer_region_def default_regions[] = {
+static struct wm_adsp_buffer_region_def default_regions[] = {
        {
                .mem_type = WMFW_ADSP2_XM,
                .base_offset = HOST_BUFFER_FIELD(X_buf_base),
@@ -407,14 +628,7 @@ static const struct wm_adsp_buffer_region_def default_regions[] = {
        },
 };
 
-struct wm_adsp_fw_caps {
-       u32 id;
-       struct snd_codec_desc desc;
-       int num_regions;
-       const struct wm_adsp_buffer_region_def *region_defs;
-};
-
-static const struct wm_adsp_fw_caps ctrl_caps[] = {
+static struct wm_adsp_fw_caps ctrl_caps[] = {
        {
                .id = SND_AUDIOCODEC_BESPOKE,
                .desc = {
@@ -428,7 +642,7 @@ static const struct wm_adsp_fw_caps ctrl_caps[] = {
        },
 };
 
-static const struct wm_adsp_fw_caps trace_caps[] = {
+static struct wm_adsp_fw_caps trace_caps[] = {
        {
                .id = SND_AUDIOCODEC_BESPOKE,
                .desc = {
@@ -446,13 +660,7 @@ static const struct wm_adsp_fw_caps trace_caps[] = {
        },
 };
 
-static const struct {
-       const char *file;
-       int compr_direction;
-       int num_caps;
-       const struct wm_adsp_fw_caps *caps;
-       bool voice_trigger;
-} wm_adsp_fw[WM_ADSP_NUM_FW] = {
+static struct wm_adsp_fw_defs wm_adsp_fw[WM_ADSP_NUM_FW] = {
        [WM_ADSP_FW_MBC_VSS] =  { .file = "mbc-vss" },
        [WM_ADSP_FW_HIFI] =     { .file = "hifi" },
        [WM_ADSP_FW_TX] =       { .file = "tx" },
@@ -506,12 +714,18 @@ static const char *wm_adsp_mem_region_name(unsigned int type)
        switch (type) {
        case WMFW_ADSP1_PM:
                return "PM";
+       case WMFW_HALO_PM_PACKED:
+               return "PM_PACKED";
        case WMFW_ADSP1_DM:
                return "DM";
        case WMFW_ADSP2_XM:
                return "XM";
+       case WMFW_HALO_XM_PACKED:
+               return "XM_PACKED";
        case WMFW_ADSP2_YM:
                return "YM";
+       case WMFW_HALO_YM_PACKED:
+               return "YM_PACKED";
        case WMFW_ADSP1_ZM:
                return "ZM";
        default:
@@ -713,7 +927,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
 
        mutex_lock(&dsp[e->shift_l].pwr_lock);
 
-       if (dsp[e->shift_l].booted || dsp[e->shift_l].compr)
+       if (dsp[e->shift_l].booted || dsp[e->shift_l].compr[0])
                ret = -EBUSY;
        else
                dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
@@ -723,7 +937,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
-static const struct soc_enum wm_adsp_fw_enum[] = {
+static struct soc_enum wm_adsp_fw_enum[] = {
        SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
        SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
        SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
@@ -751,6 +965,11 @@ const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
 };
 EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
 
+static const struct snd_kcontrol_new wm_adsp_ao_fw_controls[] = {
+       SOC_ENUM_EXT("DSP1AO Firmware", wm_adsp_fw_enum[0],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+};
+
 static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
                                                        int type)
 {
@@ -763,24 +982,48 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
        return NULL;
 }
 
-static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
-                                         unsigned int offset)
+static unsigned int wm_adsp_region_to_reg(struct wm_adsp *dsp,
+                                        struct wm_adsp_region const *mem,
+                                        unsigned int offset)
 {
        if (WARN_ON(!mem))
                return offset;
-       switch (mem->type) {
-       case WMFW_ADSP1_PM:
-               return mem->base + (offset * 3);
-       case WMFW_ADSP1_DM:
-               return mem->base + (offset * 2);
-       case WMFW_ADSP2_XM:
-               return mem->base + (offset * 2);
-       case WMFW_ADSP2_YM:
-               return mem->base + (offset * 2);
-       case WMFW_ADSP1_ZM:
-               return mem->base + (offset * 2);
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+       case WMFW_ADSP2:
+               switch (mem->type) {
+               case WMFW_ADSP1_PM:
+                       return mem->base + (offset * 3);
+               case WMFW_ADSP1_DM:
+                       return mem->base + (offset * 2);
+               case WMFW_ADSP2_XM:
+                       return mem->base + (offset * 2);
+               case WMFW_ADSP2_YM:
+                       return mem->base + (offset * 2);
+               case WMFW_ADSP1_ZM:
+                       return mem->base + (offset * 2);
+               default:
+                       WARN(1, "Unknown memory region type");
+                       return offset;
+               }
+       case WMFW_HALO:
+               switch (mem->type) {
+               case WMFW_ADSP2_XM:
+                       return mem->base + (offset * 4);
+               case WMFW_ADSP2_YM:
+                       return mem->base + (offset * 4);
+               case WMFW_HALO_XM_PACKED:
+                       return (mem->base + (offset * 3)) & ~0x3;
+               case WMFW_HALO_YM_PACKED:
+                       return (mem->base + (offset * 3)) & ~0x3;
+               case WMFW_HALO_PM_PACKED:
+                       return mem->base + (offset * 5);
+               default:
+                       WARN(1, "Unknown memory region type");
+                       return offset;
+               }
        default:
-               WARN(1, "Unknown memory region type");
+               WARN(1, "Unknown DSP type");
                return offset;
        }
 }
@@ -830,6 +1073,25 @@ static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
                 scratch[1] >> 16);
 }
 
+static void wm_halo_show_fw_status(struct wm_adsp *dsp)
+{
+       u32 scratch[4];
+       int ret;
+
+       ret = regmap_raw_read(dsp->regmap, dsp->base + HALO_SCRATCH1,
+                             scratch, sizeof(scratch));
+       if (ret) {
+               adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
+               return;
+       }
+
+       adsp_dbg(dsp, "FW SCRATCH 1:0x%x 2:0x%x 3:0x%x 4:0x%x\n",
+                be32_to_cpu(scratch[0]),
+                be32_to_cpu(scratch[1]),
+                be32_to_cpu(scratch[2]),
+                be32_to_cpu(scratch[3]));
+}
+
 static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
 {
        return container_of(ext, struct wm_coeff_ctl, bytes_ext);
@@ -848,7 +1110,8 @@ static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
                return -EINVAL;
        }
 
-       *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset);
+       *reg = wm_adsp_region_to_reg(dsp, mem,
+                                    ctl->alg_region.base + ctl->offset);
 
        return 0;
 }
@@ -1045,31 +1308,55 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
 static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
                                 void *buf, size_t len)
 {
+       LIST_HEAD(buf_list);
        struct wm_adsp *dsp = ctl->dsp;
-       void *scratch;
+       struct wm_adsp_buf *scratch;
        int ret;
        unsigned int reg;
+       int read_len = 0;
+       size_t toread_len;
+       unsigned int addr_div;
+
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+       case WMFW_ADSP2:
+               addr_div = 2;
+               break;
+       default:
+               addr_div = 1;
+               break;
+       }
 
        ret = wm_coeff_base_reg(ctl, &reg);
        if (ret)
                return ret;
 
-       scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
-       if (!scratch)
-               return -ENOMEM;
+       while ((len - read_len) > 0) {
+               toread_len = (len - read_len) > PAGE_SIZE ?
+                       PAGE_SIZE : (len - read_len);
 
-       ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
-       if (ret) {
-               adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
-                        len, reg, ret);
-               kfree(scratch);
-               return ret;
-       }
-       adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
+               scratch = wm_adsp_buf_alloc(NULL, toread_len, &buf_list);
+               if (!scratch) {
+                       adsp_err(dsp, "Out of memory\n");
+                       wm_adsp_buf_free(&buf_list);
+                       return -ENOMEM;
+               }
 
-       memcpy(buf, scratch, len);
-       kfree(scratch);
+               regmap_raw_read(dsp->regmap, reg + read_len / addr_div,
+                               scratch->buf, toread_len);
+               if (ret) {
+                       adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
+                                toread_len,
+                                reg + read_len / addr_div, ret);
+                       wm_adsp_buf_free(&buf_list);
+                       return ret;
+               }
+               adsp_dbg(dsp, "Read %zu bytes from %x\n", toread_len,
+                        reg + read_len / addr_div);
+               read_len += toread_len;
+       }
 
+       wm_adsp_buf_flash(&buf_list, buf);
        return 0;
 }
 
@@ -1206,14 +1493,12 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
                kcontrol->put = wm_coeff_put_acked;
                break;
        default:
-               if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
-                       ctl->bytes_ext.max = ctl->len;
-                       ctl->bytes_ext.get = wm_coeff_tlv_get;
-                       ctl->bytes_ext.put = wm_coeff_tlv_put;
-               } else {
-                       kcontrol->get = wm_coeff_get;
-                       kcontrol->put = wm_coeff_put;
-               }
+               kcontrol->get = wm_coeff_get;
+               kcontrol->put = wm_coeff_put;
+
+               ctl->bytes_ext.max = ctl->len;
+               ctl->bytes_ext.get = wm_coeff_tlv_get;
+               ctl->bytes_ext.put = wm_coeff_tlv_put;
                break;
        }
 
@@ -1241,9 +1526,16 @@ static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
                if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
                        continue;
 
-               ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
-               if (ret < 0)
-                       return ret;
+               /*
+                * For readable controls populate the cache from the DSP memory.
+                * For non-readable controls the cache was zero-filled when
+                * created so we don't need to do anything.
+                */
+               if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) {
+                       ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        return 0;
@@ -1332,7 +1624,8 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
        default:
                ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
                                "DSP%d%c %.12s %x", dsp->num, *region_name,
-                               wm_adsp_fw_text[dsp->fw], alg_region->alg);
+                               dsp->fw_enum.texts[dsp->fw],
+                               alg_region->alg);
 
                /* Truncate the subname from the start if it is too long */
                if (subname) {
@@ -1360,7 +1653,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
        ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
        if (!ctl)
                return -ENOMEM;
-       ctl->fw_name = wm_adsp_fw_text[dsp->fw];
+       ctl->fw_name = dsp->fw_enum.texts[dsp->fw];
        ctl->alg_region = *alg_region;
        ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
        if (!ctl->name) {
@@ -1419,6 +1712,44 @@ struct wm_coeff_parsed_alg {
        int ncoeff;
 };
 
+int wm_adsp_handle_fw_event(struct wm_adsp *dsp)
+{
+       struct wm_coeff_ctl *ctl;
+       int ret = 0, serviced = 0;
+       u32 val = 0;
+
+       list_for_each_entry(ctl, &dsp->ctl_list, list) {
+               if (ctl->type != WMFW_CTL_TYPE_FWEVENT)
+                       continue;
+               if (!dsp->fwevent_cb) {
+                       adsp_err(dsp,
+                                "FW event callback not registered: %s\n",
+                                ctl->name);
+                       return -EINVAL;
+               }
+
+               ret = wm_coeff_read_control(ctl, &val, ctl->len);
+               if (ret < 0)
+                       return ret;
+
+               val = be32_to_cpu(val);
+               if (val == 0xFFFFFFu)
+                       continue;
+
+               dsp->fwevent_cb(dsp, val);
+
+               val = cpu_to_be32(0xFFFFFFu);
+               ret = wm_coeff_write_control(ctl, &val, ctl->len);
+               if (ret < 0)
+                       return ret;
+               serviced++;
+
+       }
+
+       return serviced;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_handle_fw_event);
+
 struct wm_coeff_parsed_coeff {
        int offset;
        int mem_type;
@@ -1592,6 +1923,7 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
                                return -EINVAL;
                        break;
                case WMFW_CTL_TYPE_HOSTEVENT:
+               case WMFW_CTL_TYPE_FWEVENT:
                        ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
                                                WMFW_CTL_FLAG_SYS |
                                                WMFW_CTL_FLAG_VOLATILE |
@@ -1625,6 +1957,55 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
        return 0;
 }
 
+static int wm_adsp_write_blocks(struct wm_adsp *dsp, const u8 *data, size_t len,
+                               unsigned int reg, struct list_head *list,
+                               size_t burst_multiple)
+
+{
+       size_t to_write = PAGE_SIZE - (PAGE_SIZE % burst_multiple);
+       size_t remain = len;
+       struct wm_adsp_buf *buf;
+       unsigned int addr_div;
+       int ret;
+
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+       case WMFW_ADSP2:
+               addr_div = 2;
+               break;
+       default:
+               addr_div = 1;
+               break;
+       }
+
+       while (remain > 0) {
+               if (remain < to_write)
+                       to_write = remain;
+
+               buf = wm_adsp_buf_alloc(data, to_write, list);
+               if (!buf) {
+                       adsp_err(dsp, "Out of memory\n");
+                       return -ENOMEM;
+               }
+
+               ret = regmap_raw_write_async(dsp->regmap, reg,
+                                            buf->buf, to_write);
+               if (ret != 0) {
+                       adsp_err(dsp,
+                                "Failed to write %zd bytes at %d\n",
+                                to_write, reg);
+
+                       return ret;
+               }
+
+               data += to_write;
+               reg += to_write / addr_div;
+               remain -= to_write;
+       }
+
+       return 0;
+}
+
 static int wm_adsp_load(struct wm_adsp *dsp)
 {
        LIST_HEAD(buf_list);
@@ -1639,17 +2020,20 @@ static int wm_adsp_load(struct wm_adsp *dsp)
        const struct wm_adsp_region *mem;
        const char *region_name;
        char *file, *text = NULL;
-       struct wm_adsp_buf *buf;
        unsigned int reg;
        int regions = 0;
        int ret, offset, type, sizes;
+       unsigned int burst_multiple;
 
        file = kzalloc(PAGE_SIZE, GFP_KERNEL);
        if (file == NULL)
                return -ENOMEM;
 
-       snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
-                wm_adsp_fw[dsp->fw].file);
+       if (dsp->firmwares[dsp->fw].fullname)
+               snprintf(file, PAGE_SIZE, "%s", dsp->firmwares[dsp->fw].file);
+       else
+               snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part,
+                        dsp->num, dsp->firmwares[dsp->fw].file);
        file[PAGE_SIZE - 1] = '\0';
 
        ret = request_firmware(&firmware, file, dsp->dev);
@@ -1673,18 +2057,42 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                goto out_fw;
        }
 
-       switch (header->ver) {
-       case 0:
-               adsp_warn(dsp, "%s: Depreciated file format %d\n",
-                         file, header->ver);
-               break;
-       case 1:
-       case 2:
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+       case WMFW_ADSP2:
+               switch (header->ver) {
+               case 0:
+                       adsp_warn(dsp, "%s: Deprecated file format %d\n",
+                                 file, header->ver);
+                       break;
+               case 1:
+               case 2:
+                       break;
+               default:
+                       adsp_err(dsp, "%s: unknown file format %d\n",
+                                file, header->ver);
+                       goto out_fw;
+               }
                break;
-       default:
-               adsp_err(dsp, "%s: unknown file format %d\n",
-                        file, header->ver);
-               goto out_fw;
+       case WMFW_HALO:
+               switch (header->ver) {
+               case 1:
+               case 2:
+                       /*
+                        * we are required to load these for testing purposes
+                        * but this format is not allowed for production fw
+                        */
+                       adsp_warn(dsp,
+                                 "%s: Not a production firmware (deprecated file format %d)\n",
+                                 file, header->ver);
+                       break;
+               case 3:
+                       break;
+               default:
+                       adsp_err(dsp, "%s: unknown file format %d\n",
+                                file, header->ver);
+                       goto out_fw;
+               }
        }
 
        adsp_info(dsp, "Firmware version: %d\n", header->ver);
@@ -1709,6 +2117,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                         le32_to_cpu(adsp1_sizes->zm));
                break;
 
+       case WMFW_HALO:
        case WMFW_ADSP2:
                pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
                adsp2_sizes = (void *)&(header[1]);
@@ -1742,6 +2151,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                region = (void *)&(firmware->data[pos]);
                region_name = "Unknown";
                reg = 0;
+               burst_multiple = 4;
                text = NULL;
                offset = le32_to_cpu(region->offset) & 0xffffff;
                type = be32_to_cpu(region->type) & 0xff;
@@ -1773,8 +2183,11 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                case WMFW_ADSP2_XM:
                case WMFW_ADSP2_YM:
                case WMFW_ADSP1_ZM:
+               case WMFW_HALO_PM_PACKED:
+               case WMFW_HALO_XM_PACKED:
+               case WMFW_HALO_YM_PACKED:
                        region_name = wm_adsp_mem_region_name(type);
-                       reg = wm_adsp_region_to_reg(mem, offset);
+                       reg = wm_adsp_region_to_reg(dsp, mem, offset);
                        break;
                default:
                        adsp_warn(dsp,
@@ -1805,23 +2218,16 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                }
 
                if (reg) {
-                       buf = wm_adsp_buf_alloc(region->data,
-                                               le32_to_cpu(region->len),
-                                               &buf_list);
-                       if (!buf) {
-                               adsp_err(dsp, "Out of memory\n");
-                               ret = -ENOMEM;
-                               goto out_fw;
-                       }
+                       ret = wm_adsp_write_blocks(dsp, region->data,
+                                                  le32_to_cpu(region->len),
+                                                  reg, &buf_list,
+                                                  burst_multiple);
 
-                       ret = regmap_raw_write_async(regmap, reg, buf->buf,
-                                                    le32_to_cpu(region->len));
                        if (ret != 0) {
                                adsp_err(dsp,
-                                       "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+                                       "%s.%d: Failed writing data at %d in %s: %d\n",
                                        file, regions,
-                                       le32_to_cpu(region->len), offset,
-                                       region_name, ret);
+                                       offset, region_name, ret);
                                goto out_fw;
                        }
                }
@@ -1859,7 +2265,11 @@ static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
        struct wm_coeff_ctl *ctl;
 
        list_for_each_entry(ctl, &dsp->ctl_list, list) {
-               if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
+               const char *fw_name;
+
+               fw_name = dsp->fw_enum.texts[dsp->fw];
+
+               if (ctl->fw_name == fw_name &&
                    alg_region->alg == ctl->alg_region.alg &&
                    alg_region->type == ctl->alg_region.type) {
                        ctl->alg_region.base = alg_region->base;
@@ -2088,6 +2498,7 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
        n_algs = be32_to_cpu(adsp2_id.n_algs);
        dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
        dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
+       dsp->fw_vendor_id = 0;
        adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
                  dsp->fw_id,
                  (dsp->fw_id_version & 0xff0000) >> 16,
@@ -2105,13 +2516,21 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
        if (IS_ERR(alg_region))
                return PTR_ERR(alg_region);
 
-       alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
-                                          adsp2_id.fw.id, adsp2_id.zm);
-       if (IS_ERR(alg_region))
-               return PTR_ERR(alg_region);
+       switch (dsp->type) {
+       case WMFW_HALO:
+               pos = sizeof(adsp2_id);
+               len = sizeof(*adsp2_alg) * n_algs;
+               break;
+       default:
+               alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
+                                                  adsp2_id.fw.id, adsp2_id.zm);
+               if (IS_ERR(alg_region))
+                       return PTR_ERR(alg_region);
 
-       pos = sizeof(adsp2_id) / 2;
-       len = (sizeof(*adsp2_alg) * n_algs) / 2;
+               pos = sizeof(adsp2_id) / 2;
+               len = (sizeof(*adsp2_alg) * n_algs) / 2;
+               break;
+       }
 
        adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
        if (IS_ERR(adsp2_alg))
@@ -2170,6 +2589,10 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
                        }
                }
 
+               /* no ZM on HALO */
+               if (dsp->type == WMFW_HALO)
+                       continue;
+
                alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
                                                   adsp2_alg[i].alg.id,
                                                   adsp2_alg[i].zm);
@@ -2197,40 +2620,170 @@ out:
        return ret;
 }
 
-static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+static int wm_halo_setup_algs(struct wm_adsp *dsp)
 {
-       LIST_HEAD(buf_list);
-       struct regmap *regmap = dsp->regmap;
-       struct wmfw_coeff_hdr *hdr;
-       struct wmfw_coeff_item *blk;
-       const struct firmware *firmware;
-       const struct wm_adsp_region *mem;
+       struct wmfw_halo_id_hdr halo_id;
+       struct wmfw_halo_alg_hdr *halo_alg;
        struct wm_adsp_alg_region *alg_region;
-       const char *region_name;
-       int ret, pos, blocks, type, offset, reg;
-       char *file;
-       struct wm_adsp_buf *buf;
-
-       file = kzalloc(PAGE_SIZE, GFP_KERNEL);
-       if (file == NULL)
-               return -ENOMEM;
+       const struct wm_adsp_region *mem;
+       unsigned int pos, len, block_rev;
+       size_t n_algs;
+       int i, ret;
 
-       snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
-                wm_adsp_fw[dsp->fw].file);
-       file[PAGE_SIZE - 1] = '\0';
+       mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+       if (WARN_ON(!mem))
+               return -EINVAL;
 
-       ret = request_firmware(&firmware, file, dsp->dev);
+       ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id,
+                             sizeof(halo_id));
        if (ret != 0) {
-               adsp_warn(dsp, "Failed to request '%s'\n", file);
-               ret = 0;
-               goto out;
-       }
-       ret = -EINVAL;
-
-       if (sizeof(*hdr) >= firmware->size) {
-               adsp_err(dsp, "%s: file too short, %zu bytes\n",
-                       file, firmware->size);
-               goto out_fw;
+               adsp_err(dsp, "Failed to read algorithm info: %d\n",
+                        ret);
+               return ret;
+       }
+
+       block_rev = be32_to_cpu(halo_id.fw.block_rev) >> 16;
+       switch (block_rev) {
+       case 3:
+               break;
+       default:
+               adsp_err(dsp, "Unknown firmware ID block version 0x%x\n",
+                        block_rev);
+               return -EINVAL;
+       }
+
+       n_algs = be32_to_cpu(halo_id.n_algs);
+       dsp->fw_id = be32_to_cpu(halo_id.fw.id);
+       dsp->fw_id_version = be32_to_cpu(halo_id.fw.ver);
+       dsp->fw_vendor_id = be32_to_cpu(halo_id.fw.vendor_id);
+       adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %zu algorithms\n",
+                 dsp->fw_id,
+                 dsp->fw_vendor_id,
+                 (dsp->fw_id_version & 0xff0000) >> 16,
+                 (dsp->fw_id_version & 0xff00) >> 8,
+                 dsp->fw_id_version & 0xff,
+                 n_algs);
+
+       alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
+                                          halo_id.fw.id, halo_id.xm_base);
+       if (IS_ERR(alg_region))
+               return PTR_ERR(alg_region);
+
+       alg_region = wm_adsp_create_region(dsp, WMFW_HALO_XM_PACKED,
+                                          halo_id.fw.id, halo_id.xm_base);
+       if (IS_ERR(alg_region))
+               return PTR_ERR(alg_region);
+
+       alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
+                                          halo_id.fw.id, halo_id.ym_base);
+       if (IS_ERR(alg_region))
+               return PTR_ERR(alg_region);
+
+       alg_region = wm_adsp_create_region(dsp, WMFW_HALO_YM_PACKED,
+                                          halo_id.fw.id, halo_id.ym_base);
+       if (IS_ERR(alg_region))
+               return PTR_ERR(alg_region);
+
+       pos = sizeof(halo_id);
+       len = (sizeof(*halo_alg) * n_algs);
+
+       halo_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
+       if (IS_ERR(halo_alg))
+               return PTR_ERR(halo_alg);
+
+       for (i = 0; i < n_algs; i++) {
+               adsp_info(dsp,
+                         "%d: ID %x v%d.%d.%d XM@%x YM@%x\n",
+                         i, be32_to_cpu(halo_alg[i].alg.id),
+                         (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16,
+                         (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8,
+                         be32_to_cpu(halo_alg[i].alg.ver) & 0xff,
+                         be32_to_cpu(halo_alg[i].xm_base),
+                         be32_to_cpu(halo_alg[i].ym_base));
+
+               alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
+                                                  halo_alg[i].alg.id,
+                                                  halo_alg[i].xm_base);
+               if (IS_ERR(alg_region)) {
+                       ret = PTR_ERR(alg_region);
+                       goto out;
+               }
+
+               alg_region = wm_adsp_create_region(dsp, WMFW_HALO_XM_PACKED,
+                                                  halo_alg[i].alg.id,
+                                                  halo_alg[i].xm_base);
+               if (IS_ERR(alg_region)) {
+                       ret = PTR_ERR(alg_region);
+                       goto out;
+               }
+
+               alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
+                                                  halo_alg[i].alg.id,
+                                                  halo_alg[i].ym_base);
+               if (IS_ERR(alg_region)) {
+                       ret = PTR_ERR(alg_region);
+                       goto out;
+               }
+
+               alg_region = wm_adsp_create_region(dsp, WMFW_HALO_YM_PACKED,
+                                                  halo_alg[i].alg.id,
+                                                  halo_alg[i].ym_base);
+               if (IS_ERR(alg_region)) {
+                       ret = PTR_ERR(alg_region);
+                       goto out;
+               }
+       }
+
+out:
+       kfree(halo_alg);
+       return ret;
+}
+
+static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+{
+       LIST_HEAD(buf_list);
+       struct regmap *regmap = dsp->regmap;
+       struct wmfw_coeff_hdr *hdr;
+       struct wmfw_coeff_item *blk;
+       const struct firmware *firmware;
+       const struct wm_adsp_region *mem;
+       struct wm_adsp_alg_region *alg_region;
+       const char *region_name;
+       int ret, pos, blocks, type, offset, reg;
+       char *file;
+       unsigned int burst_multiple;
+
+       if (dsp->firmwares[dsp->fw].binfile &&
+           !(strcmp(dsp->firmwares[dsp->fw].binfile, "None")))
+               return 0;
+
+       file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (file == NULL)
+               return -ENOMEM;
+
+       if (dsp->firmwares[dsp->fw].fullname && dsp->firmwares[dsp->fw].binfile)
+               snprintf(file, PAGE_SIZE, "%s",
+                        dsp->firmwares[dsp->fw].binfile);
+       else if (dsp->firmwares[dsp->fw].binfile)
+               snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part,
+                        dsp->num, dsp->firmwares[dsp->fw].binfile);
+       else
+               snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part,
+                        dsp->num, dsp->firmwares[dsp->fw].file);
+       file[PAGE_SIZE - 1] = '\0';
+
+       ret = request_firmware(&firmware, file, dsp->dev);
+       if (ret != 0) {
+               adsp_warn(dsp, "Failed to request '%s'\n", file);
+               ret = 0;
+               goto out;
+       }
+       ret = -EINVAL;
+
+       if (sizeof(*hdr) >= firmware->size) {
+               adsp_err(dsp, "%s: file too short, %zu bytes\n",
+                       file, firmware->size);
+               goto out_fw;
        }
 
        hdr = (void *)&firmware->data[0];
@@ -2273,6 +2826,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                         file, blocks, le32_to_cpu(blk->len), offset, type);
 
                reg = 0;
+               burst_multiple = 4;
                region_name = "Unknown";
                switch (type) {
                case (WMFW_NAME_TEXT << 8):
@@ -2291,7 +2845,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                                        adsp_err(dsp, "No ZM\n");
                                        break;
                                }
-                               reg = wm_adsp_region_to_reg(mem, 0);
+                               reg = wm_adsp_region_to_reg(dsp, mem, 0);
 
                        } else {
                                region_name = "register";
@@ -2303,6 +2857,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                case WMFW_ADSP1_ZM:
                case WMFW_ADSP2_XM:
                case WMFW_ADSP2_YM:
+               case WMFW_HALO_XM_PACKED:
+               case WMFW_HALO_YM_PACKED:
+               case WMFW_HALO_PM_PACKED:
                        adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
                                 file, blocks, le32_to_cpu(blk->len),
                                 type, le32_to_cpu(blk->id));
@@ -2317,7 +2874,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                                                le32_to_cpu(blk->id));
                        if (alg_region) {
                                reg = alg_region->base;
-                               reg = wm_adsp_region_to_reg(mem, reg);
+                               reg = wm_adsp_region_to_reg(dsp, mem, reg);
                                reg += offset;
                        } else {
                                adsp_err(dsp, "No %x for algorithm %x\n",
@@ -2343,20 +2900,14 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                                goto out_fw;
                        }
 
-                       buf = wm_adsp_buf_alloc(blk->data,
-                                               le32_to_cpu(blk->len),
-                                               &buf_list);
-                       if (!buf) {
-                               adsp_err(dsp, "Out of memory\n");
-                               ret = -ENOMEM;
-                               goto out_fw;
-                       }
-
                        adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
                                 file, blocks, le32_to_cpu(blk->len),
                                 reg);
-                       ret = regmap_raw_write_async(regmap, reg, buf->buf,
-                                                    le32_to_cpu(blk->len));
+
+                       ret = wm_adsp_write_blocks(dsp, blk->data,
+                                                  le32_to_cpu(blk->len),
+                                                  reg, &buf_list,
+                                                  burst_multiple);
                        if (ret != 0) {
                                adsp_err(dsp,
                                        "%s.%d: Failed to write to %x in %s: %d\n",
@@ -2512,6 +3063,17 @@ err_mutex:
 }
 EXPORT_SYMBOL_GPL(wm_adsp1_event);
 
+int wm_adsp_compr_channel(struct wm_adsp_compr *compr)
+{
+       int i;
+       struct wm_adsp *dsp = compr->dsp;
+
+       for (i = 0; i < WM_ADSP_MAX_CHANNEL_PER_DSP; i++)
+               if (dsp->compr[i] == compr)
+                       return i;
+       return 0;
+}
+
 static int wm_adsp2_ena(struct wm_adsp *dsp)
 {
        unsigned int val;
@@ -2614,6 +3176,251 @@ err_mutex:
        mutex_unlock(&dsp->pwr_lock);
 }
 
+static int wm_halo_set_rate_block(struct wm_adsp *dsp,
+                                 unsigned int rate_base,
+                                 unsigned int n_rates,
+                                 const u8 *rate_cache)
+{
+       unsigned int addr = dsp->base + rate_base, val;
+       int ret, i;
+
+       mutex_lock(dsp->rate_lock);
+
+       for (i = 0; i < n_rates; ++i) {
+               val = rate_cache[i] << HALO_DSP_RATE_SHIFT;
+
+               ret = regmap_update_bits(dsp->regmap,
+                                        addr + (i * 8),
+                                        HALO_DSP_RATE_MASK,
+                                        val);
+               if (ret) {
+                       adsp_err(dsp, "Failed to set rate: %d\n", ret);
+                       mutex_unlock(dsp->rate_lock);
+                       return ret;
+               }
+
+               adsp_dbg(dsp, "Set rate %d to 0x%x\n", i, val);
+       }
+
+       udelay(300);
+
+       mutex_unlock(dsp->rate_lock);
+
+       return 0;
+}
+
+static int wm_halo_clear_stream_arb(struct wm_adsp *dsp)
+{
+       struct regmap *regmap = dsp->regmap;
+       unsigned int dspbase = dsp->base, reg, begin, end;
+       u32 values[3] = {0, 0, 0};
+       int ret;
+
+       /* disable stream arbiter masters */
+       for (reg = dspbase + HALO_STREAM_ARB_MSTR0_CONFIG_0;
+            reg <= dspbase + HALO_STREAM_ARB_MSTR5_CONFIG_0;
+            reg += 0x10) {
+               ret = regmap_update_bits(regmap, reg,
+                                        HALO_STREAM_ARB_MSTR_EN_MASK, 0);
+               if (ret)
+                       goto error;
+       }
+
+       /* clear stream arbiter masters */
+       for (reg = dspbase + HALO_STREAM_ARB_MSTR0_CONFIG_0;
+            reg <= dspbase + HALO_STREAM_ARB_MSTR5_CONFIG_0;
+            reg += 0x10) {
+               ret = regmap_bulk_write(regmap, reg, values, 3);
+               if (ret)
+                       goto error;
+       }
+
+       /* clear stream arbiter channel configs */
+       begin = dspbase + HALO_STREAM_ARB_TX1_CONFIG_0;
+       end = begin + dsp->n_tx_channels * 0x8;
+       for (reg = begin; reg < end; reg += 0x8) {
+               ret = regmap_write(regmap, reg,
+                                  HALO_STREAM_ARB_MSTR_SEL_DEFAULT);
+               if (ret)
+                       goto error;
+       }
+       begin = dspbase + HALO_STREAM_ARB_RX1_CONFIG_0;
+       end = begin + dsp->n_rx_channels * 0x8;
+       for (reg = begin; reg < end; reg += 0x8) {
+               ret = regmap_write(regmap, reg,
+                                  HALO_STREAM_ARB_MSTR_SEL_DEFAULT);
+               if (ret)
+                       goto error;
+       }
+
+       /* clear stream arbiter interrupt registers */
+       values[0] = HALO_STREAM_ARB_MSTR_SEL_DEFAULT;
+       for (reg = dspbase + HALO_STREAM_ARB_IRQ0_CONFIG_0;
+            reg <= dspbase + HALO_STREAM_ARB_IRQ7_CONFIG_1;
+            reg += 0x10) {
+               ret = regmap_bulk_write(regmap, reg, values, 2);
+               if (ret)
+                       goto error;
+       }
+       return 0;
+
+error:
+       adsp_err(dsp,
+                "Error while clearing stream arbiter config (reg 0x%x): %d\n",
+                reg, ret);
+       return ret;
+}
+
+static int wm_halo_configure_mpu(struct wm_adsp *dsp)
+{
+       struct regmap *regmap = dsp->regmap;
+       int i = 0, len = 0, ret;
+       unsigned int sysinfo_base = dsp->base_sysinfo, dsp_base = dsp->base;
+       unsigned int xm_sz, xm_bank_sz, ym_sz, ym_bank_sz;
+       unsigned int xm_acc_cfg, ym_acc_cfg;
+       unsigned int lock_cfg;
+
+       ret = regmap_read(regmap, sysinfo_base + HALO_SYS_INFO_XM_BANK_SIZE,
+                         &xm_bank_sz);
+       if (ret) {
+               adsp_err(dsp, "Failed to read XM bank size.\n");
+               goto err;
+       }
+
+       if (!xm_bank_sz) {
+               adsp_err(dsp, "Failed to configure MPU (XM_BANK_SIZE = 0)\n");
+               goto err;
+       }
+
+       ret = regmap_read(regmap, sysinfo_base + HALO_SYS_INFO_YM_BANK_SIZE,
+                         &ym_bank_sz);
+       if (ret) {
+               adsp_err(dsp, "Failed to read YM bank size.\n");
+               goto err;
+       }
+
+       if (!ym_bank_sz) {
+               adsp_err(dsp, "Failed to configure MPU (YM_BANK_SIZE = 0)\n");
+               goto err;
+       }
+
+       ret = regmap_read(regmap, sysinfo_base + HALO_SYS_INFO_XM_SRAM_SIZE,
+                         &xm_sz);
+       if (ret) {
+               adsp_err(dsp, "Failed to read XM size.\n");
+               goto err;
+       }
+
+       ret = regmap_read(regmap, sysinfo_base + HALO_SYS_INFO_YM_SRAM_SIZE,
+                         &ym_sz);
+       if (ret) {
+               adsp_err(dsp, "Failed to read YM size.\n");
+               goto err;
+       }
+
+       adsp_dbg(dsp,
+                "XM size 0x%x XM bank size 0x%x YM size 0x%x YM bank size 0x%x\n",
+                xm_sz, xm_bank_sz, ym_sz, ym_bank_sz);
+
+       /* calculate amount of banks to unlock */
+       xm_acc_cfg = (1 << (xm_sz / xm_bank_sz)) - 1;
+       ym_acc_cfg = (1 << (ym_sz / ym_bank_sz)) - 1;
+
+       /* unlock MPU */
+       ret = regmap_write(regmap, dsp_base + HALO_MPU_LOCK_CONFIG,
+                          HALO_MPU_UNLOCK_CODE_0);
+       if (ret) {
+               adsp_err(dsp, "Error while unlocking MPU: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(regmap, dsp_base + HALO_MPU_LOCK_CONFIG,
+                          HALO_MPU_UNLOCK_CODE_1);
+       if (ret) {
+               adsp_err(dsp, "Error while unlocking MPU: %d\n", ret);
+               goto err;
+       }
+
+       adsp_dbg(dsp, "Unlocking XM (cfg: %x) and YM (cfg: %x)",
+                xm_acc_cfg, ym_acc_cfg);
+
+       /* unlock XMEM and YMEM */
+       ret = regmap_write(regmap, dsp_base + HALO_MPU_XMEM_ACCESS_0,
+                          xm_acc_cfg);
+       if (ret)
+               goto err;
+
+       ret = regmap_write(regmap, dsp_base + HALO_MPU_YMEM_ACCESS_0,
+                          ym_acc_cfg);
+       if (ret)
+               goto err;
+
+       len = sizeof(halo_mpu_access) / sizeof(halo_mpu_access[0]);
+       /* configure all other banks */
+       lock_cfg = (dsp->unlock_all) ? 0xFFFFFFFF : 0;
+       for (i = 0; i < len; i++) { /* TODO: think if can be done without LUT */
+               ret = regmap_write(regmap, dsp_base + halo_mpu_access[i],
+                                       lock_cfg);
+               if (ret)
+                       goto err;
+       }
+
+       /* lock MPU */
+       ret = regmap_write(regmap, dsp_base + HALO_MPU_LOCK_CONFIG, 0);
+       if (ret)
+               adsp_err(dsp, "Error while locking MPU: %d\n", ret);
+
+err:
+       return ret;
+}
+
+static void wm_halo_boot_work(struct work_struct *work)
+{
+       struct wm_adsp *dsp = container_of(work,
+                                          struct wm_adsp,
+                                          boot_work);
+       int ret;
+
+       mutex_lock(&dsp->pwr_lock);
+
+       ret = wm_adsp_load(dsp);
+       if (ret != 0)
+               goto err;
+
+       switch (dsp->fw_ver) {
+       case 1:
+       case 2:
+               ret = wm_adsp2_setup_algs(dsp);
+               if (ret != 0)
+                       goto err;
+               break;
+       default:
+               ret = wm_halo_setup_algs(dsp);
+               if (ret != 0)
+                       goto err;
+               break;
+       }
+
+       ret = wm_adsp_load_coeff(dsp);
+       if (ret != 0)
+               goto err;
+
+       /* Initialize caches for enabled and unset controls */
+       ret = wm_coeff_init_control_caches(dsp);
+       if (ret != 0)
+               goto err;
+
+       /* Sync set controls */
+       ret = wm_coeff_sync_controls(dsp);
+       if (ret != 0)
+               goto err;
+
+       dsp->booted = true;
+
+err:
+       mutex_unlock(&dsp->pwr_lock);
+}
+
 static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
 {
        int ret;
@@ -2657,7 +3464,12 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        char preload[32];
 
-       snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift);
+       if (codec->component.name_prefix)
+               snprintf(preload, ARRAY_SIZE(preload), "%s DSP%u%s Preload",
+                       codec->component.name_prefix, mc->shift, dsp->suffix);
+       else
+               snprintf(preload, ARRAY_SIZE(preload), "DSP%u%s Preload",
+                       mc->shift, dsp->suffix);
 
        dsp->preloaded = ucontrol->value.integer.value[0];
 
@@ -2726,10 +3538,167 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
+EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
+
+int wm_halo_early_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+       struct wm_adsp *dsp = &dsps[w->shift];
+       struct wm_coeff_ctl *ctl;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               queue_work(system_unbound_wq, &dsp->boot_work);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               mutex_lock(&dsp->pwr_lock);
+
+               wm_adsp_debugfs_clear(dsp);
+
+               dsp->fw_id = 0;
+               dsp->fw_id_version = 0;
+
+               dsp->booted = false;
+
+               list_for_each_entry(ctl, &dsp->ctl_list, list)
+                       ctl->enabled = 0;
+
+               wm_adsp_free_alg_regions(dsp);
+
+               mutex_unlock(&dsp->pwr_lock);
+
+               adsp_dbg(dsp, "Shutdown complete\n");
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_halo_early_event);
+
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+       struct wm_adsp *dsp = &dsps[w->shift];
+       int ret;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               flush_work(&dsp->boot_work);
+
+               mutex_lock(&dsp->pwr_lock);
+
+               if (!dsp->booted) {
+                       ret = -EIO;
+                       goto err;
+               }
+
+               ret = wm_adsp2_ena(dsp);
+               if (ret != 0)
+                       goto err;
+
+               /* Sync set controls */
+               ret = wm_coeff_sync_controls(dsp);
+               if (ret != 0)
+                       goto err;
+
+               wm_adsp2_lock(dsp, dsp->lock_regions);
+
+               ret = regmap_update_bits(dsp->regmap,
+                                        dsp->base + ADSP2_CONTROL,
+                                        ADSP2_CORE_ENA | ADSP2_START,
+                                        ADSP2_CORE_ENA | ADSP2_START);
+               if (ret != 0)
+                       goto err;
+
+               if (dsp->firmwares[dsp->fw].num_caps != 0) {
+                       ret = wm_adsp_buffer_init(dsp);
+                       if (ret < 0)
+                               goto err;
+               }
+
+               dsp->running = true;
+
+               mutex_unlock(&dsp->pwr_lock);
+
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               /* Tell the firmware to cleanup */
+               wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
+
+               wm_adsp_stop_watchdog(dsp);
+
+               /* Log firmware state, it can be useful for analysis */
+               switch (dsp->rev) {
+               case 0:
+                       wm_adsp2_show_fw_status(dsp);
+                       break;
+               default:
+                       wm_adsp2v2_show_fw_status(dsp);
+                       break;
+               }
+
+               mutex_lock(&dsp->pwr_lock);
+
+               dsp->running = false;
+
+               regmap_update_bits(dsp->regmap,
+                                  dsp->base + ADSP2_CONTROL,
+                                  ADSP2_CORE_ENA | ADSP2_START, 0);
+
+               /* Make sure DMAs are quiesced */
+               switch (dsp->rev) {
+               case 0:
+                       regmap_write(dsp->regmap,
+                                    dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+                       regmap_write(dsp->regmap,
+                                    dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+                       regmap_write(dsp->regmap,
+                                    dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+
+                       regmap_update_bits(dsp->regmap,
+                                          dsp->base + ADSP2_CONTROL,
+                                          ADSP2_SYS_ENA, 0);
+                       break;
+               default:
+                       regmap_write(dsp->regmap,
+                                    dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+                       regmap_write(dsp->regmap,
+                                    dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+                       regmap_write(dsp->regmap,
+                                    dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
+                       break;
+               }
+
+               if (dsp->firmwares[dsp->fw].num_caps != 0)
+                       wm_adsp_buffer_free(dsp);
+
+               mutex_unlock(&dsp->pwr_lock);
+
+               adsp_dbg(dsp, "Execution stopped\n");
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+err:
+       regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+                          ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+       mutex_unlock(&dsp->pwr_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_event);
 
-int wm_adsp2_event(struct snd_soc_dapm_widget *w,
-                  struct snd_kcontrol *kcontrol, int event)
+int wm_halo_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+                 int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
@@ -2747,25 +3716,56 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                        goto err;
                }
 
-               ret = wm_adsp2_ena(dsp);
-               if (ret != 0)
+               ret = regmap_update_bits(dsp->regmap,
+                                        dsp->base + HALO_CCM_CORE_CONTROL,
+                                        HALO_CORE_RESET, HALO_CORE_RESET);
+               if (ret != 0) {
+                       adsp_err(dsp, "Error while resetting core: %d\n", ret);
+                       return ret;
+               }
+
+               adsp_dbg(dsp, "Setting RX rates.\n");
+               ret = wm_halo_set_rate_block(dsp, HALO_SAMPLE_RATE_RX1,
+                                            dsp->n_rx_channels,
+                                            dsp->rx_rate_cache);
+               if (ret) {
+                       adsp_err(dsp, "Failed to set RX rates.\n");
+                       goto err;
+               }
+
+               adsp_dbg(dsp, "Setting TX rates.\n");
+               ret = wm_halo_set_rate_block(dsp, HALO_SAMPLE_RATE_TX1,
+                                            dsp->n_tx_channels,
+                                            dsp->tx_rate_cache);
+               if (ret) {
+                       adsp_err(dsp, "Failed to set TX rates.\n");
                        goto err;
+               }
 
-               /* Sync set controls */
-               ret = wm_coeff_sync_controls(dsp);
+               ret = wm_halo_clear_stream_arb(dsp);
                if (ret != 0)
                        goto err;
 
-               wm_adsp2_lock(dsp, dsp->lock_regions);
+               /* disable NMI */
+               ret = regmap_write(dsp->regmap,
+                                  dsp->base + HALO_INTP_CTL_NMI_CONTROL,
+                                  0);
+               if (ret != 0) {
+                       adsp_err(dsp, "Error while disabling NMI: %d\n", ret);
+                       goto err;
+               }
+
+               ret = wm_halo_configure_mpu(dsp);
+               if (ret != 0)
+                       goto err;
 
                ret = regmap_update_bits(dsp->regmap,
-                                        dsp->base + ADSP2_CONTROL,
-                                        ADSP2_CORE_ENA | ADSP2_START,
-                                        ADSP2_CORE_ENA | ADSP2_START);
+                                        dsp->base + HALO_CCM_CORE_CONTROL,
+                                        HALO_CORE_EN, HALO_CORE_EN);
                if (ret != 0)
                        goto err;
 
-               if (wm_adsp_fw[dsp->fw].num_caps != 0) {
+               if (dsp->firmwares[dsp->fw].num_caps != 0) {
                        ret = wm_adsp_buffer_init(dsp);
                        if (ret < 0)
                                goto err;
@@ -2774,93 +3774,71 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                dsp->running = true;
 
                mutex_unlock(&dsp->pwr_lock);
-
                break;
-
        case SND_SOC_DAPM_PRE_PMD:
                /* Tell the firmware to cleanup */
                wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
 
-               wm_adsp_stop_watchdog(dsp);
-
                /* Log firmware state, it can be useful for analysis */
-               switch (dsp->rev) {
-               case 0:
-                       wm_adsp2_show_fw_status(dsp);
-                       break;
-               default:
-                       wm_adsp2v2_show_fw_status(dsp);
-                       break;
-               }
+               wm_halo_show_fw_status(dsp);
 
                mutex_lock(&dsp->pwr_lock);
 
                dsp->running = false;
 
                regmap_update_bits(dsp->regmap,
-                                  dsp->base + ADSP2_CONTROL,
-                                  ADSP2_CORE_ENA | ADSP2_START, 0);
-
-               /* Make sure DMAs are quiesced */
-               switch (dsp->rev) {
-               case 0:
-                       regmap_write(dsp->regmap,
-                                    dsp->base + ADSP2_RDMA_CONFIG_1, 0);
-                       regmap_write(dsp->regmap,
-                                    dsp->base + ADSP2_WDMA_CONFIG_1, 0);
-                       regmap_write(dsp->regmap,
-                                    dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+                                  dsp->base + HALO_CCM_CORE_CONTROL,
+                                  HALO_CORE_EN, 0);
 
-                       regmap_update_bits(dsp->regmap,
-                                          dsp->base + ADSP2_CONTROL,
-                                          ADSP2_SYS_ENA, 0);
-                       break;
-               default:
-                       regmap_write(dsp->regmap,
-                                    dsp->base + ADSP2_RDMA_CONFIG_1, 0);
-                       regmap_write(dsp->regmap,
-                                    dsp->base + ADSP2_WDMA_CONFIG_1, 0);
-                       regmap_write(dsp->regmap,
-                                    dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
-                       break;
-               }
+               wm_halo_clear_stream_arb(dsp);
 
-               if (wm_adsp_fw[dsp->fw].num_caps != 0)
+               if (dsp->firmwares[dsp->fw].num_caps != 0)
                        wm_adsp_buffer_free(dsp);
 
                mutex_unlock(&dsp->pwr_lock);
 
-               adsp_dbg(dsp, "Execution stopped\n");
+               adsp_info(dsp, "Execution stopped\n");
                break;
-
        default:
                break;
        }
 
        return 0;
 err:
-       regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
-                          ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
        mutex_unlock(&dsp->pwr_lock);
+       regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL,
+                          HALO_CORE_EN, 0);
        return ret;
 }
-EXPORT_SYMBOL_GPL(wm_adsp2_event);
+EXPORT_SYMBOL_GPL(wm_halo_event);
 
 int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        char preload[32];
+       int ret;
+
+       if (codec->component.name_prefix)
+               snprintf(preload, ARRAY_SIZE(preload), "%s DSP%d%s Preload",
+                       codec->component.name_prefix, dsp->num, dsp->suffix);
+       else
+               snprintf(preload, ARRAY_SIZE(preload), "DSP%d%s Preload",
+                       dsp->num, dsp->suffix);
 
-       snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num);
        snd_soc_dapm_disable_pin(dapm, preload);
 
        wm_adsp2_init_debugfs(dsp, codec);
 
        dsp->codec = codec;
 
-       return snd_soc_add_codec_controls(codec,
-                                         &wm_adsp_fw_controls[dsp->num - 1],
-                                         1);
+       if (dsp->ao_dsp)
+               ret = snd_soc_add_codec_controls(codec,
+                                        &wm_adsp_ao_fw_controls[dsp->num - 1],
+                                        1);
+       else
+               ret = snd_soc_add_codec_controls(codec, &dsp->fw_ctrl, 1);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe);
 
@@ -2872,6 +3850,162 @@ int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec)
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove);
 
+#ifdef CONFIG_OF
+static int wm_adsp_of_parse_caps(struct wm_adsp *dsp,
+                                struct device_node *np,
+                                struct wm_adsp_fw_defs *fw)
+{
+       const char *prop = "cirrus,compr-caps";
+       int i;
+       int len_prop;
+       u32 of_cap;
+
+       if (!of_get_property(np, prop, &len_prop))
+               return -EINVAL;
+
+       len_prop /= sizeof(u32);
+
+       if (len_prop < 5 || len_prop > 4 + MAX_NUM_SAMPLE_RATES)
+               return -EOVERFLOW;
+
+       fw->num_caps = 1;
+       fw->caps = devm_kzalloc(dsp->dev,
+                               sizeof(struct wm_adsp_fw_caps),
+                               GFP_KERNEL);
+       if (!fw->caps)
+               return -ENOMEM;
+
+       fw->caps->num_regions = ARRAY_SIZE(default_regions);
+       fw->caps->region_defs = devm_kzalloc(dsp->dev,
+                                            sizeof(default_regions),
+                                            GFP_KERNEL);
+       if (!fw->caps->region_defs)
+               return -ENOMEM;
+
+       memcpy(fw->caps->region_defs, default_regions, sizeof(default_regions));
+
+       of_property_read_u32_index(np, prop, 0, &of_cap);
+       fw->caps->id = of_cap;
+       of_property_read_u32_index(np, prop, 1, &of_cap);
+       fw->caps->desc.max_ch = of_cap;
+       of_property_read_u32_index(np, prop, 2, &of_cap);
+       fw->caps->desc.formats = of_cap;
+       of_property_read_u32_index(np, prop, 3, &of_cap);
+       fw->compr_direction = of_cap;
+
+       for (i = 4; i < len_prop; ++i) {
+               of_property_read_u32_index(np, prop, i, &of_cap);
+               fw->caps->desc.sample_rates[i - 4] = of_cap;
+       }
+       fw->caps->desc.num_sample_rates = i - 4;
+
+       return 0;
+}
+
+static int wm_adsp_of_parse_firmware(struct wm_adsp *dsp,
+                                    struct device_node *np)
+{
+       struct device_node *fws = of_get_child_by_name(np, "firmware");
+       struct device_node *fw = NULL;
+       const char **ctl_names;
+       int ret;
+       int i;
+
+       if (!fws)
+               return 0;
+
+       i = 0;
+       while ((fw = of_get_next_child(fws, fw)) != NULL)
+               i++;
+
+       if (i == 0)
+               return 0;
+
+       dsp->num_firmwares = i;
+
+       dsp->firmwares = devm_kzalloc(dsp->dev,
+                                     i * sizeof(struct wm_adsp_fw_defs),
+                                     GFP_KERNEL);
+       if (!dsp->firmwares)
+               return -ENOMEM;
+
+       ctl_names = devm_kzalloc(dsp->dev,
+                                i * sizeof(const char *),
+                                GFP_KERNEL);
+       if (!ctl_names)
+               return -ENOMEM;
+
+       i = 0;
+       while ((fw = of_get_next_child(fws, fw)) != NULL) {
+               ctl_names[i] = fw->name;
+
+               ret = of_property_read_string(fw, "cirrus,wmfw-file",
+                                             &dsp->firmwares[i].file);
+               if (ret < 0) {
+                       dev_err(dsp->dev,
+                               "Firmware filename missing/malformed: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = of_property_read_string(fw, "cirrus,bin-file",
+                                             &dsp->firmwares[i].binfile);
+               if (ret < 0)
+                       dsp->firmwares[i].binfile = NULL;
+
+               dsp->firmwares[i].fullname =
+                       of_property_read_bool(fw, "cirrus,full-name");
+
+               wm_adsp_of_parse_caps(dsp, fw, &dsp->firmwares[i]);
+
+               i++;
+       }
+
+       dsp->fw_enum.items = dsp->num_firmwares;
+       dsp->fw_enum.texts = ctl_names;
+
+       dsp->fw_ctrl = wm_adsp_fw_controls[dsp->num - 1];
+       dsp->fw_ctrl.private_value = (unsigned long)(&dsp->fw_enum);
+
+       return dsp->num_firmwares;
+}
+
+static int wm_adsp_of_parse_adsp(struct wm_adsp *dsp)
+{
+       struct device_node *np = of_get_child_by_name(dsp->dev->of_node,
+                                                     "adsps");
+       struct device_node *core = NULL;
+       unsigned int addr;
+       int ret;
+
+       if (!np)
+               return 0;
+
+       while ((core = of_get_next_child(np, core)) != NULL) {
+               ret = of_property_read_u32(core, "reg", &addr);
+               if (ret < 0) {
+                       dev_err(dsp->dev,
+                               "Failed to get ADSP base address: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               if (addr == dsp->base)
+                       break;
+       }
+
+       if (!core)
+               return 0;
+
+       return wm_adsp_of_parse_firmware(dsp, core);
+}
+#else
+static inline int wm_adsp_of_parse_adsp(struct wm_adsp *dsp)
+{
+       return 0;
+}
+#endif
+
 int wm_adsp2_init(struct wm_adsp *dsp)
 {
        int ret;
@@ -2900,10 +4034,42 @@ int wm_adsp2_init(struct wm_adsp *dsp)
 
        mutex_init(&dsp->pwr_lock);
 
+       if (!dsp->dev->of_node || wm_adsp_of_parse_adsp(dsp) <= 0) {
+               dsp->fw_enum = wm_adsp_fw_enum[dsp->num - 1];
+               dsp->fw_ctrl = wm_adsp_fw_controls[dsp->num - 1];
+               dsp->num_firmwares = ARRAY_SIZE(wm_adsp_fw);
+               dsp->firmwares = wm_adsp_fw;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_init);
 
+int wm_halo_init(struct wm_adsp *dsp, struct mutex *rate_lock)
+{
+       INIT_LIST_HEAD(&dsp->alg_regions);
+       INIT_LIST_HEAD(&dsp->ctl_list);
+       INIT_WORK(&dsp->boot_work, wm_halo_boot_work);
+
+       mutex_init(&dsp->pwr_lock);
+
+       dsp->rate_lock = rate_lock;
+       dsp->rx_rate_cache = kcalloc(dsp->n_rx_channels, sizeof(u8),
+                                    GFP_KERNEL);
+       dsp->tx_rate_cache = kcalloc(dsp->n_tx_channels, sizeof(u8),
+                                    GFP_KERNEL);
+
+       if (!dsp->dev->of_node || wm_adsp_of_parse_adsp(dsp) <= 0) {
+               dsp->fw_enum = wm_adsp_fw_enum[dsp->num - 1];
+               dsp->fw_ctrl = wm_adsp_fw_controls[dsp->num - 1];
+               dsp->num_firmwares = ARRAY_SIZE(wm_adsp_fw);
+               dsp->firmwares = wm_adsp_fw;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_halo_init);
+
 void wm_adsp2_remove(struct wm_adsp *dsp)
 {
        struct wm_coeff_ctl *ctl;
@@ -2914,6 +4080,9 @@ void wm_adsp2_remove(struct wm_adsp *dsp)
                list_del(&ctl->list);
                wm_adsp_free_ctl_blk(ctl);
        }
+
+       kfree(dsp->rx_rate_cache);
+       kfree(dsp->tx_rate_cache);
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_remove);
 
@@ -2924,14 +4093,15 @@ static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
 
 static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
 {
+       int channel = wm_adsp_compr_channel(compr);
        /*
         * Note this will be more complex once each DSP can support multiple
         * streams
         */
-       if (!compr->dsp->buffer)
+       if (!compr->dsp->buffer[channel])
                return -EINVAL;
 
-       compr->buf = compr->dsp->buffer;
+       compr->buf = compr->dsp->buffer[channel];
        compr->buf->compr = compr;
 
        return 0;
@@ -2952,26 +4122,28 @@ static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
        }
 }
 
-int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
+int wm_adsp_compr_open(struct wm_adsp *dsp,
+                      struct snd_compr_stream *stream,
+                      int channel)
 {
        struct wm_adsp_compr *compr;
        int ret = 0;
 
        mutex_lock(&dsp->pwr_lock);
 
-       if (wm_adsp_fw[dsp->fw].num_caps == 0) {
+       if (dsp->firmwares[dsp->fw].num_caps == 0) {
                adsp_err(dsp, "Firmware does not support compressed API\n");
                ret = -ENXIO;
                goto out;
        }
 
-       if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
+       if (dsp->firmwares[dsp->fw].compr_direction != stream->direction) {
                adsp_err(dsp, "Firmware does not support stream direction\n");
                ret = -EINVAL;
                goto out;
        }
 
-       if (dsp->compr) {
+       if (dsp->compr[channel]) {
                /* It is expect this limitation will be removed in future */
                adsp_err(dsp, "Only a single stream supported per DSP\n");
                ret = -EBUSY;
@@ -2987,7 +4159,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
        compr->dsp = dsp;
        compr->stream = stream;
 
-       dsp->compr = compr;
+       dsp->compr[channel] = compr;
 
        stream->runtime->private_data = compr;
 
@@ -3002,11 +4174,13 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
 {
        struct wm_adsp_compr *compr = stream->runtime->private_data;
        struct wm_adsp *dsp = compr->dsp;
+       int channel;
 
        mutex_lock(&dsp->pwr_lock);
 
        wm_adsp_compr_detach(compr);
-       dsp->compr = NULL;
+       channel = wm_adsp_compr_channel(compr);
+       dsp->compr[channel] = NULL;
 
        kfree(compr->raw_buf);
        kfree(compr);
@@ -3038,8 +4212,8 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
                return -EINVAL;
        }
 
-       for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
-               caps = &wm_adsp_fw[dsp->fw].caps[i];
+       for (i = 0; i < dsp->firmwares[dsp->fw].num_caps; i++) {
+               caps = &dsp->firmwares[dsp->fw].caps[i];
                desc = &caps->desc;
 
                if (caps->id != params->codec.id)
@@ -3103,15 +4277,16 @@ int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
                           struct snd_compr_caps *caps)
 {
        struct wm_adsp_compr *compr = stream->runtime->private_data;
+       struct wm_adsp *dsp = compr->dsp;
        int fw = compr->dsp->fw;
        int i;
 
-       if (wm_adsp_fw[fw].caps) {
-               for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
-                       caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
+       if (dsp->firmwares[fw].caps) {
+               for (i = 0; i < dsp->firmwares[fw].num_caps; i++)
+                       caps->codecs[i] = dsp->firmwares[fw].caps[i].id;
 
                caps->num_codecs = i;
-               caps->direction = wm_adsp_fw[fw].compr_direction;
+               caps->direction = dsp->firmwares[fw].compr_direction;
 
                caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
                caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
@@ -3134,7 +4309,7 @@ static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
        if (!mem)
                return -EINVAL;
 
-       reg = wm_adsp_region_to_reg(mem, mem_addr);
+       reg = wm_adsp_region_to_reg(dsp, mem, mem_addr);
 
        ret = regmap_raw_read(dsp->regmap, reg, data,
                              sizeof(*data) * num_words);
@@ -3162,7 +4337,7 @@ static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
        if (!mem)
                return -EINVAL;
 
-       reg = wm_adsp_region_to_reg(mem, mem_addr);
+       reg = wm_adsp_region_to_reg(dsp, mem, mem_addr);
 
        data = cpu_to_be32(data & 0x00ffffffu);
 
@@ -3188,20 +4363,45 @@ static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
        struct wm_adsp_alg_region *alg_region;
        struct wm_adsp *dsp = buf->dsp;
        u32 xmalg, addr, magic;
-       int i, ret;
+       int i, ret, two_bufs = 0;
 
        alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
-       xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
+       switch (dsp->type) {
+       case WMFW_ADSP2:
+               xmalg = sizeof(struct wm_adsp_system_config_xm_hdr);
+               xmalg /= sizeof(__be32);
+               break;
+       case WMFW_HALO:
+               xmalg = sizeof(struct wm_halo_system_config_xm_hdr);
+               xmalg /= sizeof(__be32);
+               break;
+       default:
+               WARN(1, "Unknown DSP type");
+               return -ENODEV;
+       }
 
        addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
        ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
        if (ret < 0)
                return ret;
 
+       /* Hack for Moto FW to support 2 buffers */
+       if (magic == WM_ADSP_ALG_XM2_STRUCT_MAGIC) {
+               xmalg++;
+               addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
+               ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
+               if (ret < 0)
+                       return ret;
+               two_bufs = 1;
+       }
+
        if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
                return -EINVAL;
 
        addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
+       /* move for 7 words for second buffer */
+       if (buf->num)
+               addr += 7;
        for (i = 0; i < 5; ++i) {
                ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
                                             &buf->host_buf_ptr);
@@ -3217,14 +4417,16 @@ static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
        if (!buf->host_buf_ptr)
                return -EIO;
 
-       adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
+       adsp_dbg(dsp, "buffer #%d host_buf_ptr=%x\n",
+                buf->num, buf->host_buf_ptr);
 
-       return 0;
+       return two_bufs;
 }
 
 static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
 {
-       const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
+       struct wm_adsp *dsp = buf->dsp;
+       const struct wm_adsp_fw_caps *caps = dsp->firmwares[dsp->fw].caps;
        struct wm_adsp_buffer_region *region;
        u32 offset = 0;
        int i, ret;
@@ -3248,8 +4450,8 @@ static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
                region->cumulative_size = offset;
 
                adsp_dbg(buf->dsp,
-                        "region=%d type=%d base=%04x off=%04x size=%04x\n",
-                        i, region->mem_type, region->base_addr,
+                        "buffer #%d region=%d type=%d base=%04x off=%04x size=%04x\n",
+                        buf->num, i, region->mem_type, region->base_addr,
                         region->offset, region->cumulative_size);
        }
 
@@ -3260,6 +4462,7 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
 {
        struct wm_adsp_compr_buf *buf;
        int ret;
+       int two_bufs = 0;
 
        buf = kzalloc(sizeof(*buf), GFP_KERNEL);
        if (!buf)
@@ -3268,6 +4471,7 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
        buf->dsp = dsp;
        buf->read_index = -1;
        buf->irq_count = 0xFFFFFFFF;
+       buf->num = 0;
 
        ret = wm_adsp_buffer_locate(buf);
        if (ret < 0) {
@@ -3275,7 +4479,10 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
                goto err_buffer;
        }
 
-       buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions,
+       if (ret == 1)
+               two_bufs = 1;
+
+       buf->regions = kcalloc(dsp->firmwares[dsp->fw].caps->num_regions,
                               sizeof(*buf->regions), GFP_KERNEL);
        if (!buf->regions) {
                ret = -ENOMEM;
@@ -3288,10 +4495,53 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
                goto err_regions;
        }
 
-       dsp->buffer = buf;
+       dsp->buffer[0] = buf;
+       dsp->buf_num++;
 
-       return 0;
+       if (!two_bufs)
+               return 0;
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_clean1;
+       }
+
+       buf->dsp = dsp;
+       buf->read_index = -1;
+       buf->irq_count = 0xFFFFFFFF;
+       buf->num = 1;
+
+       ret = wm_adsp_buffer_locate(buf);
+       if (ret < 0) {
+               adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret);
+               goto err_buffer2;
+       }
+
+       buf->regions = kcalloc(dsp->firmwares[dsp->fw].caps->num_regions,
+                              sizeof(*buf->regions), GFP_KERNEL);
+       if (!buf->regions) {
+               ret = -ENOMEM;
+               goto err_buffer2;
+       }
 
+       ret = wm_adsp_buffer_populate(buf);
+       if (ret < 0) {
+               adsp_err(dsp, "Failed to populate host buffer: %d\n", ret);
+               goto err_regions2;
+       }
+       dsp->buffer[1] = buf;
+       dsp->buf_num++;
+       return 0;
+err_regions2:
+       kfree(buf->regions);
+err_buffer2:
+       kfree(buf);
+err_clean1:
+       kfree(dsp->buffer[0]->regions);
+       kfree(dsp->buffer[0]);
+       dsp->buffer[0] = NULL;
+       return ret;
 err_regions:
        kfree(buf->regions);
 err_buffer:
@@ -3301,15 +4551,19 @@ err_buffer:
 
 static int wm_adsp_buffer_free(struct wm_adsp *dsp)
 {
-       if (dsp->buffer) {
-               wm_adsp_compr_detach(dsp->buffer->compr);
+       int i;
 
-               kfree(dsp->buffer->regions);
-               kfree(dsp->buffer);
+       for (i = 0; i < dsp->buf_num; i++) {
+               if (dsp->buffer[i]) {
+                       wm_adsp_compr_detach(dsp->buffer[i]->compr);
 
-               dsp->buffer = NULL;
-       }
+                       kfree(dsp->buffer[i]->regions);
+                       kfree(dsp->buffer[i]);
 
+                       dsp->buffer[i] = NULL;
+               }
+       }
+       dsp->buf_num = 0;
        return 0;
 }
 
@@ -3335,6 +4589,8 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
                        break;
                }
 
+               compr->buf->avail = 0;
+
                /* Trigger the IRQ at one fragment of data */
                ret = wm_adsp_buffer_write(compr->buf,
                                           HOST_BUFFER_FIELD(high_water_mark),
@@ -3360,7 +4616,8 @@ EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
 
 static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
 {
-       int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
+       struct wm_adsp *dsp = buf->dsp;
+       int last_region = dsp->firmwares[dsp->fw].caps->num_regions - 1;
 
        return buf->regions[last_region].cumulative_size;
 }
@@ -3371,6 +4628,11 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
        int write_index, read_index, avail;
        int ret;
 
+#if IS_ENABLED(CONFIG_SND_SOC_AOV_TRIGGER)
+       /* Always read read_index in Moto AOV solution */
+       buf->read_index = -1;
+#endif
+
        /* Only sync read index if we haven't already read a valid index */
        if (buf->read_index < 0) {
                ret = wm_adsp_buffer_read(buf,
@@ -3396,6 +4658,9 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
 
        write_index = sign_extend32(next_write_index, 23);
 
+       /* Don't empty the buffer as it kills the firmware */
+       write_index--;
+
        avail = write_index - buf->read_index;
        if (avail < 0)
                avail += wm_adsp_buffer_size(buf);
@@ -3425,7 +4690,7 @@ static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
        return 0;
 }
 
-int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
+int wm_adsp_compr_handle_irq(struct wm_adsp *dsp, int channel)
 {
        struct wm_adsp_compr_buf *buf;
        struct wm_adsp_compr *compr;
@@ -3433,8 +4698,8 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
 
        mutex_lock(&dsp->pwr_lock);
 
-       buf = dsp->buffer;
-       compr = dsp->compr;
+       buf = dsp->buffer[channel];
+       compr = dsp->compr[channel];
 
        if (!buf) {
                ret = -ENODEV;
@@ -3460,7 +4725,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
                goto out;
        }
 
-       if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
+       if (dsp->firmwares[dsp->fw].voice_trigger && buf->irq_count == 2)
                ret = WM_ADSP_COMPR_VOICE_TRIGGER;
 
 out_notify:
@@ -3552,6 +4817,7 @@ EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
 static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
 {
        struct wm_adsp_compr_buf *buf = compr->buf;
+       struct wm_adsp *dsp = buf->dsp;
        u8 *pack_in = (u8 *)compr->raw_buf;
        u8 *pack_out = (u8 *)compr->raw_buf;
        unsigned int adsp_addr;
@@ -3559,11 +4825,11 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
        int i, j, ret;
 
        /* Calculate read parameters */
-       for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
+       for (i = 0; i < dsp->firmwares[dsp->fw].caps->num_regions; ++i)
                if (buf->read_index < buf->regions[i].cumulative_size)
                        break;
 
-       if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
+       if (i == dsp->firmwares[dsp->fw].caps->num_regions)
                return -EINVAL;
 
        mem_type = buf->regions[i].mem_type;
@@ -3769,4 +5035,100 @@ error:
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
 
+static void wm_halo_dump_fault_info(struct wm_adsp *dsp, const char *region,
+                                   unsigned int addr, unsigned int status)
+{
+       unsigned int write = status & HALO_MPU_VIO_ERR_WR_MASK;
+       unsigned int type = (status & HALO_MPU_VIO_STS_MASK) >>
+                                HALO_MPU_VIO_STS_SHIFT;
+       unsigned int src = (status & HALO_MPU_VIO_ERR_SRC_MASK) >>
+                          HALO_MPU_VIO_ERR_SRC_SHIFT;
+
+       adsp_warn(dsp, "%s: FAULT_ADDR:0x%x FAULT_STATUS:0x%x %s\n",
+                 region, addr, status,
+                 write ? "write" : "read");
+
+       switch (src) {
+       case 0:
+               adsp_warn(dsp, "%s: SRC=HALO\n", region);
+               break;
+       default:
+               adsp_warn(dsp, "%s: SRC=Requestor%u\n", region, src);
+               break;
+       }
+
+       adsp_warn(dsp, "%s: %s %s %s %s %s %s\n",
+                 region,
+                 (type & HALO_MPU_VIO_SRAM) ? "SRAM" : "",
+                 (type & HALO_MPU_VIO_REG) ? "REG" : "",
+                 (type & HALO_MPU_VIO_AHB) ? "AHB" : "",
+                 (type & HALO_MPU_VIO_EREG) ? "EREG" : "",
+                 (type & HALO_MPU_VIO_EXTERNAL_MEM) ? "ExtMem" : "",
+                 (type & HALO_MPU_VIO_NON_EXIST) ? "NotExist" : "");
+}
+
+irqreturn_t wm_halo_bus_error(struct wm_adsp *dsp)
+{
+       struct regmap *regmap = dsp->regmap;
+       unsigned int fault[6], ahb_sts, reg;
+       int ret;
+
+       mutex_lock(&dsp->pwr_lock);
+
+       /* Ensure we log the fault even if we fail to read the fault info */
+       adsp_warn(dsp, "MPU FAULT\n");
+
+       ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1,
+                         &ahb_sts);
+       if (ret) {
+               adsp_warn(dsp, "Failed to read AHB DEBUG_1 (%d)\n", ret);
+               goto exit_unlock;
+       }
+
+       adsp_warn(dsp, "AHB WINDOW: ADDR: 0x%x STATUS: 0x%x\n",
+                 (ahb_sts & HALO_AHBM_CORE_ERR_ADDR_MASK) >>
+                 HALO_AHBM_CORE_ERR_ADDR_SHIFT,
+                 ahb_sts);
+       adsp_warn(dsp, "AHB WINDOW: %s %s %s %s\n",
+                 (ahb_sts & HALO_AHBM_ADDR_ERR_MASK) ? "ADDR" : "",
+                 (ahb_sts & HALO_AHBM_LOCKED_ERR_MASK) ? "LOCKED" : "",
+                 (ahb_sts & HALO_AHBM_SIZE_ERR_MASK) ? "SIZE" : "",
+                 (ahb_sts & HALO_AHBM_MODE_ERR_MASK) ? "MODE" : "");
+
+       ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0,
+                         &ahb_sts);
+       if (ret) {
+               adsp_warn(dsp, "Failed to read AHB DEBUG_0 (%d)\n", ret);
+               goto exit_unlock;
+       }
+
+       adsp_warn(dsp, "AHB SYS_ADDR: 0x%x\n", ahb_sts);
+
+       ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR,
+                              fault, ARRAY_SIZE(fault));
+       if (ret) {
+               adsp_warn(dsp, "Failed to read MPU fault info (%d)\n", ret);
+               goto exit_unlock;
+       }
+
+       wm_halo_dump_fault_info(dsp, "XM", fault[0], fault[1]);
+       wm_halo_dump_fault_info(dsp, "YM", fault[2], fault[3]);
+       wm_halo_dump_fault_info(dsp, "PM", fault[4], fault[5]);
+
+       /* Clear fault status */
+       for (reg = HALO_MPU_XM_VIO_STATUS; reg <= HALO_MPU_PM_VIO_STATUS;
+            reg += 8) {
+               ret = regmap_write(regmap, dsp->base + reg, 0);
+               if (ret)
+                       adsp_warn(dsp, "Failed to clear MPU status @0x%x (%d)\n",
+                                 reg, ret);
+       }
+
+exit_unlock:
+       mutex_unlock(&dsp->pwr_lock);
+
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_halo_bus_error);
+
 MODULE_LICENSE("GPL v2");
old mode 100644 (file)
new mode 100755 (executable)
index 41cc11c..a23fe12
@@ -22,6 +22,7 @@
 /* Return values for wm_adsp_compr_handle_irq */
 #define WM_ADSP_COMPR_OK                 0
 #define WM_ADSP_COMPR_VOICE_TRIGGER      1
+#define WM_ADSP_MAX_CHANNEL_PER_DSP      2
 
 #define WM_ADSP2_REGION_0 BIT(0)
 #define WM_ADSP2_REGION_1 BIT(1)
@@ -55,16 +56,42 @@ struct wm_adsp_alg_region {
 struct wm_adsp_compr;
 struct wm_adsp_compr_buf;
 
+struct wm_adsp_buffer_region_def {
+       unsigned int mem_type;
+       unsigned int base_offset;
+       unsigned int size_offset;
+};
+
+struct wm_adsp_fw_caps {
+       u32 id;
+       struct snd_codec_desc desc;
+       int num_regions;
+       struct wm_adsp_buffer_region_def *region_defs;
+};
+
+struct wm_adsp_fw_defs {
+       const char *file;
+       const char *binfile;
+       bool fullname;
+       int compr_direction;
+       int num_caps;
+       struct wm_adsp_fw_caps *caps;
+       bool voice_trigger;
+};
+
 struct wm_adsp {
        const char *part;
        int rev;
        int num;
        int type;
+       bool ao_dsp;
+       const char *suffix;
        struct device *dev;
        struct regmap *regmap;
        struct snd_soc_codec *codec;
 
        int base;
+       int base_sysinfo;
        int sysclk_reg;
        int sysclk_mask;
        int sysclk_shift;
@@ -73,6 +100,7 @@ struct wm_adsp {
 
        unsigned int fw_id;
        unsigned int fw_id_version;
+       unsigned int fw_vendor_id;
 
        const struct wm_adsp_region *mem;
        int num_mems;
@@ -84,16 +112,30 @@ struct wm_adsp {
        bool booted;
        bool running;
 
+       int num_firmwares;
+       struct wm_adsp_fw_defs *firmwares;
+       struct snd_kcontrol_new fw_ctrl;
+       struct soc_enum fw_enum;
+
        struct list_head ctl_list;
 
        struct work_struct boot_work;
 
-       struct wm_adsp_compr *compr;
-       struct wm_adsp_compr_buf *buffer;
+       int buf_num;
+       struct wm_adsp_compr *compr[WM_ADSP_MAX_CHANNEL_PER_DSP];
+       struct wm_adsp_compr_buf *buffer[WM_ADSP_MAX_CHANNEL_PER_DSP];
 
        struct mutex pwr_lock;
 
        unsigned int lock_regions;
+       bool unlock_all;
+
+       unsigned int n_rx_channels;
+       unsigned int n_tx_channels;
+
+       struct mutex *rate_lock;
+       u8 *rx_rate_cache;
+       u8 *tx_rate_cache;
 
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_root;
@@ -101,6 +143,21 @@ struct wm_adsp {
        char *bin_file_name;
 #endif
 
+       void (*fwevent_cb)(struct wm_adsp *dsp, int eventid);
+};
+
+struct wm_adsp_compr {
+       struct wm_adsp *dsp;
+       struct wm_adsp_compr_buf *buf;
+
+       struct snd_compr_stream *stream;
+       struct snd_compressed_buffer size;
+
+       u32 *raw_buf;
+       unsigned int copied_total;
+
+       unsigned int sample_rate;
+       bool freed;
 };
 
 #define WM_ADSP1(wname, num) \
@@ -121,6 +178,16 @@ struct wm_adsp {
        .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
 
+#define WM_HALO(wname, num, event_fn) \
+       SND_SOC_DAPM_SPK(wname " Preload", NULL), \
+{      .id = snd_soc_dapm_supply, .name = wname " Preloader", \
+       .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \
+       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \
+       .subseq = 100, /* Ensure we run after SYSCLK supply widget */ }, \
+{      .id = snd_soc_dapm_out_drv, .name = wname, \
+       .reg = SND_SOC_NOPM, .shift = num, .event = wm_halo_event, \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
 extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
 
 int wm_adsp1_init(struct wm_adsp *dsp);
@@ -128,34 +195,46 @@ int wm_adsp2_init(struct wm_adsp *dsp);
 void wm_adsp2_remove(struct wm_adsp *dsp);
 int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec);
 int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
+int wm_halo_init(struct wm_adsp *dsp, struct mutex *rate_lock);
 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
+
 int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol, int event,
                         unsigned int freq);
 
 int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions);
 irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp);
+irqreturn_t wm_halo_bus_error(struct wm_adsp *dsp);
 
 int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
 
+int wm_halo_early_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event);
+int wm_halo_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event);
+
 int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
                           struct snd_ctl_elem_value *ucontrol);
 int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
                           struct snd_ctl_elem_value *ucontrol);
 
-int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream);
+int wm_adsp_compr_open(struct wm_adsp *dsp,
+                             struct snd_compr_stream *stream,
+                             int channel);
 int wm_adsp_compr_free(struct snd_compr_stream *stream);
 int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
                             struct snd_compr_params *params);
 int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
                           struct snd_compr_caps *caps);
 int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
-int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
+int wm_adsp_compr_handle_irq(struct wm_adsp *dsp, int channel);
 int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
                          struct snd_compr_tstamp *tstamp);
 int wm_adsp_compr_copy(struct snd_compr_stream *stream,
                       char __user *buf, size_t count);
 
+extern int wm_adsp_handle_fw_event(struct wm_adsp *dsp);
+
 #endif
old mode 100644 (file)
new mode 100755 (executable)
index ec78b9d..c25dc87
@@ -29,6 +29,7 @@
 /* Non-ALSA coefficient types start at 0x1000 */
 #define WMFW_CTL_TYPE_ACKED       0x1000 /* acked control */
 #define WMFW_CTL_TYPE_HOSTEVENT   0x1001 /* event control */
+#define WMFW_CTL_TYPE_FWEVENT     0x1004 /* firmware event control */
 
 struct wmfw_header {
        char magic[4];
@@ -87,6 +88,23 @@ struct wmfw_adsp2_id_hdr {
        __be32 n_algs;
 } __packed;
 
+struct wmfw_halo_fwid_hdr {
+       __be32 core_id;
+       __be32 block_rev;
+       __be32 vendor_id;
+       __be32 id;
+       __be32 ver;
+} __packed;
+
+struct wmfw_halo_id_hdr {
+       struct wmfw_halo_fwid_hdr fw;
+       __be32 xm_base;
+       __be32 xm_size;
+       __be32 ym_base;
+       __be32 ym_size;
+       __be32 n_algs;
+} __packed;
+
 struct wmfw_alg_hdr {
        __be32 id;
        __be32 ver;
@@ -105,6 +123,14 @@ struct wmfw_adsp2_alg_hdr {
        __be32 ym;
 } __packed;
 
+struct wmfw_halo_alg_hdr {
+       struct wmfw_alg_hdr alg;
+       __be32 xm_base;
+       __be32 xm_size;
+       __be32 ym_base;
+       __be32 ym_size;
+} __packed;
+
 struct wmfw_adsp_alg_data {
        __le32 id;
        u8 name[WMFW_MAX_ALG_NAME];
@@ -153,6 +179,7 @@ struct wmfw_coeff_item {
 
 #define WMFW_ADSP1 1
 #define WMFW_ADSP2 2
+#define WMFW_HALO 4
 
 #define WMFW_ABSOLUTE         0xf0
 #define WMFW_ALGORITHM_DATA   0xf2
@@ -168,4 +195,8 @@ struct wmfw_coeff_item {
 #define WMFW_ADSP2_XM 5
 #define WMFW_ADSP2_YM 6
 
+#define WMFW_HALO_PM_PACKED 0x10
+#define WMFW_HALO_XM_PACKED 0x11
+#define WMFW_HALO_YM_PACKED 0x12
+
 #endif