All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-29  2:34 ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-29  2:34 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Peter Hsiang, Liam Girdwood,
	Mark Brown, Peter Ujfalusi
  Cc: alsa-devel, linux-kernel, Jesse Marroquin

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
---
 include/sound/max98088.h    |   54 +
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2355 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  190 ++++
 5 files changed, 2605 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..30652be
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,54 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+#define EQ_CFG_MAX 32
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq1_cfg;
+       struct max98088_eq_cfg *eq2_cfg;
+       unsigned int eq1_cfgcnt;
+       unsigned int eq2_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode:1;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microphone input
+        */
+       unsigned int digmic_left_mode:1;
+       unsigned int digmic_right_mode:1;
+
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4ccc2b7..4e6713c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS4270 if I2C
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
@@ -157,6 +158,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 23e7e2c..7184611 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..52f57d5
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2355 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+/* Configurations associated with each channel of DAI stream */
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+       int eq_textcnt;
+       const char *eq_texts[EQ_CFG_MAX];
+       int eq_sel;
+       struct soc_enum eq_enum;
+};
+
+/* Codec private data */
+struct max98088_priv {
+       u8 reg_cache[M98088_REG_CNT];
+       void *control_data;
+       struct max98088_pdata *pdata;
+
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       u8 power_state;
+       unsigned int ex_mode;
+       unsigned int digmic;
+       unsigned int mic1pre;
+       unsigned int mic2pre;
+       unsigned int extmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x00, 0x00, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 data[2];
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+/*
+ * For kernels compiled without unsigned long long int division
+ */
+unsigned long long int ulldiv(unsigned long long int dividend,
+                             unsigned long long int divisor)
+{
+       unsigned long long int quotient = 0;
+       int shift = 1;
+
+       BUG_ON(divisor == 0);
+
+       /* Result is 1.0 if divisor and dividend are equal */
+       if (divisor == dividend)
+               return 1;
+
+       /* Normalize divisor */
+       while (!(divisor & 0x8000000000000000ULL)) {
+               divisor <<= 1;
+               ++shift;
+       }
+
+       /* Shift and subtract */
+       while (shift--) {
+               quotient <<= 1;
+
+               if (divisor <= dividend) {
+                       dividend -= divisor;
+                       ++quotient;
+               }
+               divisor >>= 1;
+       }
+
+       /* Round up */
+       if (dividend > divisor)
+               ++quotient;
+
+       return quotient;
+}
+
+#define INA1_PGA_BIT 0x01
+#define INA2_PGA_BIT 0x02
+#define INB1_PGA_BIT 0x04
+#define INB2_PGA_BIT 0x08
+/*
+ * The INx1 and INx2 PGAs share a power control signal.
+ * This function OR's the two power events to keep an unpowered INx
+ * from turning off it's counterpart.
+ * The control names are used to identify the PGA.
+ */
+static int max98088_pga_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       u8 *state = &max98088->power_state;
+       unsigned int val;
+       unsigned int pga;
+       unsigned int mask;
+
+       BUG_ON(w->reg != M98088_REG_4C_PWR_EN_IN);
+
+       if (strncmp(w->name, "INA1", 4) == 0) {
+               pga = INA1_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INA2", 4) == 0) {
+               pga = INA2_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INB1", 4) == 0) {
+               pga = INB1_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else if (strncmp(w->name, "INB2", 4) == 0) {
+               pga = INB2_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else {
+               return -EINVAL;
+       }
+
+       if (event == SND_SOC_DAPM_POST_PMU) {
+               /* ON */
+               *state |= pga;
+
+               /* Turn on, avoiding unnecessary writes */
+               val = snd_soc_read(codec, w->reg);
+               if (!(val & (1 << w->shift))) {
+                       val |= (1 << w->shift);
+                       snd_soc_write(codec, w->reg, val);
+               }
+       } else if (event == SND_SOC_DAPM_POST_PMD) {
+               /* OFF */
+               *state &= ~pga;
+
+               /* Turn off if both are off, avoiding unnecessary writes */
+               if (!(*state & mask)) {
+                       val = snd_soc_read(codec, w->reg);
+                       if (val & (1 << w->shift)) {
+                               val &= ~(1 << w->shift);
+                               snd_soc_write(codec, w->reg, val);
+                       }
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       BUG_ON(band > 4);
+       BUG_ON(dai > 1);
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+
+       return;
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_ex_mode[] = {
+       "Off",
+       "100Hz",
+       "400Hz",
+       "600Hz",
+       "800Hz",
+       "1000Hz",
+       "200-400Hz",
+       "400-600Hz",
+       "400-800Hz",
+};
+
+static const unsigned int ex_mode_table[] = {
+       0x00,           /* disabled */
+       (0<<4)|3,       /* 100Hz */
+       (1<<4)|0,       /* 400Hz */
+       (2<<4)|0,       /* 600Hz */
+       (3<<4)|0,       /* 800Hz */
+       (4<<4)|0,       /* 1000Hz */
+       (1<<4)|1,       /* 200-400Hz */
+       (2<<4)|2,       /* 400-600Hz */
+       (3<<4)|2,       /* 400-800Hz */
+};
+
+static const struct soc_enum max98088_ex_mode_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_ex_mode), max98088_ex_mode),
+};
+
+static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->ex_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(ex_mode_table))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       snd_soc_write(codec, M98088_REG_41_SPKDHP,
+               ex_mode_table[*mode]);
+
+       return 0;
+}
+
+static int max98088_ex_mode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->ex_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static const char *max98088_micpre[] = {
+       "0dB",
+       "20dB",
+       "30dB",
+};
+
+static const struct soc_enum max98088_micpre_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_micpre), max98088_micpre),
+};
+
+static const char *max98088_extmic[] = {
+       "Off",
+       "MIC1",
+       "MIC2",
+};
+
+static const struct soc_enum max98088_extmic_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
+};
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic1pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic1pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic2pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic2pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_extmic_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->extmic_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_extmic))
+               return -EINVAL;
+
+       *mode = sel;
+       snd_soc_update_bits(codec, M98088_REG_48_CFG_MIC,
+               M98088_EXTMIC_MASK, sel);
+
+       return 0;
+}
+
+static int max98088_extmic_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->extmic_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       /* Analog outputs */
+
+       SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+       SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+       SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+       /* Analog inputs */
+
+       SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_ENUM_EXT("MIC1 Boost Volume", max98088_micpre_enum,
+               max98088_mic1pre_get, max98088_mic1pre_set),
+
+       SOC_ENUM_EXT("MIC2 Boost Volume", max98088_micpre_enum,
+               max98088_mic2pre_get, max98088_mic2pre_set),
+
+       SOC_ENUM_EXT("Ext MIC Switch", max98088_extmic_enum,
+               max98088_extmic_get, max98088_extmic_set),
+
+       SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       /* ADC input digital gains and volume controls */
+
+       SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       /* Equalizer */
+
+       SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       /* Excursion limiter */
+
+       SOC_ENUM_EXT("EX Limiter Mode", max98088_ex_mode_enum,
+               max98088_ex_mode_get, max98088_ex_mode_set),
+       SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+       /* Voice/music filters */
+
+       SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+       SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+               0, 1, 0),
+
+       /* Automatic level control (for both DAI1/DAI2) */
+
+       SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       /* Power limiter */
+
+       SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       /* THD distortion limiter */
+
+       SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_hp_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 status;
+
+       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+       /* powering down headphone gracefully */
+       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
+       if ((status & M98088_HPEN) == M98088_HPEN) {
+               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
+                       (status & ~M98088_HPEN));
+       }
+       schedule_timeout(msecs_to_jiffies(20));
+
+       return 0;
+}
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (w->reg == M98088_REG_35_LVL_MIC1) {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+               } else {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* DAPM widgets top level */
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA_E("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+/* DAPM AUDIO_MAP: */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right HP Mixer", "Left DAC2 Switch", "DACL2"  },
+       {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+       {"MIC1 Input", NULL, "MIC1"},
+       {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+       u32 rate;
+       u8  sr;
+} rate_table[] = {
+       {8000,  0x10},
+       {11025, 0x20},
+       {16000, 0x30},
+       {22050, 0x40},
+       {24000, 0x50},
+       {32000, 0x60},
+       {44100, 0x70},
+       {48000, 0x80},
+       {88200, 0x90},
+       {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate) {
+                       *value = rate_table[i].sr;
+                       return 0;
+               }
+       }
+       *value = rate_table[0].sr;
+       return -EINVAL;
+}
+
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI2 SR2 value for the DSP */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_19_DAI2_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       /* requested clock frequency is already setup */
+       if (freq == max98088->sysclk)
+               return 0;
+
+       max98088->sysclk = freq; /* remember current sysclk */
+
+       /* setup clocks for slave mode, and using the PLL
+        * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+        *         0x02 (when master clk is 20MHz to 30MHz)..
+        */
+       if ((freq >= 10000000) && (freq < 20000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+       } else if ((freq >= 20000000) && (freq < 30000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+       } else {
+               dev_err(codec->dev, "Invalid master clock frequency\n");
+               return -EINVAL;
+       }
+
+       if (snd_soc_read(codec, M98088_REG_51_PWR_SYS)  & M98088_SHDNRUN) {
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, 0);
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       0, M98088_SHDNRUN);
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg15val;
+       u8 reg14msk = 0;
+       u8 reg14val = 0;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg14msk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg14val |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg14val |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg14msk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg14msk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg14msk |= M98088_DAI_BCI;
+                       reg14val |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg14msk |= M98088_DAI_WCI;
+                       reg14val |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       reg14msk, reg14val);
+
+               reg15val = M98088_DAI_BSEL64;
+               if (max98088->digmic)
+                       reg15val |= M98088_DAI_OSR64;
+               snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+       }
+
+       return 0;
+}
+
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg1Cmsk = 0;
+       u8 reg1Cval = 0;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg1Cmsk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg1Cval |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg1Cval |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg1Cmsk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg1Cmsk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg1Cmsk |= M98088_DAI_BCI;
+                       reg1Cval |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg1Cmsk |= M98088_DAI_WCI;
+                       reg1Cval |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       reg1Cmsk, reg1Cval);
+
+               snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       M98088_DAI_BSEL64);
+       }
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       if (!codec->cache_sync)
+               return;
+
+       codec->cache_only = 0;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+
+       codec->cache_sync = 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               max98088_sync_cache(codec);
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, M98088_MBEN);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, 0);
+#ifdef CONFIG_REGULATOR
+               codec->cache_sync = 1;
+#endif
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+static struct snd_soc_dai_driver max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq1_cfg[best].name,
+               pdata->eq1_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq1_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq2_cfg[best].name,
+               pdata->eq2_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq2_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->eq1_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq1(codec);
+       return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->eq2_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq2(codec);
+       return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_handle_eq1_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq1control =
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->dai[0].eq_enum,
+                       max98088_get_eq1_enum,
+                       max98088_put_eq1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq1_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->eq_texts[i] = pdata->eq1_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(codec, &eq1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq2control =
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->dai[1].eq_enum,
+                       max98088_get_eq2_enum,
+                       max98088_put_eq2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq2_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               cdata->eq_texts[i] = pdata->eq2_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(codec, &eq2control, 1);
+       if (ret != 0)
+               printk(KERN_ERR "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval = 0;
+
+       if (!pdata) {
+               dev_dbg(codec->dev, "No platform data\n");
+               return;
+       }
+
+       /* configure mic for analog/digital mic mode */
+       if (pdata->digmic_left_mode)
+               regval |= M98088_DIGMIC_L;
+
+       if (pdata->digmic_right_mode)
+               regval |= M98088_DIGMIC_R;
+
+       max98088->digmic = (regval ? 1 : 0);
+
+       snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* configure receiver output */
+       regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               M98088_REC_LINEMODE_MASK, regval);
+
+       /* configure equalizers */
+       if (pdata->eq1_cfgcnt)
+               max98088_handle_eq1_pdata(codec);
+
+       if (pdata->eq2_cfgcnt)
+               max98088_handle_eq2_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct snd_soc_codec *codec)
+{
+       int i;
+       u8 *cache = codec->reg_cache;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < M98088_REG_CNT; i++) {
+               if (i == M98088_REG_51_PWR_SYS)
+                       continue;
+
+               if (!max98088_access[i].writable)
+                       continue;
+
+               max98088_hw_write(codec, i, cache[i]);
+       }
+
+       /* now enter into the resume mode bias level */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define max98088_suspend NULL
+#define max98088_resume NULL
+#endif
+
+static int max98088_probe(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       int ret = 0;
+
+       codec->cache_sync = 1;
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       max98088->power_state = 0; /* INA INB power enable state */
+       max98088->ex_mode = 0; /* excursion limiter mode */
+       max98088->digmic = 0; /* 0=analog, 1=digital */
+       max98088->mic1pre = 0;
+       max98088->mic2pre = 0;
+
+       ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read device revision: %d\n",
+                       ret);
+               goto err_access;
+       }
+       dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+       snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+               M98088_S1NORMAL|M98088_SDATA);
+
+       snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+               M98088_S2NORMAL|M98088_SDATA);
+
+       max98088_handle_pdata(codec);
+
+       max98088_add_widgets(codec);
+
+err_access:
+       return ret;
+}
+
+static int max98088_remove(struct snd_soc_codec *codec)
+{
+       if (codec->control_data)
+               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+       .set_bias_level = max98088_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(max98088_reg),
+       .reg_word_size = sizeof(u8),
+       .reg_cache_default = max98088_reg,
+       .volatile_register = max98088_volatile_register,
+};
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct max98088_priv *max98088;
+       int ret;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max98088);
+       max98088->control_data = i2c;
+       max98088->pdata = i2c->dev.platform_data;
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_max98088, &max98088_dai[0], 2);
+       if (ret < 0)
+               kfree(max98088);
+       return ret;
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "max98088-codec",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+
+module_init(max98088_init);
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..9e9d8da
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,190 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+       #define M98088_DAI_BSEL64               (1<<0)
+       #define M98088_DAI_OSR64                (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+       #define M98088_S1NORMAL                 (1<<6)
+       #define M98088_S2NORMAL                 (2<<6)
+       #define M98088_SDATA                    (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+       #define M98088_DAI_DHF                  (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+       #define M98088_REC_LINEMODE             (1<<7)
+       #define M98088_REC_LINEMODE_MASK        (1<<7)
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+       #define M98088_MICPRE_MASK              (3<<5)
+       #define M98088_MICPRE_SHIFT             5
+
+/* M98088_REG_3A_LVL_HP_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+       #define M98088_EXTMIC_MASK              (3<<0)
+       #define M98088_DIGMIC_L                 (1<<5)
+       #define M98088_DIGMIC_R                 (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+       #define M98088_INAEN                    (1<<7)
+       #define M98088_INBEN                    (1<<6)
+       #define M98088_MBEN                     (1<<3)
+       #define M98088_ADLEN                    (1<<1)
+       #define M98088_ADREN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+struct max98088_setup_data {
+       unsigned short i2c_address;
+};
+
+#endif
--
1.6.3.3


^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-29  2:34 ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-29  2:34 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Peter Hsiang, Liam Girdwood, Mark Brown
  Cc: alsa-devel, linux-kernel, Jesse Marroquin

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
---
 include/sound/max98088.h    |   54 +
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2355 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  190 ++++
 5 files changed, 2605 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..30652be
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,54 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+#define EQ_CFG_MAX 32
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq1_cfg;
+       struct max98088_eq_cfg *eq2_cfg;
+       unsigned int eq1_cfgcnt;
+       unsigned int eq2_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode:1;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microphone input
+        */
+       unsigned int digmic_left_mode:1;
+       unsigned int digmic_right_mode:1;
+
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4ccc2b7..4e6713c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS4270 if I2C
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
@@ -157,6 +158,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 23e7e2c..7184611 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..52f57d5
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2355 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+/* Configurations associated with each channel of DAI stream */
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+       int eq_textcnt;
+       const char *eq_texts[EQ_CFG_MAX];
+       int eq_sel;
+       struct soc_enum eq_enum;
+};
+
+/* Codec private data */
+struct max98088_priv {
+       u8 reg_cache[M98088_REG_CNT];
+       void *control_data;
+       struct max98088_pdata *pdata;
+
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       u8 power_state;
+       unsigned int ex_mode;
+       unsigned int digmic;
+       unsigned int mic1pre;
+       unsigned int mic2pre;
+       unsigned int extmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x00, 0x00, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 data[2];
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+/*
+ * For kernels compiled without unsigned long long int division
+ */
+unsigned long long int ulldiv(unsigned long long int dividend,
+                             unsigned long long int divisor)
+{
+       unsigned long long int quotient = 0;
+       int shift = 1;
+
+       BUG_ON(divisor == 0);
+
+       /* Result is 1.0 if divisor and dividend are equal */
+       if (divisor == dividend)
+               return 1;
+
+       /* Normalize divisor */
+       while (!(divisor & 0x8000000000000000ULL)) {
+               divisor <<= 1;
+               ++shift;
+       }
+
+       /* Shift and subtract */
+       while (shift--) {
+               quotient <<= 1;
+
+               if (divisor <= dividend) {
+                       dividend -= divisor;
+                       ++quotient;
+               }
+               divisor >>= 1;
+       }
+
+       /* Round up */
+       if (dividend > divisor)
+               ++quotient;
+
+       return quotient;
+}
+
+#define INA1_PGA_BIT 0x01
+#define INA2_PGA_BIT 0x02
+#define INB1_PGA_BIT 0x04
+#define INB2_PGA_BIT 0x08
+/*
+ * The INx1 and INx2 PGAs share a power control signal.
+ * This function OR's the two power events to keep an unpowered INx
+ * from turning off it's counterpart.
+ * The control names are used to identify the PGA.
+ */
+static int max98088_pga_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       u8 *state = &max98088->power_state;
+       unsigned int val;
+       unsigned int pga;
+       unsigned int mask;
+
+       BUG_ON(w->reg != M98088_REG_4C_PWR_EN_IN);
+
+       if (strncmp(w->name, "INA1", 4) == 0) {
+               pga = INA1_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INA2", 4) == 0) {
+               pga = INA2_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INB1", 4) == 0) {
+               pga = INB1_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else if (strncmp(w->name, "INB2", 4) == 0) {
+               pga = INB2_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else {
+               return -EINVAL;
+       }
+
+       if (event == SND_SOC_DAPM_POST_PMU) {
+               /* ON */
+               *state |= pga;
+
+               /* Turn on, avoiding unnecessary writes */
+               val = snd_soc_read(codec, w->reg);
+               if (!(val & (1 << w->shift))) {
+                       val |= (1 << w->shift);
+                       snd_soc_write(codec, w->reg, val);
+               }
+       } else if (event == SND_SOC_DAPM_POST_PMD) {
+               /* OFF */
+               *state &= ~pga;
+
+               /* Turn off if both are off, avoiding unnecessary writes */
+               if (!(*state & mask)) {
+                       val = snd_soc_read(codec, w->reg);
+                       if (val & (1 << w->shift)) {
+                               val &= ~(1 << w->shift);
+                               snd_soc_write(codec, w->reg, val);
+                       }
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       BUG_ON(band > 4);
+       BUG_ON(dai > 1);
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+
+       return;
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_ex_mode[] = {
+       "Off",
+       "100Hz",
+       "400Hz",
+       "600Hz",
+       "800Hz",
+       "1000Hz",
+       "200-400Hz",
+       "400-600Hz",
+       "400-800Hz",
+};
+
+static const unsigned int ex_mode_table[] = {
+       0x00,           /* disabled */
+       (0<<4)|3,       /* 100Hz */
+       (1<<4)|0,       /* 400Hz */
+       (2<<4)|0,       /* 600Hz */
+       (3<<4)|0,       /* 800Hz */
+       (4<<4)|0,       /* 1000Hz */
+       (1<<4)|1,       /* 200-400Hz */
+       (2<<4)|2,       /* 400-600Hz */
+       (3<<4)|2,       /* 400-800Hz */
+};
+
+static const struct soc_enum max98088_ex_mode_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_ex_mode), max98088_ex_mode),
+};
+
+static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->ex_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(ex_mode_table))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       snd_soc_write(codec, M98088_REG_41_SPKDHP,
+               ex_mode_table[*mode]);
+
+       return 0;
+}
+
+static int max98088_ex_mode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->ex_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static const char *max98088_micpre[] = {
+       "0dB",
+       "20dB",
+       "30dB",
+};
+
+static const struct soc_enum max98088_micpre_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_micpre), max98088_micpre),
+};
+
+static const char *max98088_extmic[] = {
+       "Off",
+       "MIC1",
+       "MIC2",
+};
+
+static const struct soc_enum max98088_extmic_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
+};
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic1pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic1pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic2pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->mic2pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_extmic_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->extmic_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_extmic))
+               return -EINVAL;
+
+       *mode = sel;
+       snd_soc_update_bits(codec, M98088_REG_48_CFG_MIC,
+               M98088_EXTMIC_MASK, sel);
+
+       return 0;
+}
+
+static int max98088_extmic_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int *mode = &max98088->extmic_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       /* Analog outputs */
+
+       SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+       SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+       SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+       /* Analog inputs */
+
+       SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_ENUM_EXT("MIC1 Boost Volume", max98088_micpre_enum,
+               max98088_mic1pre_get, max98088_mic1pre_set),
+
+       SOC_ENUM_EXT("MIC2 Boost Volume", max98088_micpre_enum,
+               max98088_mic2pre_get, max98088_mic2pre_set),
+
+       SOC_ENUM_EXT("Ext MIC Switch", max98088_extmic_enum,
+               max98088_extmic_get, max98088_extmic_set),
+
+       SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       /* ADC input digital gains and volume controls */
+
+       SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       /* Equalizer */
+
+       SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       /* Excursion limiter */
+
+       SOC_ENUM_EXT("EX Limiter Mode", max98088_ex_mode_enum,
+               max98088_ex_mode_get, max98088_ex_mode_set),
+       SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+       /* Voice/music filters */
+
+       SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+       SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+               0, 1, 0),
+
+       /* Automatic level control (for both DAI1/DAI2) */
+
+       SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       /* Power limiter */
+
+       SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       /* THD distortion limiter */
+
+       SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_hp_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 status;
+
+       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+       /* powering down headphone gracefully */
+       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
+       if ((status & M98088_HPEN) == M98088_HPEN) {
+               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
+                       (status & ~M98088_HPEN));
+       }
+       schedule_timeout(msecs_to_jiffies(20));
+
+       return 0;
+}
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (w->reg == M98088_REG_35_LVL_MIC1) {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+               } else {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* DAPM widgets top level */
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA_E("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+/* DAPM AUDIO_MAP: */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right HP Mixer", "Left DAC2 Switch", "DACL2"  },
+       {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+       {"MIC1 Input", NULL, "MIC1"},
+       {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+       u32 rate;
+       u8  sr;
+} rate_table[] = {
+       {8000,  0x10},
+       {11025, 0x20},
+       {16000, 0x30},
+       {22050, 0x40},
+       {24000, 0x50},
+       {32000, 0x60},
+       {44100, 0x70},
+       {48000, 0x80},
+       {88200, 0x90},
+       {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate) {
+                       *value = rate_table[i].sr;
+                       return 0;
+               }
+       }
+       *value = rate_table[0].sr;
+       return -EINVAL;
+}
+
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI2 SR2 value for the DSP */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_19_DAI2_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       /* requested clock frequency is already setup */
+       if (freq == max98088->sysclk)
+               return 0;
+
+       max98088->sysclk = freq; /* remember current sysclk */
+
+       /* setup clocks for slave mode, and using the PLL
+        * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+        *         0x02 (when master clk is 20MHz to 30MHz)..
+        */
+       if ((freq >= 10000000) && (freq < 20000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+       } else if ((freq >= 20000000) && (freq < 30000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+       } else {
+               dev_err(codec->dev, "Invalid master clock frequency\n");
+               return -EINVAL;
+       }
+
+       if (snd_soc_read(codec, M98088_REG_51_PWR_SYS)  & M98088_SHDNRUN) {
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, 0);
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       0, M98088_SHDNRUN);
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg15val;
+       u8 reg14msk = 0;
+       u8 reg14val = 0;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg14msk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg14val |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg14val |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg14msk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg14msk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg14msk |= M98088_DAI_BCI;
+                       reg14val |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg14msk |= M98088_DAI_WCI;
+                       reg14val |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       reg14msk, reg14val);
+
+               reg15val = M98088_DAI_BSEL64;
+               if (max98088->digmic)
+                       reg15val |= M98088_DAI_OSR64;
+               snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+       }
+
+       return 0;
+}
+
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg1Cmsk = 0;
+       u8 reg1Cval = 0;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg1Cmsk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg1Cval |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg1Cval |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg1Cmsk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg1Cmsk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg1Cmsk |= M98088_DAI_BCI;
+                       reg1Cval |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg1Cmsk |= M98088_DAI_WCI;
+                       reg1Cval |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       reg1Cmsk, reg1Cval);
+
+               snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       M98088_DAI_BSEL64);
+       }
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       if (!codec->cache_sync)
+               return;
+
+       codec->cache_only = 0;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+
+       codec->cache_sync = 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               max98088_sync_cache(codec);
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, M98088_MBEN);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, 0);
+#ifdef CONFIG_REGULATOR
+               codec->cache_sync = 1;
+#endif
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+static struct snd_soc_dai_driver max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq1_cfg[best].name,
+               pdata->eq1_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq1_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq2_cfg[best].name,
+               pdata->eq2_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq2_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->eq1_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq1(codec);
+       return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->eq2_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq2(codec);
+       return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_handle_eq1_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq1control =
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->dai[0].eq_enum,
+                       max98088_get_eq1_enum,
+                       max98088_put_eq1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq1_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->eq_texts[i] = pdata->eq1_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(codec, &eq1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq2control =
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->dai[1].eq_enum,
+                       max98088_get_eq2_enum,
+                       max98088_put_eq2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq2_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               cdata->eq_texts[i] = pdata->eq2_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(codec, &eq2control, 1);
+       if (ret != 0)
+               printk(KERN_ERR "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval = 0;
+
+       if (!pdata) {
+               dev_dbg(codec->dev, "No platform data\n");
+               return;
+       }
+
+       /* configure mic for analog/digital mic mode */
+       if (pdata->digmic_left_mode)
+               regval |= M98088_DIGMIC_L;
+
+       if (pdata->digmic_right_mode)
+               regval |= M98088_DIGMIC_R;
+
+       max98088->digmic = (regval ? 1 : 0);
+
+       snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* configure receiver output */
+       regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               M98088_REC_LINEMODE_MASK, regval);
+
+       /* configure equalizers */
+       if (pdata->eq1_cfgcnt)
+               max98088_handle_eq1_pdata(codec);
+
+       if (pdata->eq2_cfgcnt)
+               max98088_handle_eq2_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct snd_soc_codec *codec)
+{
+       int i;
+       u8 *cache = codec->reg_cache;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < M98088_REG_CNT; i++) {
+               if (i == M98088_REG_51_PWR_SYS)
+                       continue;
+
+               if (!max98088_access[i].writable)
+                       continue;
+
+               max98088_hw_write(codec, i, cache[i]);
+       }
+
+       /* now enter into the resume mode bias level */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define max98088_suspend NULL
+#define max98088_resume NULL
+#endif
+
+static int max98088_probe(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       int ret = 0;
+
+       codec->cache_sync = 1;
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       max98088->power_state = 0; /* INA INB power enable state */
+       max98088->ex_mode = 0; /* excursion limiter mode */
+       max98088->digmic = 0; /* 0=analog, 1=digital */
+       max98088->mic1pre = 0;
+       max98088->mic2pre = 0;
+
+       ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read device revision: %d\n",
+                       ret);
+               goto err_access;
+       }
+       dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+       snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+               M98088_S1NORMAL|M98088_SDATA);
+
+       snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+               M98088_S2NORMAL|M98088_SDATA);
+
+       max98088_handle_pdata(codec);
+
+       max98088_add_widgets(codec);
+
+err_access:
+       return ret;
+}
+
+static int max98088_remove(struct snd_soc_codec *codec)
+{
+       if (codec->control_data)
+               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+       .set_bias_level = max98088_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(max98088_reg),
+       .reg_word_size = sizeof(u8),
+       .reg_cache_default = max98088_reg,
+       .volatile_register = max98088_volatile_register,
+};
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct max98088_priv *max98088;
+       int ret;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max98088);
+       max98088->control_data = i2c;
+       max98088->pdata = i2c->dev.platform_data;
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_max98088, &max98088_dai[0], 2);
+       if (ret < 0)
+               kfree(max98088);
+       return ret;
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "max98088-codec",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+
+module_init(max98088_init);
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..9e9d8da
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,190 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+       #define M98088_DAI_BSEL64               (1<<0)
+       #define M98088_DAI_OSR64                (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+       #define M98088_S1NORMAL                 (1<<6)
+       #define M98088_S2NORMAL                 (2<<6)
+       #define M98088_SDATA                    (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+       #define M98088_DAI_DHF                  (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+       #define M98088_REC_LINEMODE             (1<<7)
+       #define M98088_REC_LINEMODE_MASK        (1<<7)
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+       #define M98088_MICPRE_MASK              (3<<5)
+       #define M98088_MICPRE_SHIFT             5
+
+/* M98088_REG_3A_LVL_HP_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+       #define M98088_EXTMIC_MASK              (3<<0)
+       #define M98088_DIGMIC_L                 (1<<5)
+       #define M98088_DIGMIC_R                 (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+       #define M98088_INAEN                    (1<<7)
+       #define M98088_INBEN                    (1<<6)
+       #define M98088_MBEN                     (1<<3)
+       #define M98088_ADLEN                    (1<<1)
+       #define M98088_ADREN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+struct max98088_setup_data {
+       unsigned short i2c_address;
+};
+
+#endif
--
1.6.3.3

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-29  2:34 ` Peter Hsiang
@ 2010-09-29  3:37   ` Mark Brown
  -1 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-29  3:37 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Tue, Sep 28, 2010 at 07:34:34PM -0700, Peter Hsiang wrote:

> +#define EQ_CFG_MAX 32

There doesn't seem to be any need to hard code a limit here?

> +static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
> +                            unsigned int value)
> +{
> +       u8 data[2];
> +
> +       data[0] = reg;
> +       data[1] = value;
> +       if (codec->hw_write(codec->control_data, data, 2) == 2)
> +               return 0;
> +       else
> +               return -EIO;
> +}

As previously discussed you should use the soc-cache code here.

> +/*
> + * For kernels compiled without unsigned long long int division
> + */
> +unsigned long long int ulldiv(unsigned long long int dividend,
> +                             unsigned long long int divisor)

This is causing me some concern.  Even if it is required this does not
look like something that should be part of a specific driver - what
happens if some other driver needs the same thing?

> +/*
> + * The INx1 and INx2 PGAs share a power control signal.
> + * This function OR's the two power events to keep an unpowered INx
> + * from turning off it's counterpart.
> + * The control names are used to identify the PGA.
> + */

This...

> +       if (strncmp(w->name, "INA1", 4) == 0) {
> +               pga = INA1_PGA_BIT;
> +               mask = INA1_PGA_BIT | INA2_PGA_BIT;

...doesn't seem to correspond to this, at least with the normal way mask
is used.  Apart from anything else it looks like you have individual
mask bits for each of the INx1 and INx2 PGAs (since you can define mask
bits for each of them).  It would be much clearer if the code made it
obvious that these aren't register bits but rather that you're storing
the data in a register-like bitfield in a variable.  I can't help but
think that a reference count would be much clearer.

> +       if (event == SND_SOC_DAPM_POST_PMU) {
> +               /* ON */
> +               *state |= pga;
> +
> +               /* Turn on, avoiding unnecessary writes */
> +               val = snd_soc_read(codec, w->reg);
> +               if (!(val & (1 << w->shift))) {
> +                       val |= (1 << w->shift);
> +                       snd_soc_write(codec, w->reg, val);
> +               }

snd_soc_update_bits() will suppress unwanted register writes.

> +       }
> +
> +       return;
> +}

No need for return statements at the end of void functions.

> +
> +static const unsigned int ex_mode_table[] = {
> +       0x00,           /* disabled */
> +       (0<<4)|3,       /* 100Hz */
> +       (1<<4)|0,       /* 400Hz */
> +       (2<<4)|0,       /* 600Hz */
> +       (3<<4)|0,       /* 800Hz */
> +       (4<<4)|0,       /* 1000Hz */
> +       (1<<4)|1,       /* 200-400Hz */
> +       (2<<4)|2,       /* 400-600Hz */
> +       (3<<4)|2,       /* 400-800Hz */
> +};

You should add value muxes like we have for DAPM.

> +static const char *max98088_micpre[] = {
> +       "0dB",
> +       "20dB",
> +       "30dB",
> +};

> +static const struct soc_enum max98088_micpre_enum[] = {
> +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_micpre), max98088_micpre),
> +};

This should be a TLV control, not an enum.

> +static const char *max98088_extmic[] = {
> +       "Off",
> +       "MIC1",
> +       "MIC2",
> +};

> +static const struct soc_enum max98088_extmic_enum[] = {
> +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
> +};

This looks like it should be in DAPM.

> +static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
> +                               struct snd_ctl_elem_value *ucontrol)
> +{
> +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
> +       unsigned int *mode = &max98088->mic1pre;
> +       int sel = ucontrol->value.integer.value[0];
> +
> +       if (sel >= ARRAY_SIZE(max98088_micpre))
> +               return -EINVAL;
> +
> +       *mode = ucontrol->value.integer.value[0];
> +       return 0;
> +}

I'd expect that some action would be taken when the value is set here.
All this does is set a variable, changing the control will have no
effect.

> +static int max98088_hp_event(struct snd_soc_dapm_widget *w,
> +                            struct snd_kcontrol *kcontrol, int event)
> +{
> +       struct snd_soc_codec *codec = w->codec;
> +       u16 status;
> +
> +       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
> +
> +       /* powering down headphone gracefully */
> +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> +       if ((status & M98088_HPEN) == M98088_HPEN) {
> +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> +                       (status & ~M98088_HPEN));
> +       }
> +       schedule_timeout(msecs_to_jiffies(20));

This looks rather like it should just be a post event implementing a
timeout?

> +       if (rate != cdata->rate) {
> +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> +               if (rate_value(rate, &regval))
> +                       return -EINVAL;
> +
> +               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
> +               cdata->rate = rate;
> +       }

Just use snd_soc_update_bits() to suppress unneeded writes.  Many of the
operaations have this issue.

> +               & M98088_DAI_MAS) {
> +               if (max98088->sysclk == 0)
> +                       return -EINVAL;

You should print an error here so users can tell what went wrong.

> +               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> +               case SND_SOC_DAIFMT_I2S:
> +                       reg14val |= M98088_DAI_DLY;
> +                       break;
> +               case SND_SOC_DAIFMT_LEFT_J:
> +                       reg14msk |= M98088_DAI_DLY;
> +                       break;

This looks fishy - in one case you set the value, in another format you
set the mask, both to the same bitfield.   I'd expect the mask of bits
being updated to stay constant, but you seem to be treating the mask as
meaning bits to be cleared.  I rather suspect you'll run into problems
if you test this.

> +       case SND_SOC_BIAS_STANDBY:
> +               max98088_sync_cache(codec);
> +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> +                               M98088_MBEN, M98088_MBEN);
> +               break;

Do you really want to sync the cache *every* time you go into standby?

> +
> +       case SND_SOC_BIAS_OFF:
> +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> +                               M98088_MBEN, 0);
> +#ifdef CONFIG_REGULATOR
> +               codec->cache_sync = 1;
> +#endif

Why the ifdef?

> +
> +       /* Build an array of texts for the enum API. The number
> +        * of texts is likely fewer than the number of configurations
> +        * due to multiple sample rates for the same text name. */
> +       cdata->eq_textcnt = 0;
> +       for (i = 0; i < pdata->eq1_cfgcnt; i++) {

It might be nice to give credit to the drivers you've based your work on :)

> +       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
> +
> +       /* Sync reg_cache with the hardware */
> +       for (i = 0; i < M98088_REG_CNT; i++) {
> +               if (i == M98088_REG_51_PWR_SYS)
> +                       continue;
> +
> +               if (!max98088_access[i].writable)
> +                       continue;
> +
> +               max98088_hw_write(codec, i, cache[i]);
> +       }

This appears to duplicate the register sync in your bias management.

> +static struct i2c_driver max98088_i2c_driver = {
> +       .driver = {
> +               .name = "max98088-codec",

Drop the -codec from the name.

> +module_init(max98088_init);

Normally this would be next to the function it references.

> +struct max98088_setup_data {
> +       unsigned short i2c_address;
> +};

This should be removed.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-29  3:37   ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-29  3:37 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Jesse Marroquin, Liam Girdwood

On Tue, Sep 28, 2010 at 07:34:34PM -0700, Peter Hsiang wrote:

> +#define EQ_CFG_MAX 32

There doesn't seem to be any need to hard code a limit here?

> +static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
> +                            unsigned int value)
> +{
> +       u8 data[2];
> +
> +       data[0] = reg;
> +       data[1] = value;
> +       if (codec->hw_write(codec->control_data, data, 2) == 2)
> +               return 0;
> +       else
> +               return -EIO;
> +}

As previously discussed you should use the soc-cache code here.

> +/*
> + * For kernels compiled without unsigned long long int division
> + */
> +unsigned long long int ulldiv(unsigned long long int dividend,
> +                             unsigned long long int divisor)

This is causing me some concern.  Even if it is required this does not
look like something that should be part of a specific driver - what
happens if some other driver needs the same thing?

> +/*
> + * The INx1 and INx2 PGAs share a power control signal.
> + * This function OR's the two power events to keep an unpowered INx
> + * from turning off it's counterpart.
> + * The control names are used to identify the PGA.
> + */

This...

> +       if (strncmp(w->name, "INA1", 4) == 0) {
> +               pga = INA1_PGA_BIT;
> +               mask = INA1_PGA_BIT | INA2_PGA_BIT;

...doesn't seem to correspond to this, at least with the normal way mask
is used.  Apart from anything else it looks like you have individual
mask bits for each of the INx1 and INx2 PGAs (since you can define mask
bits for each of them).  It would be much clearer if the code made it
obvious that these aren't register bits but rather that you're storing
the data in a register-like bitfield in a variable.  I can't help but
think that a reference count would be much clearer.

> +       if (event == SND_SOC_DAPM_POST_PMU) {
> +               /* ON */
> +               *state |= pga;
> +
> +               /* Turn on, avoiding unnecessary writes */
> +               val = snd_soc_read(codec, w->reg);
> +               if (!(val & (1 << w->shift))) {
> +                       val |= (1 << w->shift);
> +                       snd_soc_write(codec, w->reg, val);
> +               }

snd_soc_update_bits() will suppress unwanted register writes.

> +       }
> +
> +       return;
> +}

No need for return statements at the end of void functions.

> +
> +static const unsigned int ex_mode_table[] = {
> +       0x00,           /* disabled */
> +       (0<<4)|3,       /* 100Hz */
> +       (1<<4)|0,       /* 400Hz */
> +       (2<<4)|0,       /* 600Hz */
> +       (3<<4)|0,       /* 800Hz */
> +       (4<<4)|0,       /* 1000Hz */
> +       (1<<4)|1,       /* 200-400Hz */
> +       (2<<4)|2,       /* 400-600Hz */
> +       (3<<4)|2,       /* 400-800Hz */
> +};

You should add value muxes like we have for DAPM.

> +static const char *max98088_micpre[] = {
> +       "0dB",
> +       "20dB",
> +       "30dB",
> +};

> +static const struct soc_enum max98088_micpre_enum[] = {
> +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_micpre), max98088_micpre),
> +};

This should be a TLV control, not an enum.

> +static const char *max98088_extmic[] = {
> +       "Off",
> +       "MIC1",
> +       "MIC2",
> +};

> +static const struct soc_enum max98088_extmic_enum[] = {
> +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
> +};

This looks like it should be in DAPM.

> +static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
> +                               struct snd_ctl_elem_value *ucontrol)
> +{
> +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
> +       unsigned int *mode = &max98088->mic1pre;
> +       int sel = ucontrol->value.integer.value[0];
> +
> +       if (sel >= ARRAY_SIZE(max98088_micpre))
> +               return -EINVAL;
> +
> +       *mode = ucontrol->value.integer.value[0];
> +       return 0;
> +}

I'd expect that some action would be taken when the value is set here.
All this does is set a variable, changing the control will have no
effect.

> +static int max98088_hp_event(struct snd_soc_dapm_widget *w,
> +                            struct snd_kcontrol *kcontrol, int event)
> +{
> +       struct snd_soc_codec *codec = w->codec;
> +       u16 status;
> +
> +       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
> +
> +       /* powering down headphone gracefully */
> +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> +       if ((status & M98088_HPEN) == M98088_HPEN) {
> +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> +                       (status & ~M98088_HPEN));
> +       }
> +       schedule_timeout(msecs_to_jiffies(20));

This looks rather like it should just be a post event implementing a
timeout?

> +       if (rate != cdata->rate) {
> +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> +               if (rate_value(rate, &regval))
> +                       return -EINVAL;
> +
> +               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
> +               cdata->rate = rate;
> +       }

Just use snd_soc_update_bits() to suppress unneeded writes.  Many of the
operaations have this issue.

> +               & M98088_DAI_MAS) {
> +               if (max98088->sysclk == 0)
> +                       return -EINVAL;

You should print an error here so users can tell what went wrong.

> +               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> +               case SND_SOC_DAIFMT_I2S:
> +                       reg14val |= M98088_DAI_DLY;
> +                       break;
> +               case SND_SOC_DAIFMT_LEFT_J:
> +                       reg14msk |= M98088_DAI_DLY;
> +                       break;

This looks fishy - in one case you set the value, in another format you
set the mask, both to the same bitfield.   I'd expect the mask of bits
being updated to stay constant, but you seem to be treating the mask as
meaning bits to be cleared.  I rather suspect you'll run into problems
if you test this.

> +       case SND_SOC_BIAS_STANDBY:
> +               max98088_sync_cache(codec);
> +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> +                               M98088_MBEN, M98088_MBEN);
> +               break;

Do you really want to sync the cache *every* time you go into standby?

> +
> +       case SND_SOC_BIAS_OFF:
> +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> +                               M98088_MBEN, 0);
> +#ifdef CONFIG_REGULATOR
> +               codec->cache_sync = 1;
> +#endif

Why the ifdef?

> +
> +       /* Build an array of texts for the enum API. The number
> +        * of texts is likely fewer than the number of configurations
> +        * due to multiple sample rates for the same text name. */
> +       cdata->eq_textcnt = 0;
> +       for (i = 0; i < pdata->eq1_cfgcnt; i++) {

It might be nice to give credit to the drivers you've based your work on :)

> +       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
> +
> +       /* Sync reg_cache with the hardware */
> +       for (i = 0; i < M98088_REG_CNT; i++) {
> +               if (i == M98088_REG_51_PWR_SYS)
> +                       continue;
> +
> +               if (!max98088_access[i].writable)
> +                       continue;
> +
> +               max98088_hw_write(codec, i, cache[i]);
> +       }

This appears to duplicate the register sync in your bias management.

> +static struct i2c_driver max98088_i2c_driver = {
> +       .driver = {
> +               .name = "max98088-codec",

Drop the -codec from the name.

> +module_init(max98088_init);

Normally this would be next to the function it references.

> +struct max98088_setup_data {
> +       unsigned short i2c_address;
> +};

This should be removed.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-29  3:37   ` Mark Brown
@ 2010-09-29 21:42     ` Peter Hsiang
  -1 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-29 21:42 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Sep 29, 2010, Mark Brown wrote:
> On Tue, Sep 28, 2010 at 07:34:34PM -0700, Peter Hsiang wrote:
> 
> > +#define EQ_CFG_MAX 32
> 
> There doesn't seem to be any need to hard code a limit here?

Are you requiring the implementation be exactly the same way you did?

> 
> > +/*
> > + * For kernels compiled without unsigned long long int division
> > + */
> > +unsigned long long int ulldiv(unsigned long long int dividend,
> > +                             unsigned long long int divisor)
> 
> This is causing me some concern.  Even if it is required this does not
> look like something that should be part of a specific driver - what
> happens if some other driver needs the same thing?

You are more than welcome to use this in your driver as well :)
Just use it.  Sounds like you are suggesting to have it located in a
central location outside of the codec driver?
Would you like that in soc-core.c?  Let me know.

> 
> > +/*
> > + * The INx1 and INx2 PGAs share a power control signal.
> > + * This function OR's the two power events to keep an unpowered INx
> > + * from turning off it's counterpart.
> > + * The control names are used to identify the PGA.
> > + */
> 
> This...
> 
> > +       if (strncmp(w->name, "INA1", 4) == 0) {
> > +               pga = INA1_PGA_BIT;
> > +               mask = INA1_PGA_BIT | INA2_PGA_BIT;
> 
> ...doesn't seem to correspond to this, at least with the normal way mask
> is used.  Apart from anything else it looks like you have individual
> mask bits for each of the INx1 and INx2 PGAs (since you can define mask
> bits for each of them).  It would be much clearer if the code made it
> obvious that these aren't register bits but rather that you're storing
> the data in a register-like bitfield in a variable.  I can't help but
> think that a reference count would be much clearer.

There are more than one way to solve a problem, and they all can be
correct.
Would additional comments to clarify that this is not register bits
satisfy your requirement?

> 
> > +       if (event == SND_SOC_DAPM_POST_PMU) {
> > +               /* ON */
> > +               *state |= pga;
> > +
> > +               /* Turn on, avoiding unnecessary writes */
> > +               val = snd_soc_read(codec, w->reg);
> > +               if (!(val & (1 << w->shift))) {
> > +                       val |= (1 << w->shift);
> > +                       snd_soc_write(codec, w->reg, val);
> > +               }
> 
> snd_soc_update_bits() will suppress unwanted register writes.

>From the comments sounds like it resemble snd_soc_update_bits.
The code logic here is different from snd_soc_update_bits,
and therefore snd_soc_update_bits can not be used for this
like you suggested.

> > +
> > +static const unsigned int ex_mode_table[] = {
> > +       0x00,           /* disabled */
> > +       (0<<4)|3,       /* 100Hz */
> > +       (1<<4)|0,       /* 400Hz */
> > +       (2<<4)|0,       /* 600Hz */
> > +       (3<<4)|0,       /* 800Hz */
> > +       (4<<4)|0,       /* 1000Hz */
> > +       (1<<4)|1,       /* 200-400Hz */
> > +       (2<<4)|2,       /* 400-600Hz */
> > +       (3<<4)|2,       /* 400-800Hz */
> > +};
> 
> You should add value muxes like we have for DAPM.

Please clarify what you mean by referencing the specific 
code usage case in the dapm source module.

> 
> > +static const char *max98088_extmic[] = {
> > +       "Off",
> > +       "MIC1",
> > +       "MIC2",
> > +};
> 
> > +static const struct soc_enum max98088_extmic_enum[] = {
> > +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
> > +};
> 
> This looks like it should be in DAPM.

This has nothing to do with power management.  
I guess you were confused by the word "Off" in the options list.
The "Off" case means the input for external mic is not used,
i.e. disconnected.  It does not power up or down anything.

> 
> > +static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
> > +                               struct snd_ctl_elem_value *ucontrol)
> > +{
> > +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> > +       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
> > +       unsigned int *mode = &max98088->mic1pre;
> > +       int sel = ucontrol->value.integer.value[0];
> > +
> > +       if (sel >= ARRAY_SIZE(max98088_micpre))
> > +               return -EINVAL;
> > +
> > +       *mode = ucontrol->value.integer.value[0];
> > +       return 0;
> > +}
> 
> I'd expect that some action would be taken when the value is set here.
> All this does is set a variable, changing the control will have no
> effect.

The register field works like this:
00 = preamp disabled
01 = 0dB
10 = 20dB
11 = 30dB
So the gain and the power setting are in the same bit field.
DAPM owns the power control.  Here the user settings are stored,
and they get picked up by DAPM.

> 
> > +static int max98088_hp_event(struct snd_soc_dapm_widget *w,
> > +                            struct snd_kcontrol *kcontrol, int event)
> > +{
> > +       struct snd_soc_codec *codec = w->codec;
> > +       u16 status;
> > +
> > +       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
> > +
> > +       /* powering down headphone gracefully */
> > +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > +                       (status & ~M98088_HPEN));
> > +       }
> > +       schedule_timeout(msecs_to_jiffies(20));
> 
> This looks rather like it should just be a post event implementing a
> timeout?

This needs to work as a pre event.

> 
> > +       if (rate != cdata->rate) {
> > +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> > +               if (rate_value(rate, &regval))
> > +                       return -EINVAL;
> > +
> > +               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
> > +               cdata->rate = rate;
> > +       }
> 
> Just use snd_soc_update_bits() to suppress unneeded writes.  Many of the
> operaations have this issue.

The full value for this register is accounted for here and so there
is no need to use snd_soc_update_bits for this case.

> 
> > +               & M98088_DAI_MAS) {
> > +               if (max98088->sysclk == 0)
> > +                       return -EINVAL;
> 
> You should print an error here so users can tell what went wrong.

Yes thank you.

> 
> > +       case SND_SOC_BIAS_STANDBY:
> > +               max98088_sync_cache(codec);
> > +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> > +                               M98088_MBEN, M98088_MBEN);
> > +               break;
> 
> Do you really want to sync the cache *every* time you go into standby?

The sync_cache function itself will just return if the
codec->cache_sync flag is cleared from the first time it ran.
You do the exact same thing in your codec driver...
What is the change that you are suggesting?

> 
> > +module_init(max98088_init);
> 
> Normally this would be next to the function it references.

Is this a new formatting style of the kernel now all across,
or is this a personal preference?
Not a problem, I can change it.  Just that I find a huge number 
of drivers in the kernel having the module_init and module_exit 
together at the very end.


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-29 21:42     ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-29 21:42 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Jesse, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Marroquin, Liam, Girdwood

On Wed, Sep 29, 2010, Mark Brown wrote:
> On Tue, Sep 28, 2010 at 07:34:34PM -0700, Peter Hsiang wrote:
> 
> > +#define EQ_CFG_MAX 32
> 
> There doesn't seem to be any need to hard code a limit here?

Are you requiring the implementation be exactly the same way you did?

> 
> > +/*
> > + * For kernels compiled without unsigned long long int division
> > + */
> > +unsigned long long int ulldiv(unsigned long long int dividend,
> > +                             unsigned long long int divisor)
> 
> This is causing me some concern.  Even if it is required this does not
> look like something that should be part of a specific driver - what
> happens if some other driver needs the same thing?

You are more than welcome to use this in your driver as well :)
Just use it.  Sounds like you are suggesting to have it located in a
central location outside of the codec driver?
Would you like that in soc-core.c?  Let me know.

> 
> > +/*
> > + * The INx1 and INx2 PGAs share a power control signal.
> > + * This function OR's the two power events to keep an unpowered INx
> > + * from turning off it's counterpart.
> > + * The control names are used to identify the PGA.
> > + */
> 
> This...
> 
> > +       if (strncmp(w->name, "INA1", 4) == 0) {
> > +               pga = INA1_PGA_BIT;
> > +               mask = INA1_PGA_BIT | INA2_PGA_BIT;
> 
> ...doesn't seem to correspond to this, at least with the normal way mask
> is used.  Apart from anything else it looks like you have individual
> mask bits for each of the INx1 and INx2 PGAs (since you can define mask
> bits for each of them).  It would be much clearer if the code made it
> obvious that these aren't register bits but rather that you're storing
> the data in a register-like bitfield in a variable.  I can't help but
> think that a reference count would be much clearer.

There are more than one way to solve a problem, and they all can be
correct.
Would additional comments to clarify that this is not register bits
satisfy your requirement?

> 
> > +       if (event == SND_SOC_DAPM_POST_PMU) {
> > +               /* ON */
> > +               *state |= pga;
> > +
> > +               /* Turn on, avoiding unnecessary writes */
> > +               val = snd_soc_read(codec, w->reg);
> > +               if (!(val & (1 << w->shift))) {
> > +                       val |= (1 << w->shift);
> > +                       snd_soc_write(codec, w->reg, val);
> > +               }
> 
> snd_soc_update_bits() will suppress unwanted register writes.

>From the comments sounds like it resemble snd_soc_update_bits.
The code logic here is different from snd_soc_update_bits,
and therefore snd_soc_update_bits can not be used for this
like you suggested.

> > +
> > +static const unsigned int ex_mode_table[] = {
> > +       0x00,           /* disabled */
> > +       (0<<4)|3,       /* 100Hz */
> > +       (1<<4)|0,       /* 400Hz */
> > +       (2<<4)|0,       /* 600Hz */
> > +       (3<<4)|0,       /* 800Hz */
> > +       (4<<4)|0,       /* 1000Hz */
> > +       (1<<4)|1,       /* 200-400Hz */
> > +       (2<<4)|2,       /* 400-600Hz */
> > +       (3<<4)|2,       /* 400-800Hz */
> > +};
> 
> You should add value muxes like we have for DAPM.

Please clarify what you mean by referencing the specific 
code usage case in the dapm source module.

> 
> > +static const char *max98088_extmic[] = {
> > +       "Off",
> > +       "MIC1",
> > +       "MIC2",
> > +};
> 
> > +static const struct soc_enum max98088_extmic_enum[] = {
> > +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
> > +};
> 
> This looks like it should be in DAPM.

This has nothing to do with power management.  
I guess you were confused by the word "Off" in the options list.
The "Off" case means the input for external mic is not used,
i.e. disconnected.  It does not power up or down anything.

> 
> > +static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
> > +                               struct snd_ctl_elem_value *ucontrol)
> > +{
> > +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> > +       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
> > +       unsigned int *mode = &max98088->mic1pre;
> > +       int sel = ucontrol->value.integer.value[0];
> > +
> > +       if (sel >= ARRAY_SIZE(max98088_micpre))
> > +               return -EINVAL;
> > +
> > +       *mode = ucontrol->value.integer.value[0];
> > +       return 0;
> > +}
> 
> I'd expect that some action would be taken when the value is set here.
> All this does is set a variable, changing the control will have no
> effect.

The register field works like this:
00 = preamp disabled
01 = 0dB
10 = 20dB
11 = 30dB
So the gain and the power setting are in the same bit field.
DAPM owns the power control.  Here the user settings are stored,
and they get picked up by DAPM.

> 
> > +static int max98088_hp_event(struct snd_soc_dapm_widget *w,
> > +                            struct snd_kcontrol *kcontrol, int event)
> > +{
> > +       struct snd_soc_codec *codec = w->codec;
> > +       u16 status;
> > +
> > +       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
> > +
> > +       /* powering down headphone gracefully */
> > +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > +                       (status & ~M98088_HPEN));
> > +       }
> > +       schedule_timeout(msecs_to_jiffies(20));
> 
> This looks rather like it should just be a post event implementing a
> timeout?

This needs to work as a pre event.

> 
> > +       if (rate != cdata->rate) {
> > +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> > +               if (rate_value(rate, &regval))
> > +                       return -EINVAL;
> > +
> > +               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
> > +               cdata->rate = rate;
> > +       }
> 
> Just use snd_soc_update_bits() to suppress unneeded writes.  Many of the
> operaations have this issue.

The full value for this register is accounted for here and so there
is no need to use snd_soc_update_bits for this case.

> 
> > +               & M98088_DAI_MAS) {
> > +               if (max98088->sysclk == 0)
> > +                       return -EINVAL;
> 
> You should print an error here so users can tell what went wrong.

Yes thank you.

> 
> > +       case SND_SOC_BIAS_STANDBY:
> > +               max98088_sync_cache(codec);
> > +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> > +                               M98088_MBEN, M98088_MBEN);
> > +               break;
> 
> Do you really want to sync the cache *every* time you go into standby?

The sync_cache function itself will just return if the
codec->cache_sync flag is cleared from the first time it ran.
You do the exact same thing in your codec driver...
What is the change that you are suggesting?

> 
> > +module_init(max98088_init);
> 
> Normally this would be next to the function it references.

Is this a new formatting style of the kernel now all across,
or is this a personal preference?
Not a problem, I can change it.  Just that I find a huge number 
of drivers in the kernel having the module_init and module_exit 
together at the very end.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-29 21:42     ` Peter Hsiang
@ 2010-09-29 22:18       ` Mark Brown
  -1 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-29 22:18 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Sep 29, 2010 at 02:42:32PM -0700, Peter Hsiang wrote:
> On Wed, Sep 29, 2010, Mark Brown wrote:
> > On Tue, Sep 28, 2010 at 07:34:34PM -0700, Peter Hsiang wrote:

> > > +#define EQ_CFG_MAX 32

> > There doesn't seem to be any need to hard code a limit here?

> Are you requiring the implementation be exactly the same way you did?

It doesn't seem very good to hard code a limit when we already know how
to figure this out at runtime, especially given the amount of dynamic
allocation you already need for the strings themselves.

> > > +/*
> > > + * For kernels compiled without unsigned long long int division
> > > + */
> > > +unsigned long long int ulldiv(unsigned long long int dividend,
> > > +                             unsigned long long int divisor)

> > This is causing me some concern.  Even if it is required this does not
> > look like something that should be part of a specific driver - what
> > happens if some other driver needs the same thing?

> You are more than welcome to use this in your driver as well :)
> Just use it.  Sounds like you are suggesting to have it located in a
> central location outside of the codec driver?
> Would you like that in soc-core.c?  Let me know.

If it is needed it should be a core kernel feature as there is nothing
ASoC specific about doing a division, but you've not explained why it is
required in the first place - are you sure you're not looking for
do_div()?

> There are more than one way to solve a problem, and they all can be
> correct.
> Would additional comments to clarify that this is not register bits
> satisfy your requirement?

Like I say the issue is that the code is *very* opaque and non-idiomatic.

> > > +       if (event == SND_SOC_DAPM_POST_PMU) {
> > > +               /* ON */
> > > +               *state |= pga;
> > > +
> > > +               /* Turn on, avoiding unnecessary writes */
> > > +               val = snd_soc_read(codec, w->reg);
> > > +               if (!(val & (1 << w->shift))) {
> > > +                       val |= (1 << w->shift);
> > > +                       snd_soc_write(codec, w->reg, val);
> > > +               }

> > snd_soc_update_bits() will suppress unwanted register writes.

> From the comments sounds like it resemble snd_soc_update_bits.
> The code logic here is different from snd_soc_update_bits,
> and therefore snd_soc_update_bits can not be used for this
> like you suggested.

What makes you say this (and as with several other things here if things
aren't clear from the code then you should make it clear in the code)?

> > You should add value muxes like we have for DAPM.

> Please clarify what you mean by referencing the specific 
> code usage case in the dapm source module.

You're looking for the non-DAPM equivalent of SND_SOC_DAPM_VALUE_MUX().

> > > +static const char *max98088_extmic[] = {
> > > +       "Off",
> > > +       "MIC1",
> > > +       "MIC2",
> > > +};

> > > +static const struct soc_enum max98088_extmic_enum[] = {
> > > +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
> > > +};

> > This looks like it should be in DAPM.

> This has nothing to do with power management.  
> I guess you were confused by the word "Off" in the options list.
> The "Off" case means the input for external mic is not used,
> i.e. disconnected.  It does not power up or down anything.

This is exactly my point - DAPM doesn't just manage power, it also
manages the audio routing (and the power as a function of that).
Remember that the DAPM map extends out of the CODEC so even if the CODEC
does not offer any meaningful power management within the device there
may be other external circuits which do (microphone biases being the
obvious example here).

> > > +       *mode = ucontrol->value.integer.value[0];
> > > +       return 0;
> > > +}
> > 

> > All this does is set a variable, changing the control will have no
> > effect.

> The register field works like this:
> 00 = preamp disabled
> 01 = 0dB
> 10 = 20dB
> 11 = 30dB
> So the gain and the power setting are in the same bit field.
> DAPM owns the power control.  Here the user settings are stored,
> and they get picked up by DAPM.

This does not address my comment at all.  Please address my comment - if
the user sets a value here one would expect that to have some immediate
effect on the operation of the device but all this does is store the
setting in a variable but there will be no immediate effect on the
operation of the device and there is nothing restricting when the value
can be set.  This means that the control state that the user sees can
vary from the actual device configuration.

> > > +       /* powering down headphone gracefully */
> > > +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > > +                       (status & ~M98088_HPEN));
> > > +       }
> > > +       schedule_timeout(msecs_to_jiffies(20));

> > This looks rather like it should just be a post event implementing a
> > timeout?

> This needs to work as a pre event.

Again, why is this?

> > > +       if (rate != cdata->rate) {
> > > +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> > > +               if (rate_value(rate, &regval))
> > > +                       return -EINVAL;
> > > +
> > > +               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
> > > +               cdata->rate = rate;
> > > +       }

> > Just use snd_soc_update_bits() to suppress unneeded writes.  Many of the
> > operaations have this issue.

> The full value for this register is accounted for here and so there
> is no need to use snd_soc_update_bits for this case.

The "to suppress unneeded writes" bit of my comment is important here -
removing the conditional statements would make the code easier to read.

> > > +       case SND_SOC_BIAS_STANDBY:
> > > +               max98088_sync_cache(codec);
> > > +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> > > +                               M98088_MBEN, M98088_MBEN);
> > > +               break;

> > Do you really want to sync the cache *every* time you go into standby?

> The sync_cache function itself will just return if the
> codec->cache_sync flag is cleared from the first time it ran.
> You do the exact same thing in your codec driver...
> What is the change that you are suggesting?

The cache syncs should be part of some operation which would make it
useful to sync the cache rather than just located at some point in the
driver without any particular reason.  For example, with the drivers
I've worked on the cache is synced after we enable the supplies for the
device since the CODEC may have been powered off and therefore lost any
register settings that might have been done.  If the cache sync is not
associated with any such event then it's at best redundant and at worst
the driver will loose some robustness since it becomes unclear if the
events which cause a cache sync to be required are joined up with the
triggering of a sync.

> > > +module_init(max98088_init);

> > Normally this would be next to the function it references.

> Is this a new formatting style of the kernel now all across,
> or is this a personal preference?

It's a global style for the kernel, though not enforced with 100%
success.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-29 22:18       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-29 22:18 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Jesse Marroquin, Liam Girdwood

On Wed, Sep 29, 2010 at 02:42:32PM -0700, Peter Hsiang wrote:
> On Wed, Sep 29, 2010, Mark Brown wrote:
> > On Tue, Sep 28, 2010 at 07:34:34PM -0700, Peter Hsiang wrote:

> > > +#define EQ_CFG_MAX 32

> > There doesn't seem to be any need to hard code a limit here?

> Are you requiring the implementation be exactly the same way you did?

It doesn't seem very good to hard code a limit when we already know how
to figure this out at runtime, especially given the amount of dynamic
allocation you already need for the strings themselves.

> > > +/*
> > > + * For kernels compiled without unsigned long long int division
> > > + */
> > > +unsigned long long int ulldiv(unsigned long long int dividend,
> > > +                             unsigned long long int divisor)

> > This is causing me some concern.  Even if it is required this does not
> > look like something that should be part of a specific driver - what
> > happens if some other driver needs the same thing?

> You are more than welcome to use this in your driver as well :)
> Just use it.  Sounds like you are suggesting to have it located in a
> central location outside of the codec driver?
> Would you like that in soc-core.c?  Let me know.

If it is needed it should be a core kernel feature as there is nothing
ASoC specific about doing a division, but you've not explained why it is
required in the first place - are you sure you're not looking for
do_div()?

> There are more than one way to solve a problem, and they all can be
> correct.
> Would additional comments to clarify that this is not register bits
> satisfy your requirement?

Like I say the issue is that the code is *very* opaque and non-idiomatic.

> > > +       if (event == SND_SOC_DAPM_POST_PMU) {
> > > +               /* ON */
> > > +               *state |= pga;
> > > +
> > > +               /* Turn on, avoiding unnecessary writes */
> > > +               val = snd_soc_read(codec, w->reg);
> > > +               if (!(val & (1 << w->shift))) {
> > > +                       val |= (1 << w->shift);
> > > +                       snd_soc_write(codec, w->reg, val);
> > > +               }

> > snd_soc_update_bits() will suppress unwanted register writes.

> From the comments sounds like it resemble snd_soc_update_bits.
> The code logic here is different from snd_soc_update_bits,
> and therefore snd_soc_update_bits can not be used for this
> like you suggested.

What makes you say this (and as with several other things here if things
aren't clear from the code then you should make it clear in the code)?

> > You should add value muxes like we have for DAPM.

> Please clarify what you mean by referencing the specific 
> code usage case in the dapm source module.

You're looking for the non-DAPM equivalent of SND_SOC_DAPM_VALUE_MUX().

> > > +static const char *max98088_extmic[] = {
> > > +       "Off",
> > > +       "MIC1",
> > > +       "MIC2",
> > > +};

> > > +static const struct soc_enum max98088_extmic_enum[] = {
> > > +       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
> > > +};

> > This looks like it should be in DAPM.

> This has nothing to do with power management.  
> I guess you were confused by the word "Off" in the options list.
> The "Off" case means the input for external mic is not used,
> i.e. disconnected.  It does not power up or down anything.

This is exactly my point - DAPM doesn't just manage power, it also
manages the audio routing (and the power as a function of that).
Remember that the DAPM map extends out of the CODEC so even if the CODEC
does not offer any meaningful power management within the device there
may be other external circuits which do (microphone biases being the
obvious example here).

> > > +       *mode = ucontrol->value.integer.value[0];
> > > +       return 0;
> > > +}
> > 

> > All this does is set a variable, changing the control will have no
> > effect.

> The register field works like this:
> 00 = preamp disabled
> 01 = 0dB
> 10 = 20dB
> 11 = 30dB
> So the gain and the power setting are in the same bit field.
> DAPM owns the power control.  Here the user settings are stored,
> and they get picked up by DAPM.

This does not address my comment at all.  Please address my comment - if
the user sets a value here one would expect that to have some immediate
effect on the operation of the device but all this does is store the
setting in a variable but there will be no immediate effect on the
operation of the device and there is nothing restricting when the value
can be set.  This means that the control state that the user sees can
vary from the actual device configuration.

> > > +       /* powering down headphone gracefully */
> > > +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > > +                       (status & ~M98088_HPEN));
> > > +       }
> > > +       schedule_timeout(msecs_to_jiffies(20));

> > This looks rather like it should just be a post event implementing a
> > timeout?

> This needs to work as a pre event.

Again, why is this?

> > > +       if (rate != cdata->rate) {
> > > +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> > > +               if (rate_value(rate, &regval))
> > > +                       return -EINVAL;
> > > +
> > > +               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
> > > +               cdata->rate = rate;
> > > +       }

> > Just use snd_soc_update_bits() to suppress unneeded writes.  Many of the
> > operaations have this issue.

> The full value for this register is accounted for here and so there
> is no need to use snd_soc_update_bits for this case.

The "to suppress unneeded writes" bit of my comment is important here -
removing the conditional statements would make the code easier to read.

> > > +       case SND_SOC_BIAS_STANDBY:
> > > +               max98088_sync_cache(codec);
> > > +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> > > +                               M98088_MBEN, M98088_MBEN);
> > > +               break;

> > Do you really want to sync the cache *every* time you go into standby?

> The sync_cache function itself will just return if the
> codec->cache_sync flag is cleared from the first time it ran.
> You do the exact same thing in your codec driver...
> What is the change that you are suggesting?

The cache syncs should be part of some operation which would make it
useful to sync the cache rather than just located at some point in the
driver without any particular reason.  For example, with the drivers
I've worked on the cache is synced after we enable the supplies for the
device since the CODEC may have been powered off and therefore lost any
register settings that might have been done.  If the cache sync is not
associated with any such event then it's at best redundant and at worst
the driver will loose some robustness since it becomes unclear if the
events which cause a cache sync to be required are joined up with the
triggering of a sync.

> > > +module_init(max98088_init);

> > Normally this would be next to the function it references.

> Is this a new formatting style of the kernel now all across,
> or is this a personal preference?

It's a global style for the kernel, though not enforced with 100%
success.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-29 22:18       ` Mark Brown
  (?)
@ 2010-09-30  0:52       ` Peter Hsiang
  2010-09-30  0:58           ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-09-30  0:52 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Sep 29, 2010, Mark Brown wrote:
> On Wed, Sep 29, 2010 at 02:42:32PM -0700, Peter Hsiang wrote:
> > On Wed, Sep 29, 2010, Mark Brown wrote:
> > > On Tue, Sep 28, 2010 at 07:34:34PM -0700, Peter Hsiang wrote:
> 
> > > You should add value muxes like we have for DAPM.
> 
> > Please clarify what you mean by referencing the specific
> > code usage case in the dapm source module.
> 
> You're looking for the non-DAPM equivalent of SND_SOC_DAPM_VALUE_MUX().

This is a simple table lookup of a register value from the index
number given by SOC_ENUM, the same way it's been done in other drivers.

I found a "case snd_soc_dapm_value_mux:" in dapm_set_path_status()
Is this what you are referring to?
How is the code there relevant to this?

> 
> > > > +       /* powering down headphone gracefully */
> > > > +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > > > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > > > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > > > +                       (status & ~M98088_HPEN));
> > > > +       }
> > > > +       schedule_timeout(msecs_to_jiffies(20));
> 
> > > This looks rather like it should just be a post event implementing a
> > > timeout?
> 
> > This needs to work as a pre event.
> 
> Again, why is this?

When powering down the headphone, the way that DAPM works is it
likes to power off one item at a time, for example, the left channel,
then right channel.  The headphone hardware likes to see the 
headphone bits L and R be powered down together, for optimum result.
This works best with the pre method.  Powering up one channel at a 
time later is fine, when DAPM resumes.

> 
> > > > +       case SND_SOC_BIAS_STANDBY:
> > > > +               max98088_sync_cache(codec);
> > > > +               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
> > > > +                               M98088_MBEN, M98088_MBEN);
> > > > +               break;
> 
> > > Do you really want to sync the cache *every* time you go into standby?
> 
> > The sync_cache function itself will just return if the
> > codec->cache_sync flag is cleared from the first time it ran.
> > You do the exact same thing in your codec driver...
> > What is the change that you are suggesting?
> 
> The cache syncs should be part of some operation which would make it
> useful to sync the cache rather than just located at some point in the
> driver without any particular reason.  For example, with the drivers
> I've worked on the cache is synced after we enable the supplies for the
> device since the CODEC may have been powered off and therefore lost any
> register settings that might have been done.  If the cache sync is not
> associated with any such event then it's at best redundant and at worst
> the driver will loose some robustness since it becomes unclear if the
> events which cause a cache sync to be required are joined up with the
> triggering of a sync.

I see :)

> 
> > > > +module_init(max98088_init);
> 
> > > Normally this would be next to the function it references.
> 
> > Is this a new formatting style of the kernel now all across,
> > or is this a personal preference?
> 
> It's a global style for the kernel, though not enforced with 100%
> success.

Haha, got it. I will help you with this statistic from now on.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-30  0:52       ` Peter Hsiang
@ 2010-09-30  0:58           ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-30  0:58 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Sep 29, 2010 at 05:52:08PM -0700, Peter Hsiang wrote:
> On Wed, Sep 29, 2010, Mark Brown wrote:

> > You're looking for the non-DAPM equivalent of SND_SOC_DAPM_VALUE_MUX().

> This is a simple table lookup of a register value from the index
> number given by SOC_ENUM, the same way it's been done in other drivers.

Yes, exactly.

> I found a "case snd_soc_dapm_value_mux:" in dapm_set_path_status()
> Is this what you are referring to?
> How is the code there relevant to this?

No, I'm referring to the fact that it provides an enum that allows
arbatrary values to be set for each enum value rather than just allowing
indexes as the standard SOC_ENUM does.  This is essentially what you've
implemented, it would factor out the code so that others can use it too
(and IIRC save you some code since IIRC you had more than one of these
in the driver).

> > > > > +       /* powering down headphone gracefully */
> > > > > +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > > > > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > > > > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > > > > +                       (status & ~M98088_HPEN));
> > > > > +       }
> > > > > +       schedule_timeout(msecs_to_jiffies(20));

> > > > This looks rather like it should just be a post event implementing a
> > > > timeout?

> > > This needs to work as a pre event.

> > Again, why is this?

> When powering down the headphone, the way that DAPM works is it
> likes to power off one item at a time, for example, the left channel,
> then right channel.  The headphone hardware likes to see the 

Have you tested with current Linux versions?  For quite a few kernel
releases now register writes are batched so that all the controls of a
given type in a single register will be set together.

> headphone bits L and R be powered down together, for optimum result.
> This works best with the pre method.  Powering up one channel at a 
> time later is fine, when DAPM resumes.

With current Linux versions you should see the left and right channels
powered down together (if they are both being powered down, of course).

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-30  0:58           ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-30  0:58 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Jesse Marroquin, Liam Girdwood

On Wed, Sep 29, 2010 at 05:52:08PM -0700, Peter Hsiang wrote:
> On Wed, Sep 29, 2010, Mark Brown wrote:

> > You're looking for the non-DAPM equivalent of SND_SOC_DAPM_VALUE_MUX().

> This is a simple table lookup of a register value from the index
> number given by SOC_ENUM, the same way it's been done in other drivers.

Yes, exactly.

> I found a "case snd_soc_dapm_value_mux:" in dapm_set_path_status()
> Is this what you are referring to?
> How is the code there relevant to this?

No, I'm referring to the fact that it provides an enum that allows
arbatrary values to be set for each enum value rather than just allowing
indexes as the standard SOC_ENUM does.  This is essentially what you've
implemented, it would factor out the code so that others can use it too
(and IIRC save you some code since IIRC you had more than one of these
in the driver).

> > > > > +       /* powering down headphone gracefully */
> > > > > +       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > > > > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > > > > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > > > > +                       (status & ~M98088_HPEN));
> > > > > +       }
> > > > > +       schedule_timeout(msecs_to_jiffies(20));

> > > > This looks rather like it should just be a post event implementing a
> > > > timeout?

> > > This needs to work as a pre event.

> > Again, why is this?

> When powering down the headphone, the way that DAPM works is it
> likes to power off one item at a time, for example, the left channel,
> then right channel.  The headphone hardware likes to see the 

Have you tested with current Linux versions?  For quite a few kernel
releases now register writes are batched so that all the controls of a
given type in a single register will be set together.

> headphone bits L and R be powered down together, for optimum result.
> This works best with the pre method.  Powering up one channel at a 
> time later is fine, when DAPM resumes.

With current Linux versions you should see the left and right channels
powered down together (if they are both being powered down, of course).

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-30  0:58           ` Mark Brown
  (?)
@ 2010-09-30  1:20           ` Peter Hsiang
  -1 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-30  1:20 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Sep 29, 2010, Mark Brown wrote:
> On Wed, Sep 29, 2010 at 05:52:08PM -0700, Peter Hsiang wrote:
> > On Wed, Sep 29, 2010, Mark Brown wrote:
> 
> > > You're looking for the non-DAPM equivalent of SND_SOC_DAPM_VALUE_MUX().
> 
> > This is a simple table lookup of a register value from the index
> > number given by SOC_ENUM, the same way it's been done in other drivers.
> 
> Yes, exactly.
> 
> > I found a "case snd_soc_dapm_value_mux:" in dapm_set_path_status()
> > Is this what you are referring to?
> > How is the code there relevant to this?
> 
> No, I'm referring to the fact that it provides an enum that allows
> arbatrary values to be set for each enum value rather than just allowing
> indexes as the standard SOC_ENUM does.  This is essentially what you've
> implemented, it would factor out the code so that others can use it too
> (and IIRC save you some code since IIRC you had more than one of these
> in the driver).

Thank you!


^ permalink raw reply	[flat|nested] 81+ messages in thread

* user space control app driver interface for sound soc
  2010-09-30  0:58           ` Mark Brown
  (?)
  (?)
@ 2010-09-30 17:23           ` Peter Hsiang
  2010-09-30 20:31             ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-09-30 17:23 UTC (permalink / raw)
  To: Mark Brown, alsa-devel


If I want to build an audio user space app to perform things like
transferring large blocks of data to the sound soc codec
using I2C, what is the best kernel driver interface to use 
for this purpose?  Add file interface (read/write/ioctl) in the
sound soc codec driver, or does ALSA already have this?
The soc codec driver needs to co-exist with this mechanism.

Thanks,

Peter

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-09-30 17:23           ` user space control app driver interface for sound soc Peter Hsiang
@ 2010-09-30 20:31             ` Mark Brown
  2010-09-30 21:55               ` Peter Hsiang
  0 siblings, 1 reply; 81+ messages in thread
From: Mark Brown @ 2010-09-30 20:31 UTC (permalink / raw)
  To: Peter Hsiang; +Cc: alsa-devel

On Thu, Sep 30, 2010 at 10:23:54AM -0700, Peter Hsiang wrote:

> If I want to build an audio user space app to perform things like
> transferring large blocks of data to the sound soc codec
> using I2C, what is the best kernel driver interface to use 
> for this purpose?  Add file interface (read/write/ioctl) in the
> sound soc codec driver, or does ALSA already have this?
> The soc codec driver needs to co-exist with this mechanism.

This depends on what's idiomatic for the data you need to download -
what is the data?

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-09-30 20:31             ` Mark Brown
@ 2010-09-30 21:55               ` Peter Hsiang
  2010-09-30 22:09                 ` Mark Brown
  0 siblings, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-09-30 21:55 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel

On Thu, Sep 30, 2010, Mark Brown wrote:
> On Thu, Sep 30, 2010 at 10:23:54AM -0700, Peter Hsiang wrote:
> 
> > If I want to build an audio user space app to perform things like
> > transferring large blocks of data to the sound soc codec
> > using I2C, what is the best kernel driver interface to use
> > for this purpose?  Add file interface (read/write/ioctl) in the
> > sound soc codec driver, or does ALSA already have this?
> > The soc codec driver needs to co-exist with this mechanism.
> 
> This depends on what's idiomatic for the data you need to download -
> what is the data?

The data could be filter coefficients, or maybe even proprietary 
binary code image that goes into the chip's memory.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-09-30 21:55               ` Peter Hsiang
@ 2010-09-30 22:09                 ` Mark Brown
  2010-09-30 23:10                   ` Peter Hsiang
  0 siblings, 1 reply; 81+ messages in thread
From: Mark Brown @ 2010-09-30 22:09 UTC (permalink / raw)
  To: Peter Hsiang; +Cc: alsa-devel

On Thu, Sep 30, 2010 at 02:55:39PM -0700, Peter Hsiang wrote:
> On Thu, Sep 30, 2010, Mark Brown wrote:

> > This depends on what's idiomatic for the data you need to download -
> > what is the data?

> The data could be filter coefficients, or maybe even proprietary 

For these it depends; frequently these are (like your existing EQ
configuration) included in platform data but it really depends on how
they are generated and if they are custom to a particular system (eg,
dependant on plastics or particular speakers).

> binary code image that goes into the chip's memory.

For this use request_firmware() and associated interfaces.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-09-30 22:09                 ` Mark Brown
@ 2010-09-30 23:10                   ` Peter Hsiang
  2010-09-30 23:34                     ` Mark Brown
  0 siblings, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-09-30 23:10 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel

On Thu, Sep 30, 2010, Mark Brown wrote:
> On Thu, Sep 30, 2010 at 02:55:39PM -0700, Peter Hsiang wrote:
> > On Thu, Sep 30, 2010, Mark Brown wrote:
> 
> > > This depends on what's idiomatic for the data you need to download -
> > > what is the data?
> 
> > The data could be filter coefficients, or maybe even proprietary
> 
> For these it depends; frequently these are (like your existing EQ
> configuration) included in platform data but it really depends on how
> they are generated and if they are custom to a particular system (eg,
> dependant on plastics or particular speakers).

In addition to the platform data, I would like in the future be
able to load in data values that are stored in or generated from the
user space.  Would I use the ALSA interface for this, or would it
need to be a separate interface?

> 
> > binary code image that goes into the chip's memory.
> 
> For this use request_firmware() and associated interfaces.

Is it more common to load the firmware from the kernel space?
Is there an established mechanism for loading from user space?
Thanks.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-09-30 23:10                   ` Peter Hsiang
@ 2010-09-30 23:34                     ` Mark Brown
  2010-10-01  1:56                       ` Peter Hsiang
  0 siblings, 1 reply; 81+ messages in thread
From: Mark Brown @ 2010-09-30 23:34 UTC (permalink / raw)
  To: Peter Hsiang; +Cc: alsa-devel

On Thu, Sep 30, 2010 at 04:10:15PM -0700, Peter Hsiang wrote:
> On Thu, Sep 30, 2010, Mark Brown wrote:

> > For these it depends; frequently these are (like your existing EQ
> > configuration) included in platform data but it really depends on how
> > they are generated and if they are custom to a particular system (eg,
> > dependant on plastics or particular speakers).

> In addition to the platform data, I would like in the future be
> able to load in data values that are stored in or generated from the
> user space.  Would I use the ALSA interface for this, or would it
> need to be a separate interface?

This would not be idiomatic for ALSA.  The particular interface to use
would depend on the volume of data being configured and the format it is
provided in.  If this is just for test and development purposes then
adding something to debugfs would be the normal thing.

> > > binary code image that goes into the chip's memory.

> > For this use request_firmware() and associated interfaces.

> Is it more common to load the firmware from the kernel space?
> Is there an established mechanism for loading from user space?

Have you looked at request_firmware()?  It is the established mechanism
for doing this and supports both firmware images built into the kernel
and firmware loaded from userspace, though firmware in userspace is the
more common approach.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-09-30 23:34                     ` Mark Brown
@ 2010-10-01  1:56                       ` Peter Hsiang
  2010-10-01  2:37                         ` Mark Brown
  0 siblings, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-10-01  1:56 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel

On Thu, Sep 30, 2010, Mark Brown wrote:
> On Thu, Sep 30, 2010 at 04:10:15PM -0700, Peter Hsiang wrote:
> > On Thu, Sep 30, 2010, Mark Brown wrote:
> 
> > > For these it depends; frequently these are (like your existing EQ
> > > configuration) included in platform data but it really depends on how
> > > they are generated and if they are custom to a particular system (eg,
> > > dependant on plastics or particular speakers).
> 
> > In addition to the platform data, I would like in the future be
> > able to load in data values that are stored in or generated from the
> > user space.  Would I use the ALSA interface for this, or would it
> > need to be a separate interface?
> 
> This would not be idiomatic for ALSA.  The particular interface to use
> would depend on the volume of data being configured and the format it is
> provided in.  If this is just for test and development purposes then
> adding something to debugfs would be the normal thing.

This will be intended for normal operation.
The volume of data would be both small amounts (say 20 bytes or so)
for custom parameters, and also larger amounts (order of kilo bytes) 
for firmware image.
Seems like ALSA does not have build-in interfaces that can be 
directly used for these use cases right?
Will it be better to extend on ALSA or setup a separate path?

> > > > binary code image that goes into the chip's memory.
> 
> > > For this use request_firmware() and associated interfaces.
> 
> > Is it more common to load the firmware from the kernel space?
> > Is there an established mechanism for loading from user space?
> 
> Have you looked at request_firmware()?  It is the established mechanism
> for doing this and supports both firmware images built into the kernel
> and firmware loaded from userspace, though firmware in userspace is the
> more common approach.

Thanks, yes I see that it's a kernel space only feature, regardless 
of where the firmware image is located.
Would it be ok for a user space app to handle the image file directly
and send down the file content for the driver to process?

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-10-01  1:56                       ` Peter Hsiang
@ 2010-10-01  2:37                         ` Mark Brown
  2010-10-01  6:56                           ` Clemens Ladisch
  0 siblings, 1 reply; 81+ messages in thread
From: Mark Brown @ 2010-10-01  2:37 UTC (permalink / raw)
  To: Peter Hsiang; +Cc: alsa-devel

On Thu, Sep 30, 2010 at 06:56:58PM -0700, Peter Hsiang wrote:
> On Thu, Sep 30, 2010, Mark Brown wrote:

> > This would not be idiomatic for ALSA.  The particular interface to use
> > would depend on the volume of data being configured and the format it is
> > provided in.  If this is just for test and development purposes then
> > adding something to debugfs would be the normal thing.

> This will be intended for normal operation.
> The volume of data would be both small amounts (say 20 bytes or so)

That's still not really answering the questions about how and when the
values will be generated which are important here.

> for custom parameters, and also larger amounts (order of kilo bytes) 
> for firmware image.

For firmware there's request_firmware().

> Seems like ALSA does not have build-in interfaces that can be 
> directly used for these use cases right?
> Will it be better to extend on ALSA or setup a separate path?

It seems likely that the behaviour should have some sort of higher level
interface wrapped around it - for example, firmwares will add certain
features to the chip, coefficients will set the chip into a given mode.
Presenting these things uninterpreted to userspace would be really bad
for usability since they're not likely to be things that make sense to
users directly.

If the values are calculated ahead of time specifically for a given
system then platform data is probably the most appropriate way to get
the data into the driver.  If the values are very large then treating
them as a firmware image may be the best approach.  It really does
depend on what the things that need to be set do and where they come
from how they should be set.

> > Have you looked at request_firmware()?  It is the established mechanism
> > for doing this and supports both firmware images built into the kernel
> > and firmware loaded from userspace, though firmware in userspace is the
> > more common approach.

> Thanks, yes I see that it's a kernel space only feature, regardless 
> of where the firmware image is located.
> Would it be ok for a user space app to handle the image file directly
> and send down the file content for the driver to process?

With request_firmware() to userspace the firmware is loaded into the
kernel by a userspace application.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-10-01  2:37                         ` Mark Brown
@ 2010-10-01  6:56                           ` Clemens Ladisch
  2010-10-01  7:12                             ` Mark Brown
  0 siblings, 1 reply; 81+ messages in thread
From: Clemens Ladisch @ 2010-10-01  6:56 UTC (permalink / raw)
  To: Mark Brown; +Cc: Peter Hsiang, alsa-devel

Mark Brown wrote:
> On Thu, Sep 30, 2010 at 06:56:58PM -0700, Peter Hsiang wrote:
> > On Thu, Sep 30, 2010, Mark Brown wrote:
> > > Have you looked at request_firmware()?  It is the established mechanism
> > > for doing this and supports both firmware images built into the kernel
> > > and firmware loaded from userspace, though firmware in userspace is the
> > > more common approach.
> > 
> > Thanks, yes I see that it's a kernel space only feature, regardless
> > of where the firmware image is located.
> > Would it be ok for a user space app to handle the image file directly
> > and send down the file content for the driver to process?
> 
> With request_firmware() to userspace the firmware is loaded into the
> kernel by a userspace application.

request_firmware() is used to get the firmware data into the driver, but
it assumes that the driver decides when to ask for it.

If the firmware isn't needed when initialzing the device, you can add
some ioctl that makes the driver call request_firmware() (this basically
assumes that the data is stored in a file with a known name), or just
add some device that allows userspace to write data to the device (use
this for dynamic data).


Regards,
Clemens

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-10-01  6:56                           ` Clemens Ladisch
@ 2010-10-01  7:12                             ` Mark Brown
  2010-10-01 13:42                               ` Takashi Iwai
  0 siblings, 1 reply; 81+ messages in thread
From: Mark Brown @ 2010-10-01  7:12 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: Peter Hsiang, alsa-devel

On Fri, Oct 01, 2010 at 08:56:25AM +0200, Clemens Ladisch wrote:

> If the firmware isn't needed when initialzing the device, you can add
> some ioctl that makes the driver call request_firmware() (this basically
> assumes that the data is stored in a file with a known name), or just
> add some device that allows userspace to write data to the device (use
> this for dynamic data).

This does rather depend on what the firmware does and why it needs to be
reloaded - it is very rare that one can just blindly reload the firmware
and frequently changing the firmware will affect the set of user tunable
features that can be offered to the application layer.  Joining
everything up in a way that avoids fragility typically means that the
driver will end up initiating all the requests based on higher level
requests from audio applications.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-10-01  7:12                             ` Mark Brown
@ 2010-10-01 13:42                               ` Takashi Iwai
  2010-10-01 17:35                                 ` Mark Brown
  2010-10-01 21:57                                 ` Peter Hsiang
  0 siblings, 2 replies; 81+ messages in thread
From: Takashi Iwai @ 2010-10-01 13:42 UTC (permalink / raw)
  To: Mark Brown; +Cc: Peter Hsiang, alsa-devel, Clemens Ladisch

At Fri, 1 Oct 2010 00:12:34 -0700,
Mark Brown wrote:
> 
> On Fri, Oct 01, 2010 at 08:56:25AM +0200, Clemens Ladisch wrote:
> 
> > If the firmware isn't needed when initialzing the device, you can add
> > some ioctl that makes the driver call request_firmware() (this basically
> > assumes that the data is stored in a file with a known name), or just
> > add some device that allows userspace to write data to the device (use
> > this for dynamic data).
> 
> This does rather depend on what the firmware does and why it needs to be
> reloaded - it is very rare that one can just blindly reload the firmware
> and frequently changing the firmware will affect the set of user tunable
> features that can be offered to the application layer.  Joining
> everything up in a way that avoids fragility typically means that the
> driver will end up initiating all the requests based on higher level
> requests from audio applications.

Such a thing wasn't so uncommon in the past, even ISA days; they
weren't called as "firmware" at that time, though.
The implementation really depends on the driver, i.e. how complex and
how influential to the whole operation, as Mark repeatedly wrote :)

FWIW, if it's an EQ band coef data or such small and more-or-less safe
data, you can create even a dedicated ALSA control element to
read/write the byte array.  For example, SPDIF status bits are
transferred in that way between the driver and the user-space.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-10-01 13:42                               ` Takashi Iwai
@ 2010-10-01 17:35                                 ` Mark Brown
  2010-10-01 21:57                                 ` Peter Hsiang
  1 sibling, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-01 17:35 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Peter Hsiang, alsa-devel, Clemens Ladisch

On Fri, Oct 01, 2010 at 03:42:55PM +0200, Takashi Iwai wrote:

> FWIW, if it's an EQ band coef data or such small and more-or-less safe
> data, you can create even a dedicated ALSA control element to
> read/write the byte array.  For example, SPDIF status bits are
> transferred in that way between the driver and the user-space.

Though in the embedded case platform data often ends up being idomatic
since many of the typical uses revolve around tuning things for physical
properties of the platform.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-10-01 13:42                               ` Takashi Iwai
  2010-10-01 17:35                                 ` Mark Brown
@ 2010-10-01 21:57                                 ` Peter Hsiang
  2010-10-03  9:09                                   ` Takashi Iwai
  1 sibling, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-10-01 21:57 UTC (permalink / raw)
  To: Takashi Iwai, Mark Brown; +Cc: alsa-devel, Clemens Ladisch

On Fri, Oct 01, 2010, Takashi Iwai wrote:
> At Fri, 1 Oct 2010 00:12:34 -0700, Mark Brown wrote:
> >
> > On Fri, Oct 01, 2010 at 08:56:25AM +0200, Clemens Ladisch wrote:
> >
> > > If the firmware isn't needed when initialzing the device, you can add
> > > some ioctl that makes the driver call request_firmware() (this basically
> > > assumes that the data is stored in a file with a known name), or just
> > > add some device that allows userspace to write data to the device (use
> > > this for dynamic data).

Thanks Clemens, Takashi and Mark for the good comments.
Does ALSA use ioctl to implement its ALSA control elements?
To add an ioctl entry, would one expand on an existing list of ioctls
in ALSA, or would it be a fresh set of open/read/write/ioctl 
implemented in the codec driver?

> >
> > This does rather depend on what the firmware does and why it needs to be
> > reloaded - it is very rare that one can just blindly reload the firmware
> > and frequently changing the firmware will affect the set of user tunable
> > features that can be offered to the application layer.  Joining
> > everything up in a way that avoids fragility typically means that the
> > driver will end up initiating all the requests based on higher level
> > requests from audio applications.
> 
> Such a thing wasn't so uncommon in the past, even ISA days; they
> weren't called as "firmware" at that time, though.
> The implementation really depends on the driver, i.e. how complex and
> how influential to the whole operation, as Mark repeatedly wrote :)

I agree too that it depends on a number of factors.
If it's dynamic, can't be computed ahead of time, and the combination 
is too large to be stored statically in a limited amount of RAM, 
then there is a valid need.
A build-in mechanism in the driver to handle dynamic info will also
enable the driver itself to function as part of the runtime solution
that produces the dynamic data, and also saves them to disk file, 
for example.

> 
> FWIW, if it's an EQ band coef data or such small and more-or-less safe
> data, you can create even a dedicated ALSA control element to
> read/write the byte array.  For example, SPDIF status bits are
> transferred in that way between the driver and the user-space.

Thanks this is good to know.
> 
> 
> Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: user space control app driver interface for sound soc
  2010-10-01 21:57                                 ` Peter Hsiang
@ 2010-10-03  9:09                                   ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2010-10-03  9:09 UTC (permalink / raw)
  To: Peter Hsiang; +Cc: alsa-devel, Mark Brown, Clemens Ladisch

At Fri, 1 Oct 2010 14:57:28 -0700,
Peter Hsiang wrote:
> 
> On Fri, Oct 01, 2010, Takashi Iwai wrote:
> > At Fri, 1 Oct 2010 00:12:34 -0700, Mark Brown wrote:
> > >
> > > On Fri, Oct 01, 2010 at 08:56:25AM +0200, Clemens Ladisch wrote:
> > >
> > > > If the firmware isn't needed when initialzing the device, you can add
> > > > some ioctl that makes the driver call request_firmware() (this basically
> > > > assumes that the data is stored in a file with a known name), or just
> > > > add some device that allows userspace to write data to the device (use
> > > > this for dynamic data).
> 
> Thanks Clemens, Takashi and Mark for the good comments.
> Does ALSA use ioctl to implement its ALSA control elements?

Yes, but you can't add your own ioctl for control API.  The API
already provides the (small fixed-size) byte array communication.

> To add an ioctl entry, would one expand on an existing list of ioctls
> in ALSA, or would it be a fresh set of open/read/write/ioctl 
> implemented in the codec driver?

If you need own syscalls, implement freely using other method, such
as ALSA hwdep device or whatever you like.  But, of course, it will be
unportable since you define a new API set there.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-29  2:34 ` Peter Hsiang
  (?)
  (?)
@ 2010-10-13  1:20 ` Peter Hsiang
  2010-10-13  1:47   ` Joe Perches
                     ` (2 more replies)
  -1 siblings, 3 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-10-13  1:20 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown, Peter Ujfalusi
  Cc: alsa-devel, linux-kernel, Jesse Marroquin

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
---
 include/sound/max98088.h    |   52 ++
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2141 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  193 ++++
 5 files changed, 2392 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..0558ae0
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,52 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq1_cfg;
+       struct max98088_eq_cfg *eq2_cfg;
+       unsigned int eq1_cfgcnt;
+       unsigned int eq2_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode:1;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microphone input
+        */
+       unsigned int digmic_left_mode:1;
+       unsigned int digmic_right_mode:1;
+
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ff7b922..53af7d2 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS4270 if I2C
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
@@ -158,6 +159,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index af4d4c4..2d4941d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -89,6 +90,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..8d56fb8
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2141 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+       int eq_textcnt;
+       const char **eq_texts;
+       int eq_sel;
+       struct soc_enum eq_enum;
+};
+
+struct max98088_priv {
+       u8 reg_cache[M98088_REG_CNT];
+       void *control_data;
+       struct max98088_pdata *pdata;
+
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       u8 ina_state;
+       u8 inb_state;
+       unsigned int ex_mode;
+       unsigned int digmic;
+       unsigned int mic1pre;
+       unsigned int mic2pre;
+       unsigned int extmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x00, 0x00, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       BUG_ON(band > 4);
+       BUG_ON(dai > 1);
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_exmode_texts[] = {
+       "Off", "100Hz", "400Hz", "600Hz", "800Hz", "1000Hz", "200-400Hz",
+       "400-600Hz", "400-800Hz",
+};
+
+static const unsigned int max98088_exmode_values[] = {
+       0x00, 0x43, 0x10, 0x20, 0x30, 0x40, 0x11, 0x22, 0x32
+};
+
+static const struct soc_enum max98088_exmode_enum =
+       SOC_VALUE_ENUM_SINGLE(M98088_REG_41_SPKDHP, 0, 127,
+                             ARRAY_SIZE(max98088_exmode_texts),
+                             max98088_exmode_texts,
+                             max98088_exmode_values);
+static const struct snd_kcontrol_new max98088_exmode_controls =
+       SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static const struct soc_enum max98088_extmic_enum =
+       SOC_ENUM_SINGLE(M98088_REG_48_CFG_MIC, 0, 3, max98088_extmic_text);
+
+static const struct snd_kcontrol_new max98088_extmic_mux =
+       SOC_DAPM_ENUM("External MIC Mux", max98088_extmic_enum);
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int sel = ucontrol->value.integer.value[0];
+
+       max98088->mic1pre = sel;
+       snd_soc_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
+               (1+sel)<<M98088_MICPRE_SHIFT);
+
+       return 0;
+}
+
+static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = max98088->mic1pre;
+       return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int sel = ucontrol->value.integer.value[0];
+
+       max98088->mic2pre = sel;
+       snd_soc_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
+               (1+sel)<<M98088_MICPRE_SHIFT);
+
+       return 0;
+}
+
+static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = max98088->mic2pre;
+       return 0;
+}
+
+static const unsigned int max98088_micboost_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+       2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+       SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+       SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+       SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+                       M98088_REG_35_LVL_MIC1, 5, 2, 0,
+                       max98088_mic1pre_get, max98088_mic1pre_set,
+                       max98088_micboost_tlv),
+       SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+                       M98088_REG_36_LVL_MIC2, 5, 2, 0,
+                       max98088_mic2pre_get, max98088_mic2pre_set,
+                       max98088_micboost_tlv),
+
+       SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+       SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+       SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+               0, 1, 0),
+
+       SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (w->reg == M98088_REG_35_LVL_MIC1) {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+               } else {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * The line inputs are 2-channel stereo inputs with the left
+ * and right channels sharing a common PGA power control signal.
+ */
+static int max98088_line_pga(struct snd_soc_dapm_widget *w,
+                            int event, int line, u8 channel)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       u8 *state;
+
+       BUG_ON(!((channel == 1) || (channel == 2)));
+
+       switch (line) {
+       case LINE_INA:
+               state = &max98088->ina_state;
+               break;
+       case LINE_INB:
+               state = &max98088->inb_state;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               *state |= channel;
+               snd_soc_update_bits(codec, w->reg,
+                       (1 << w->shift), (1 << w->shift));
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               *state &= ~channel;
+               if (*state == 0) {
+                       snd_soc_update_bits(codec, w->reg,
+                               (1 << w->shift), 0);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int max98088_pga_ina1_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INA, 1);
+}
+
+static int max98088_pga_ina2_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INA, 2);
+}
+
+static int max98088_pga_inb1_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INB, 1);
+}
+
+static int max98088_pga_inb2_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INB, 2);
+}
+
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+               &max98088_extmic_mux),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_ina1_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_ina2_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_inb1_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_inb2_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
+               &max98088_exmode_controls),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right HP Mixer", "Left DAC2 Switch", "DACL2"  },
+       {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+       {"MIC1 Input", NULL, "MIC1"},
+       {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+       u32 rate;
+       u8  sr;
+} rate_table[] = {
+       {8000,  0x10},
+       {11025, 0x20},
+       {16000, 0x30},
+       {22050, 0x40},
+       {24000, 0x50},
+       {32000, 0x60},
+       {44100, 0x70},
+       {48000, 0x80},
+       {88200, 0x90},
+       {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate) {
+                       *value = rate_table[i].sr;
+                       return 0;
+               }
+       }
+       *value = rate_table[0].sr;
+       return -EINVAL;
+}
+
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned long long ni;
+       unsigned int rate;
+       u8 regval;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate_value(rate, &regval))
+               return -EINVAL;
+
+       snd_soc_update_bits(codec, M98088_REG_11_DAI1_CLKMODE,
+               M98088_CLKMODE_MASK, regval);
+       cdata->rate = rate;
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0) {
+                       dev_err(codec->dev, "Invalid system clock frequency\n");
+                       return -EINVAL;
+               }
+               ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate;
+               do_div(ni, (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7F);
+               snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xFF);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+               M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned long long ni;
+       unsigned int rate;
+       u8 regval;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate_value(rate, &regval))
+               return -EINVAL;
+
+       snd_soc_update_bits(codec, M98088_REG_19_DAI2_CLKMODE,
+               M98088_CLKMODE_MASK, regval);
+       cdata->rate = rate;
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0) {
+                       dev_err(codec->dev, "Invalid system clock frequency\n");
+                       return -EINVAL;
+               }
+               ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate;
+               do_div(ni, (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7F);
+               snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xFF);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+               M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       /* Requested clock frequency is already setup */
+       if (freq == max98088->sysclk)
+               return 0;
+
+       max98088->sysclk = freq; /* remember current sysclk */
+
+       /* Setup clocks for slave mode, and using the PLL
+        * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+        *         0x02 (when master clk is 20MHz to 30MHz)..
+        */
+       if ((freq >= 10000000) && (freq < 20000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+       } else if ((freq >= 20000000) && (freq < 30000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+       } else {
+               dev_err(codec->dev, "Invalid master clock frequency\n");
+               return -EINVAL;
+       }
+
+       if (snd_soc_read(codec, M98088_REG_51_PWR_SYS)  & M98088_SHDNRUN) {
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, 0);
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, M98088_SHDNRUN);
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg15val;
+       u8 reg14val = 0;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* Slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* Set to master mode */
+                       reg14val |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg14val |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg14val |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg14val |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
+                       M98088_DAI_WCI, reg14val);
+
+               reg15val = M98088_DAI_BSEL64;
+               if (max98088->digmic)
+                       reg15val |= M98088_DAI_OSR64;
+               snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+       }
+
+       return 0;
+}
+
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg1Cval = 0;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* Slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* Set to master mode */
+                       reg1Cval |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg1Cval |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg1Cval |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg1Cval |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
+                       M98088_DAI_WCI, reg1Cval);
+
+               snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       M98088_DAI_BSEL64);
+       }
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       if (!codec->cache_sync)
+               return;
+
+       codec->cache_only = 0;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+
+       codec->cache_sync = 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF)
+                       max98088_sync_cache(codec);
+
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, M98088_MBEN);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, 0);
+               codec->cache_sync = 1;
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+static struct snd_soc_dai_driver max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq1_cfg[best].name,
+               pdata->eq1_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq1_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* Restore the original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq2_cfg[best].name,
+               pdata->eq2_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq2_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* Restore the original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->eq1_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq1(codec);
+       return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->eq2_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq2(codec);
+       return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_setup_eq_enum(struct max98088_eq_cfg *cfg,
+                                  unsigned int cfgcnt,
+                                  struct max98088_cdata *cdata)
+{
+       int i, j;
+       const char **t;
+
+       /* Setup an array of texts for the equalizer enum.
+        * This is based on Mark Brown's equalizer driver code.
+        * It has been extended to support multiple equalizers.
+        */
+       cdata->eq_textcnt = 0;
+       cdata->eq_texts = NULL;
+       for (i = 0; i < cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(cfg[i].name, cdata->eq_texts[j]) == 0)
+                               break;
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               /* Expand the array */
+               t = krealloc(cdata->eq_texts,
+                            sizeof(char *) * (cdata->eq_textcnt + 1),
+                            GFP_KERNEL);
+               if (t == NULL)
+                       continue;
+
+               /* Store the new entry */
+               t[cdata->eq_textcnt] = cfg[i].name;
+               cdata->eq_textcnt++;
+               cdata->eq_texts = t;
+       }
+
+       /* Now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+}
+
+static void max98088_handle_eq1_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata = &max98088->dai[0];
+       int ret;
+
+       struct snd_kcontrol_new eq1control =
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->dai[0].eq_enum,
+                       max98088_get_eq1_enum,
+                       max98088_put_eq1_enum);
+
+       max98088_setup_eq_enum(pdata->eq1_cfg, pdata->eq1_cfgcnt, cdata);
+
+       dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+               cdata->eq_enum.max);
+
+       ret = snd_soc_add_controls(codec, &eq1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata = &max98088->dai[1];
+       int ret;
+
+       struct snd_kcontrol_new eq2control =
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->dai[1].eq_enum,
+                       max98088_get_eq2_enum,
+                       max98088_put_eq2_enum);
+
+       max98088_setup_eq_enum(pdata->eq2_cfg, pdata->eq2_cfgcnt, cdata);
+
+       dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+               cdata->eq_enum.max);
+
+       ret = snd_soc_add_controls(codec, &eq2control, 1);
+       if (ret != 0)
+               dev_dbg(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval = 0;
+
+       if (!pdata) {
+               dev_dbg(codec->dev, "No platform data\n");
+               return;
+       }
+
+       /* Configure mic for analog/digital mic mode */
+       if (pdata->digmic_left_mode)
+               regval |= M98088_DIGMIC_L;
+
+       if (pdata->digmic_right_mode)
+               regval |= M98088_DIGMIC_R;
+
+       max98088->digmic = (regval ? 1 : 0);
+
+       snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* Configure receiver output */
+       regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               M98088_REC_LINEMODE_MASK, regval);
+
+       /* Configure equalizers */
+       if (pdata->eq1_cfgcnt)
+               max98088_handle_eq1_pdata(codec);
+
+       if (pdata->eq2_cfgcnt)
+               max98088_handle_eq2_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct snd_soc_codec *codec)
+{
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define max98088_suspend NULL
+#define max98088_resume NULL
+#endif
+
+static int max98088_probe(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       int ret = 0;
+
+       codec->cache_sync = 1;
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       max98088->ina_state = 0;
+       max98088->inb_state = 0;
+       max98088->ex_mode = 0;
+       max98088->digmic = 0;
+       max98088->mic1pre = 0;
+       max98088->mic2pre = 0;
+
+       ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read device revision: %d\n",
+                       ret);
+               goto err_access;
+       }
+       dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+       snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+               M98088_S1NORMAL|M98088_SDATA);
+
+       snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+               M98088_S2NORMAL|M98088_SDATA);
+
+       max98088_handle_pdata(codec);
+
+       max98088_add_widgets(codec);
+
+err_access:
+       return ret;
+}
+
+static int max98088_remove(struct snd_soc_codec *codec)
+{
+       if (codec->control_data)
+               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+       .set_bias_level = max98088_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(max98088_reg),
+       .reg_word_size = sizeof(u8),
+       .reg_cache_default = max98088_reg,
+       .volatile_register = max98088_volatile_register,
+};
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct max98088_priv *max98088;
+       int ret;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max98088);
+       max98088->control_data = i2c;
+       max98088->pdata = i2c->dev.platform_data;
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_max98088, &max98088_dai[0], 2);
+       if (ret < 0)
+               kfree(max98088);
+       return ret;
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "max98088",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+module_init(max98088_init);
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..e46b258
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,193 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_11_DAI1_CLKMODE, M98088_REG_19_DAI2_CLKMODE */
+       #define M98088_CLKMODE_MASK             0xFF
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+       #define M98088_DAI_BSEL64               (1<<0)
+       #define M98088_DAI_OSR64                (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+       #define M98088_S1NORMAL                 (1<<6)
+       #define M98088_S2NORMAL                 (2<<6)
+       #define M98088_SDATA                    (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+       #define M98088_DAI_DHF                  (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+       #define M98088_REC_LINEMODE             (1<<7)
+       #define M98088_REC_LINEMODE_MASK        (1<<7)
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+       #define M98088_MICPRE_MASK              (3<<5)
+       #define M98088_MICPRE_SHIFT             5
+
+/* M98088_REG_3A_LVL_HP_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+       #define M98088_EXTMIC_MASK              (3<<0)
+       #define M98088_DIGMIC_L                 (1<<5)
+       #define M98088_DIGMIC_R                 (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+       #define M98088_INAEN                    (1<<7)
+       #define M98088_INBEN                    (1<<6)
+       #define M98088_MBEN                     (1<<3)
+       #define M98088_ADLEN                    (1<<1)
+       #define M98088_ADREN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+/* Line inputs */
+#define LINE_INA  0
+#define LINE_INB  1
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+#endif
--
1.6.3.3


^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-13  1:20 ` [PATCH] ASoC: Add max98088 CODEC driver Peter Hsiang
@ 2010-10-13  1:47   ` Joe Perches
  2010-10-13  8:24       ` Mark Brown
  2010-10-13 10:32     ` Mark Brown
  2010-10-14  3:30   ` Peter Hsiang
  2 siblings, 1 reply; 81+ messages in thread
From: Joe Perches @ 2010-10-13  1:47 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Tue, 2010-10-12 at 18:20 -0700, Peter Hsiang wrote:
> This patch adds the MAX98088 CODEC driver.

Just trivial comments:

> +static struct {
> +       int readable;
> +       int writable;
> +       int vol;
> +} max98088_access[M98088_REG_CNT] = {

bool instead?

static struct {
	bool readable;
	bool writable,
	bool vol;
} etc...

readable isn't used anywhere, writeable is used as bool
vol isn't a good name as it's easy to confuse with
volume.  Maybe volatile_register?

> +static int max98088_volatile_register(unsigned int reg)
> +{
> +       return max98088_access[reg].vol;
> +}

No error checking if reg isn't limited to the array size.

> +static const char *max98088_exmode_texts[] = {
[]
> +static const char *max98088_ex_thresh[] = { /* volts PP */
[]
> +static const struct soc_enum max98088_ex_thresh_enum[] = {
[]
> +static const char *max98088_fltr_mode[] = {"Voice", "Music" };
[]
> +static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" };
[]
> +static const char *max98088_dai1_fltr[] = {

static const char * const



^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-13  1:47   ` Joe Perches
@ 2010-10-13  8:24       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13  8:24 UTC (permalink / raw)
  To: Joe Perches
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Tue, Oct 12, 2010 at 06:47:52PM -0700, Joe Perches wrote:
> On Tue, 2010-10-12 at 18:20 -0700, Peter Hsiang wrote:

> > +static struct {
> > +       int readable;
> > +       int writable;
> > +       int vol;
> > +} max98088_access[M98088_REG_CNT] = {

> bool instead?

> static struct {
> 	bool readable;
> 	bool writable,
> 	bool vol;
> } etc...

The readable and writable fields are being used as bitmasks:

| +       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */

so this discards data which we may wish to use in future.

> readable isn't used anywhere, writeable is used as bool
> vol isn't a good name as it's easy to confuse with
> volume.  Maybe volatile_register?

vol is traditionally used for this throughout the subsystem.  It's
unfortuante that volatile is a keyword.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-10-13  8:24       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13  8:24 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Peter Hsiang, Jesse Marroquin, Liam Girdwood

On Tue, Oct 12, 2010 at 06:47:52PM -0700, Joe Perches wrote:
> On Tue, 2010-10-12 at 18:20 -0700, Peter Hsiang wrote:

> > +static struct {
> > +       int readable;
> > +       int writable;
> > +       int vol;
> > +} max98088_access[M98088_REG_CNT] = {

> bool instead?

> static struct {
> 	bool readable;
> 	bool writable,
> 	bool vol;
> } etc...

The readable and writable fields are being used as bitmasks:

| +       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */

so this discards data which we may wish to use in future.

> readable isn't used anywhere, writeable is used as bool
> vol isn't a good name as it's easy to confuse with
> volume.  Maybe volatile_register?

vol is traditionally used for this throughout the subsystem.  It's
unfortuante that volatile is a keyword.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-13  1:20 ` [PATCH] ASoC: Add max98088 CODEC driver Peter Hsiang
@ 2010-10-13 10:32     ` Mark Brown
  2010-10-13 10:32     ` Mark Brown
  2010-10-14  3:30   ` Peter Hsiang
  2 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 10:32 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Tue, Oct 12, 2010 at 06:20:10PM -0700, Peter Hsiang wrote:

This looks pretty good, waiting for Liam's review but I think we can do
further stuff incrementally.  Minor comments:

> +
> +       /* Setup an array of texts for the equalizer enum.
> +        * This is based on Mark Brown's equalizer driver code.
> +        * It has been extended to support multiple equalizers.
> +        */

The code that you're basing this on supports multiple equalizers too -
things like the WM8994 have several.  Given that all those supported by
the original code are identical there didn't seem to be any point in
restricting which EQ a given setup can be deployed on - it's just one
more thing that can go wrong.

> +static int max98088_remove(struct snd_soc_codec *codec)
> +{
> +       if (codec->control_data)
> +               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);

The control_data check should be redundant here.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-10-13 10:32     ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 10:32 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Jesse Marroquin, Liam Girdwood

On Tue, Oct 12, 2010 at 06:20:10PM -0700, Peter Hsiang wrote:

This looks pretty good, waiting for Liam's review but I think we can do
further stuff incrementally.  Minor comments:

> +
> +       /* Setup an array of texts for the equalizer enum.
> +        * This is based on Mark Brown's equalizer driver code.
> +        * It has been extended to support multiple equalizers.
> +        */

The code that you're basing this on supports multiple equalizers too -
things like the WM8994 have several.  Given that all those supported by
the original code are identical there didn't seem to be any point in
restricting which EQ a given setup can be deployed on - it's just one
more thing that can go wrong.

> +static int max98088_remove(struct snd_soc_codec *codec)
> +{
> +       if (codec->control_data)
> +               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);

The control_data check should be redundant here.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH] sound/soc: rename vol to volatile_register as appropriate
  2010-10-13  8:24       ` Mark Brown
  (?)
@ 2010-10-13 12:10       ` Joe Perches
  2010-10-13 12:33           ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Joe Perches @ 2010-10-13 12:10 UTC (permalink / raw)
  To: Mark Brown
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

Rename the declaration and uses of variables
named vol to volatile_register to avoid name
clash with the much more common use of vol
for volume.

Signed-off-by: Joe Perches <joe@perches.com>
---
On Wed, 2010-10-13 at 09:24 +0100, Mark Brown wrote:
> On Tue, Oct 12, 2010 at 06:47:52PM -0700, Joe Perches wrote:
> > On Tue, 2010-10-12 at 18:20 -0700, Peter Hsiang wrote:
> 
> > > +static struct {
> > > +       int readable;
> > > +       int writable;
> > > +       int vol;
> > > +} max98088_access[M98088_REG_CNT] = {
> 
> > bool instead?
> 
> > static struct {
> > 	bool readable;
> > 	bool writable,
> > 	bool vol;
> > } etc...
> 
> The readable and writable fields are being used as bitmasks:

No, they are being declared as bitmasks.
writable is used once as bool, readable isn't used at all.

> | +       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
> so this discards data which we may wish to use in future.

It's not used as bitmask now, what use would there
be in the future for it as a bitmask?

> > readable isn't used anywhere, writeable is used as bool
> > vol isn't a good name as it's easy to confuse with
> > volume.  Maybe volatile_register?
> 
> vol is traditionally used for this throughout the subsystem.  It's
> unfortuante that volatile is a keyword.

As far as I see, your description of vol being
used throughout the subsystem is not true.

vol is defined 3 times in sound/soc/codecs for
volatility, used twice, and there are about 500
uses in sound for vol as volume.

Here's a patch for the current defines and uses.

 sound/soc/codecs/wm8904.c |    4 ++--
 sound/soc/codecs/wm8962.c |    2 +-
 sound/soc/codecs/wm8962.h |    2 +-
 sound/soc/codecs/wm8994.c |    2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 33be84e..18a6f5c 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -346,7 +346,7 @@ static const u16 wm8904_reg[WM8904_MAX_REGISTER + 1] = {
 static struct {
 	int readable;
 	int writable;
-	int vol;
+	bool volatile_register;
 } wm8904_access[] = {
 	{ 0xFFFF, 0xFFFF, 1 }, /* R0   - SW Reset and ID */
 	{ 0x0000, 0x0000, 0 }, /* R1   - Revision */
@@ -601,7 +601,7 @@ static struct {
 
 static int wm8904_volatile_register(unsigned int reg)
 {
-	return wm8904_access[reg].vol;
+	return wm8904_access[reg].volatile_register;
 }
 
 static int wm8904_reset(struct snd_soc_codec *codec)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 6d30f34..98ed910 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -108,7 +108,7 @@ WM8962_REGULATOR_EVENT(7)
 
 static int wm8962_volatile_register(unsigned int reg)
 {
-	if (wm8962_reg_access[reg].vol)
+	if (wm8962_reg_access[reg].volatile_register)
 		return 1;
 	else
 		return 0;
diff --git a/sound/soc/codecs/wm8962.h b/sound/soc/codecs/wm8962.h
index 2af6c93..070a4c9 100644
--- a/sound/soc/codecs/wm8962.h
+++ b/sound/soc/codecs/wm8962.h
@@ -3780,7 +3780,7 @@ extern const u16 wm8962_reg[WM8962_MAX_REGISTER + 1];
 struct wm8962_reg_access {
 	u16 read;
 	u16 write;
-	u16 vol;
+	bool volatile_register;
 };
 
 extern
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 218bcd2..d46f661 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -101,7 +101,7 @@ struct wm8994_priv {
 static struct {
 	unsigned short  readable;   /* Mask of readable bits */
 	unsigned short  writable;   /* Mask of writable bits */
-	unsigned short  vol;        /* Mask of volatile bits */
+	unsigned short  volatile_register;    /* Mask of volatile bits */
 } access_masks[] = {
 	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R0     - Software Reset */
 	{ 0x3B37, 0x3B37, 0x0000 }, /* R1     - Power Management (1) */



^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
  2010-10-13 12:10       ` [PATCH] sound/soc: rename vol to volatile_register as appropriate Joe Perches
@ 2010-10-13 12:33           ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 12:33 UTC (permalink / raw)
  To: Joe Perches
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010 at 05:10:45AM -0700, Joe Perches wrote:
> Rename the declaration and uses of variables
> named vol to volatile_register to avoid name
> clash with the much more common use of vol
> for volume.

Are any of the contexts actually ambiguous?  I have to say I don't find
this useful.  If the register I/O code knows anything about volumes I'd
say we've probably messed up somewhere.

> > > static struct {
> > > 	bool readable;
> > > 	bool writable,
> > > 	bool vol;
> > > } etc...

> > The readable and writable fields are being used as bitmasks:

> No, they are being declared as bitmasks.
> writable is used once as bool, readable isn't used at all.

They're being used in the table initialisation.

> > | +       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */

> > so this discards data which we may wish to use in future.

> It's not used as bitmask now, what use would there
> be in the future for it as a bitmask?

Examples would include validating I/O operations done by drivers, or
supporting fancy cache handling that pays attention to things per bit.

> > vol is traditionally used for this throughout the subsystem.  It's
> > unfortuante that volatile is a keyword.

> As far as I see, your description of vol being
> used throughout the subsystem is not true.

I'm sorry?  It's used as the field name for volatility in all the
drivers I can remember that use a table to look volatility up in
register properties.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
@ 2010-10-13 12:33           ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 12:33 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Peter Hsiang, Jesse Marroquin, Liam Girdwood

On Wed, Oct 13, 2010 at 05:10:45AM -0700, Joe Perches wrote:
> Rename the declaration and uses of variables
> named vol to volatile_register to avoid name
> clash with the much more common use of vol
> for volume.

Are any of the contexts actually ambiguous?  I have to say I don't find
this useful.  If the register I/O code knows anything about volumes I'd
say we've probably messed up somewhere.

> > > static struct {
> > > 	bool readable;
> > > 	bool writable,
> > > 	bool vol;
> > > } etc...

> > The readable and writable fields are being used as bitmasks:

> No, they are being declared as bitmasks.
> writable is used once as bool, readable isn't used at all.

They're being used in the table initialisation.

> > | +       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */

> > so this discards data which we may wish to use in future.

> It's not used as bitmask now, what use would there
> be in the future for it as a bitmask?

Examples would include validating I/O operations done by drivers, or
supporting fancy cache handling that pays attention to things per bit.

> > vol is traditionally used for this throughout the subsystem.  It's
> > unfortuante that volatile is a keyword.

> As far as I see, your description of vol being
> used throughout the subsystem is not true.

I'm sorry?  It's used as the field name for volatility in all the
drivers I can remember that use a table to look volatility up in
register properties.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
  2010-10-13 12:33           ` Mark Brown
  (?)
@ 2010-10-13 12:55           ` Joe Perches
  2010-10-13 15:11               ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Joe Perches @ 2010-10-13 12:55 UTC (permalink / raw)
  To: Mark Brown
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, 2010-10-13 at 13:33 +0100, Mark Brown wrote:
> On Wed, Oct 13, 2010 at 05:10:45AM -0700, Joe Perches wrote:
> > Rename the declaration and uses of variables
> > named vol to volatile_register to avoid name
> > clash with the much more common use of vol
> > for volume.
> Are any of the contexts actually ambiguous?  I have to say I don't find
> this useful.  If the register I/O code knows anything about volumes I'd
> say we've probably messed up somewhere.

Looking at the 2 register access routines where
volatile_register/vol is used, I'd say those
routines aren't useful at all and the checks
should be inline instead.

This sort of wrapper routine doesn't serve
much of a purpose to me:

 static int wm8904_volatile_register(unsigned int reg)
 {
-       return wm8904_access[reg].vol;
+       return wm8904_access[reg].volatile_register;
 }

> It's used as the field name for volatility in all the
> drivers I can remember that use a table to look volatility up in
> register properties.

I did a grep for vol in sound, I found the uses
where it was for volatile and patched them.

Are there others?  Perhaps, but I don't think so.
Probably not in sound.  Was there some other
subsystem you meant?  Maybe you have examples
I missed?


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
  2010-10-13 12:55           ` Joe Perches
@ 2010-10-13 15:11               ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 15:11 UTC (permalink / raw)
  To: Joe Perches
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010 at 05:55:48AM -0700, Joe Perches wrote:
> On Wed, 2010-10-13 at 13:33 +0100, Mark Brown wrote:

> > Are any of the contexts actually ambiguous?  I have to say I don't find
> > this useful.  If the register I/O code knows anything about volumes I'd
> > say we've probably messed up somewhere.

> Looking at the 2 register access routines where
> volatile_register/vol is used, I'd say those
> routines aren't useful at all and the checks
> should be inline instead.

If you check the code again you will notice that these functons are all
used in ops structures which presents obvious issues when trying to
replace with open coded checks.  If we remove these functions then the
core won't be able to tell if it should read registers from the hardware
or from the cache.

> This sort of wrapper routine doesn't serve
> much of a purpose to me:

>  static int wm8904_volatile_register(unsigned int reg)

I hope their usefulness is clear given the above.

> > It's used as the field name for volatility in all the
> > drivers I can remember that use a table to look volatility up in
> > register properties.

> I did a grep for vol in sound, I found the uses
> where it was for volatile and patched them.

> Are there others?  Perhaps, but I don't think so.
> Probably not in sound.  Was there some other
> subsystem you meant?  Maybe you have examples
> I missed?

You're missing the "for this" here, I believe.  To reiterate what I said
originally as far as I can remember every ASoC driver which uses a table
to look up volatility uses this naming.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
@ 2010-10-13 15:11               ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 15:11 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Peter Hsiang, Jesse Marroquin, Liam Girdwood

On Wed, Oct 13, 2010 at 05:55:48AM -0700, Joe Perches wrote:
> On Wed, 2010-10-13 at 13:33 +0100, Mark Brown wrote:

> > Are any of the contexts actually ambiguous?  I have to say I don't find
> > this useful.  If the register I/O code knows anything about volumes I'd
> > say we've probably messed up somewhere.

> Looking at the 2 register access routines where
> volatile_register/vol is used, I'd say those
> routines aren't useful at all and the checks
> should be inline instead.

If you check the code again you will notice that these functons are all
used in ops structures which presents obvious issues when trying to
replace with open coded checks.  If we remove these functions then the
core won't be able to tell if it should read registers from the hardware
or from the cache.

> This sort of wrapper routine doesn't serve
> much of a purpose to me:

>  static int wm8904_volatile_register(unsigned int reg)

I hope their usefulness is clear given the above.

> > It's used as the field name for volatility in all the
> > drivers I can remember that use a table to look volatility up in
> > register properties.

> I did a grep for vol in sound, I found the uses
> where it was for volatile and patched them.

> Are there others?  Perhaps, but I don't think so.
> Probably not in sound.  Was there some other
> subsystem you meant?  Maybe you have examples
> I missed?

You're missing the "for this" here, I believe.  To reiterate what I said
originally as far as I can remember every ASoC driver which uses a table
to look up volatility uses this naming.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol
  2010-10-13 12:33           ` Mark Brown
  (?)
  (?)
@ 2010-10-13 15:19           ` Joe Perches
  2010-10-15 10:08               ` Liam Girdwood
  2010-10-15 10:39               ` Mark Brown
  -1 siblings, 2 replies; 81+ messages in thread
From: Joe Perches @ 2010-10-13 15:19 UTC (permalink / raw)
  To: Mark Brown
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

Remove unused vol from struct access_mask

Reduces object size ~3kb.

$ size sound/soc/codecs/wm8994.o*
   text	   data	    bss	    dec	    hex	filename
  40727	   4384	   4480	  49591	   c1b7	sound/soc/codecs/wm8994.o.new
  43879	   4384	   4480	  52743	   ce07	sound/soc/codecs/wm8994.o.old

Signed-off-by: Joe Perches <joe@perches.com>
---
 sound/soc/codecs/wm8994.c | 3147 ++++++++++++++++++++++-----------------------
 1 files changed, 1573 insertions(+), 1574 deletions(-)

diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 218bcd2..da271ed 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -98,1581 +98,1580 @@ struct wm8994_priv {
 	struct wm8994_pdata *pdata;
 };
 
-static struct {
-	unsigned short  readable;   /* Mask of readable bits */
-	unsigned short  writable;   /* Mask of writable bits */
-	unsigned short  vol;        /* Mask of volatile bits */
+static const struct {
+	unsigned short readable;   /* Mask of readable bits */
+	unsigned short writable;   /* Mask of writable bits */
 } access_masks[] = {
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R0     - Software Reset */
-	{ 0x3B37, 0x3B37, 0x0000 }, /* R1     - Power Management (1) */
-	{ 0x6BF0, 0x6BF0, 0x0000 }, /* R2     - Power Management (2) */
-	{ 0x3FF0, 0x3FF0, 0x0000 }, /* R3     - Power Management (3) */
-	{ 0x3F3F, 0x3F3F, 0x0000 }, /* R4     - Power Management (4) */
-	{ 0x3F0F, 0x3F0F, 0x0000 }, /* R5     - Power Management (5) */
-	{ 0x003F, 0x003F, 0x0000 }, /* R6     - Power Management (6) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R7 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R8 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R9 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R10 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R11 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R12 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R13 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R14 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R15 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R16 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R17 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R18 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R19 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R20 */
-	{ 0x01C0, 0x01C0, 0x0000 }, /* R21    - Input Mixer (1) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R22 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R23 */
-	{ 0x00DF, 0x01DF, 0x0000 }, /* R24    - Left Line Input 1&2 Volume */
-	{ 0x00DF, 0x01DF, 0x0000 }, /* R25    - Left Line Input 3&4 Volume */
-	{ 0x00DF, 0x01DF, 0x0000 }, /* R26    - Right Line Input 1&2 Volume */
-	{ 0x00DF, 0x01DF, 0x0000 }, /* R27    - Right Line Input 3&4 Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R28    - Left Output Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R29    - Right Output Volume */
-	{ 0x0077, 0x0077, 0x0000 }, /* R30    - Line Outputs Volume */
-	{ 0x0030, 0x0030, 0x0000 }, /* R31    - HPOUT2 Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R32    - Left OPGA Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R33    - Right OPGA Volume */
-	{ 0x007F, 0x007F, 0x0000 }, /* R34    - SPKMIXL Attenuation */
-	{ 0x017F, 0x017F, 0x0000 }, /* R35    - SPKMIXR Attenuation */
-	{ 0x003F, 0x003F, 0x0000 }, /* R36    - SPKOUT Mixers */
-	{ 0x003F, 0x003F, 0x0000 }, /* R37    - ClassD */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R38    - Speaker Volume Left */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R39    - Speaker Volume Right */
-	{ 0x00FF, 0x00FF, 0x0000 }, /* R40    - Input Mixer (2) */
-	{ 0x01B7, 0x01B7, 0x0000 }, /* R41    - Input Mixer (3) */
-	{ 0x01B7, 0x01B7, 0x0000 }, /* R42    - Input Mixer (4) */
-	{ 0x01C7, 0x01C7, 0x0000 }, /* R43    - Input Mixer (5) */
-	{ 0x01C7, 0x01C7, 0x0000 }, /* R44    - Input Mixer (6) */
-	{ 0x01FF, 0x01FF, 0x0000 }, /* R45    - Output Mixer (1) */
-	{ 0x01FF, 0x01FF, 0x0000 }, /* R46    - Output Mixer (2) */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R47    - Output Mixer (3) */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R48    - Output Mixer (4) */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R49    - Output Mixer (5) */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R50    - Output Mixer (6) */
-	{ 0x0038, 0x0038, 0x0000 }, /* R51    - HPOUT2 Mixer */
-	{ 0x0077, 0x0077, 0x0000 }, /* R52    - Line Mixer (1) */
-	{ 0x0077, 0x0077, 0x0000 }, /* R53    - Line Mixer (2) */
-	{ 0x03FF, 0x03FF, 0x0000 }, /* R54    - Speaker Mixer */
-	{ 0x00C1, 0x00C1, 0x0000 }, /* R55    - Additional Control */
-	{ 0x00F0, 0x00F0, 0x0000 }, /* R56    - AntiPOP (1) */
-	{ 0x01EF, 0x01EF, 0x0000 }, /* R57    - AntiPOP (2) */
-	{ 0x00FF, 0x00FF, 0x0000 }, /* R58    - MICBIAS */
-	{ 0x000F, 0x000F, 0x0000 }, /* R59    - LDO 1 */
-	{ 0x0007, 0x0007, 0x0000 }, /* R60    - LDO 2 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R61 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R62 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R63 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R64 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R65 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R66 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R67 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R68 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R69 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R70 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R71 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R72 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R73 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R74 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R75 */
-	{ 0x8000, 0x8000, 0x0000 }, /* R76    - Charge Pump (1) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R77 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R78 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R79 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R80 */
-	{ 0x0301, 0x0301, 0x0000 }, /* R81    - Class W (1) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R82 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R83 */
-	{ 0x333F, 0x333F, 0x0000 }, /* R84    - DC Servo (1) */
-	{ 0x0FEF, 0x0FEF, 0x0000 }, /* R85    - DC Servo (2) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R86 */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R87    - DC Servo (4) */
-	{ 0x0333, 0x0000, 0x0000 }, /* R88    - DC Servo Readback */
-	{ 0x0000, 0x0000, 0x0000 }, /* R89 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R90 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R91 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R92 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R93 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R94 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R95 */
-	{ 0x00EE, 0x00EE, 0x0000 }, /* R96    - Analogue HP (1) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R97 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R98 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R99 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R100 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R101 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R102 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R103 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R104 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R105 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R106 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R107 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R108 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R109 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R110 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R111 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R112 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R113 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R114 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R115 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R116 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R117 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R118 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R119 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R120 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R121 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R122 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R123 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R124 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R125 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R126 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R127 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R128 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R129 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R130 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R131 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R132 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R133 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R134 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R135 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R136 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R137 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R138 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R139 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R140 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R141 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R142 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R143 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R144 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R145 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R146 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R147 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R148 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R149 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R150 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R151 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R152 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R153 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R154 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R155 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R156 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R157 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R158 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R159 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R160 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R161 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R162 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R163 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R164 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R165 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R166 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R167 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R168 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R169 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R170 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R171 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R172 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R173 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R174 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R175 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R176 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R177 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R178 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R179 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R180 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R181 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R182 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R183 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R184 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R185 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R186 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R187 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R188 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R189 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R190 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R191 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R192 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R193 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R194 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R195 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R196 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R197 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R198 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R199 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R200 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R201 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R202 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R203 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R204 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R205 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R206 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R207 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R208 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R209 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R210 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R211 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R212 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R213 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R214 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R215 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R216 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R217 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R218 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R219 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R220 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R221 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R222 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R223 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R224 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R225 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R226 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R227 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R228 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R229 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R230 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R231 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R232 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R233 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R234 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R235 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R236 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R237 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R238 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R239 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R240 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R241 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R242 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R243 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R244 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R245 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R246 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R247 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R248 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R249 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R250 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R251 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R252 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R253 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R254 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R255 */
-	{ 0x000F, 0x0000, 0x0000 }, /* R256   - Chip Revision */
-	{ 0x0074, 0x0074, 0x0000 }, /* R257   - Control Interface */
-	{ 0x0000, 0x0000, 0x0000 }, /* R258 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R259 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R260 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R261 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R262 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R263 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R264 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R265 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R266 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R267 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R268 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R269 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R270 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R271 */
-	{ 0x807F, 0x837F, 0x0000 }, /* R272   - Write Sequencer Ctrl (1) */
-	{ 0x017F, 0x0000, 0x0000 }, /* R273   - Write Sequencer Ctrl (2) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R274 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R275 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R276 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R277 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R278 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R279 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R280 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R281 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R282 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R283 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R284 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R285 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R286 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R287 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R288 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R289 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R290 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R291 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R292 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R293 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R294 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R295 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R296 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R297 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R298 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R299 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R300 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R301 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R302 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R303 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R304 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R305 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R306 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R307 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R308 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R309 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R310 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R311 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R312 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R313 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R314 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R315 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R316 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R317 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R318 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R319 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R320 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R321 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R322 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R323 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R324 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R325 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R326 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R327 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R328 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R329 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R330 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R331 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R332 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R333 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R334 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R335 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R336 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R337 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R338 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R339 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R340 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R341 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R342 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R343 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R344 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R345 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R346 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R347 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R348 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R349 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R350 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R351 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R352 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R353 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R354 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R355 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R356 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R357 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R358 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R359 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R360 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R361 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R362 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R363 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R364 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R365 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R366 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R367 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R368 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R369 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R370 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R371 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R372 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R373 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R374 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R375 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R376 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R377 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R378 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R379 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R380 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R381 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R382 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R383 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R384 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R385 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R386 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R387 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R388 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R389 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R390 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R391 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R392 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R393 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R394 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R395 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R396 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R397 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R398 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R399 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R400 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R401 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R402 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R403 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R404 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R405 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R406 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R407 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R408 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R409 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R410 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R411 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R412 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R413 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R414 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R415 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R416 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R417 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R418 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R419 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R420 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R421 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R422 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R423 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R424 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R425 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R426 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R427 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R428 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R429 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R430 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R431 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R432 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R433 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R434 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R435 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R436 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R437 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R438 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R439 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R440 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R441 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R442 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R443 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R444 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R445 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R446 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R447 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R448 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R449 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R450 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R451 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R452 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R453 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R454 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R455 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R456 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R457 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R458 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R459 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R460 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R461 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R462 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R463 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R464 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R465 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R466 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R467 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R468 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R469 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R470 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R471 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R472 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R473 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R474 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R475 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R476 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R477 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R478 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R479 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R480 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R481 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R482 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R483 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R484 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R485 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R486 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R487 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R488 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R489 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R490 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R491 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R492 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R493 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R494 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R495 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R496 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R497 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R498 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R499 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R500 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R501 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R502 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R503 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R504 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R505 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R506 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R507 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R508 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R509 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R510 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R511 */
-	{ 0x001F, 0x001F, 0x0000 }, /* R512   - AIF1 Clocking (1) */
-	{ 0x003F, 0x003F, 0x0000 }, /* R513   - AIF1 Clocking (2) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R514 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R515 */
-	{ 0x001F, 0x001F, 0x0000 }, /* R516   - AIF2 Clocking (1) */
-	{ 0x003F, 0x003F, 0x0000 }, /* R517   - AIF2 Clocking (2) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R518 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R519 */
-	{ 0x001F, 0x001F, 0x0000 }, /* R520   - Clocking (1) */
-	{ 0x0777, 0x0777, 0x0000 }, /* R521   - Clocking (2) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R522 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R523 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R524 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R525 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R526 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R527 */
-	{ 0x00FF, 0x00FF, 0x0000 }, /* R528   - AIF1 Rate */
-	{ 0x00FF, 0x00FF, 0x0000 }, /* R529   - AIF2 Rate */
-	{ 0x000F, 0x0000, 0x0000 }, /* R530   - Rate Status */
-	{ 0x0000, 0x0000, 0x0000 }, /* R531 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R532 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R533 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R534 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R535 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R536 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R537 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R538 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R539 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R540 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R541 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R542 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R543 */
-	{ 0x0007, 0x0007, 0x0000 }, /* R544   - FLL1 Control (1) */
-	{ 0x3F77, 0x3F77, 0x0000 }, /* R545   - FLL1 Control (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R546   - FLL1 Control (3) */
-	{ 0x7FEF, 0x7FEF, 0x0000 }, /* R547   - FLL1 Control (4) */
-	{ 0x1FDB, 0x1FDB, 0x0000 }, /* R548   - FLL1 Control (5) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R549 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R550 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R551 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R552 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R553 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R554 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R555 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R556 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R557 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R558 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R559 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R560 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R561 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R562 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R563 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R564 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R565 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R566 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R567 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R568 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R569 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R570 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R571 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R572 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R573 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R574 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R575 */
-	{ 0x0007, 0x0007, 0x0000 }, /* R576   - FLL2 Control (1) */
-	{ 0x3F77, 0x3F77, 0x0000 }, /* R577   - FLL2 Control (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R578   - FLL2 Control (3) */
-	{ 0x7FEF, 0x7FEF, 0x0000 }, /* R579   - FLL2 Control (4) */
-	{ 0x1FDB, 0x1FDB, 0x0000 }, /* R580   - FLL2 Control (5) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R581 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R582 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R583 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R584 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R585 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R586 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R587 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R588 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R589 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R590 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R591 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R592 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R593 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R594 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R595 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R596 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R597 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R598 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R599 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R600 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R601 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R602 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R603 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R604 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R605 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R606 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R607 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R608 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R609 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R610 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R611 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R612 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R613 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R614 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R615 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R616 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R617 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R618 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R619 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R620 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R621 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R622 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R623 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R624 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R625 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R626 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R627 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R628 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R629 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R630 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R631 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R632 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R633 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R634 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R635 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R636 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R637 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R638 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R639 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R640 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R641 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R642 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R643 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R644 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R645 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R646 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R647 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R648 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R649 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R650 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R651 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R652 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R653 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R654 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R655 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R656 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R657 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R658 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R659 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R660 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R661 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R662 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R663 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R664 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R665 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R666 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R667 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R668 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R669 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R670 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R671 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R672 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R673 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R674 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R675 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R676 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R677 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R678 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R679 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R680 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R681 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R682 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R683 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R684 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R685 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R686 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R687 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R688 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R689 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R690 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R691 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R692 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R693 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R694 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R695 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R696 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R697 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R698 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R699 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R700 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R701 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R702 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R703 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R704 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R705 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R706 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R707 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R708 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R709 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R710 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R711 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R712 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R713 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R714 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R715 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R716 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R717 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R718 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R719 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R720 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R721 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R722 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R723 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R724 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R725 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R726 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R727 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R728 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R729 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R730 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R731 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R732 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R733 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R734 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R735 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R736 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R737 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R738 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R739 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R740 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R741 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R742 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R743 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R744 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R745 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R746 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R747 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R748 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R749 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R750 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R751 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R752 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R753 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R754 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R755 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R756 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R757 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R758 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R759 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R760 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R761 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R762 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R763 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R764 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R765 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R766 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R767 */
-	{ 0xE1F8, 0xE1F8, 0x0000 }, /* R768   - AIF1 Control (1) */
-	{ 0xCD1F, 0xCD1F, 0x0000 }, /* R769   - AIF1 Control (2) */
-	{ 0xF000, 0xF000, 0x0000 }, /* R770   - AIF1 Master/Slave */
-	{ 0x01F0, 0x01F0, 0x0000 }, /* R771   - AIF1 BCLK */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R772   - AIF1ADC LRCLK */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R773   - AIF1DAC LRCLK */
-	{ 0x0003, 0x0003, 0x0000 }, /* R774   - AIF1DAC Data */
-	{ 0x0003, 0x0003, 0x0000 }, /* R775   - AIF1ADC Data */
-	{ 0x0000, 0x0000, 0x0000 }, /* R776 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R777 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R778 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R779 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R780 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R781 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R782 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R783 */
-	{ 0xF1F8, 0xF1F8, 0x0000 }, /* R784   - AIF2 Control (1) */
-	{ 0xFD1F, 0xFD1F, 0x0000 }, /* R785   - AIF2 Control (2) */
-	{ 0xF000, 0xF000, 0x0000 }, /* R786   - AIF2 Master/Slave */
-	{ 0x01F0, 0x01F0, 0x0000 }, /* R787   - AIF2 BCLK */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R788   - AIF2ADC LRCLK */
-	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R789   - AIF2DAC LRCLK */
-	{ 0x0003, 0x0003, 0x0000 }, /* R790   - AIF2DAC Data */
-	{ 0x0003, 0x0003, 0x0000 }, /* R791   - AIF2ADC Data */
-	{ 0x0000, 0x0000, 0x0000 }, /* R792 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R793 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R794 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R795 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R796 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R797 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R798 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R799 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R800 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R801 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R802 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R803 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R804 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R805 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R806 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R807 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R808 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R809 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R810 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R811 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R812 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R813 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R814 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R815 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R816 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R817 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R818 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R819 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R820 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R821 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R822 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R823 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R824 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R825 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R826 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R827 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R828 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R829 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R830 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R831 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R832 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R833 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R834 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R835 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R836 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R837 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R838 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R839 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R840 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R841 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R842 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R843 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R844 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R845 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R846 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R847 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R848 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R849 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R850 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R851 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R852 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R853 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R854 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R855 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R856 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R857 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R858 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R859 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R860 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R861 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R862 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R863 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R864 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R865 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R866 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R867 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R868 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R869 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R870 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R871 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R872 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R873 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R874 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R875 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R876 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R877 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R878 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R879 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R880 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R881 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R882 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R883 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R884 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R885 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R886 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R887 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R888 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R889 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R890 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R891 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R892 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R893 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R894 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R895 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R896 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R897 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R898 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R899 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R900 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R901 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R902 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R903 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R904 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R905 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R906 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R907 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R908 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R909 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R910 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R911 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R912 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R913 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R914 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R915 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R916 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R917 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R918 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R919 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R920 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R921 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R922 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R923 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R924 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R925 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R926 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R927 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R928 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R929 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R930 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R931 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R932 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R933 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R934 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R935 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R936 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R937 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R938 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R939 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R940 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R941 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R942 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R943 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R944 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R945 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R946 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R947 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R948 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R949 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R950 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R951 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R952 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R953 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R954 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R955 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R956 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R957 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R958 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R959 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R960 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R961 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R962 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R963 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R964 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R965 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R966 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R967 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R968 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R969 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R970 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R971 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R972 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R973 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R974 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R975 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R976 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R977 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R978 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R979 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R980 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R981 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R982 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R983 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R984 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R985 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R986 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R987 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R988 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R989 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R990 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R991 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R992 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R993 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R994 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R995 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R996 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R997 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R998 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R999 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1000 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1001 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1002 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1003 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1004 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1005 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1006 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1007 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1008 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1009 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1010 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1011 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1012 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1013 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1014 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1015 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1016 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1017 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1018 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1019 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1020 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1021 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1022 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1023 */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1024  - AIF1 ADC1 Left Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1025  - AIF1 ADC1 Right Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1026  - AIF1 DAC1 Left Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1027  - AIF1 DAC1 Right Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1028  - AIF1 ADC2 Left Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1029  - AIF1 ADC2 Right Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1030  - AIF1 DAC2 Left Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1031  - AIF1 DAC2 Right Volume */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1032 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1033 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1034 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1035 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1036 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1037 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1038 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1039 */
-	{ 0xF800, 0xF800, 0x0000 }, /* R1040  - AIF1 ADC1 Filters */
-	{ 0x7800, 0x7800, 0x0000 }, /* R1041  - AIF1 ADC2 Filters */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1042 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1043 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1044 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1045 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1046 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1047 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1048 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1049 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1050 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1051 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1052 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1053 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1054 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1055 */
-	{ 0x02B6, 0x02B6, 0x0000 }, /* R1056  - AIF1 DAC1 Filters (1) */
-	{ 0x3F00, 0x3F00, 0x0000 }, /* R1057  - AIF1 DAC1 Filters (2) */
-	{ 0x02B6, 0x02B6, 0x0000 }, /* R1058  - AIF1 DAC2 Filters (1) */
-	{ 0x3F00, 0x3F00, 0x0000 }, /* R1059  - AIF1 DAC2 Filters (2) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1060 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1061 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1062 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1063 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1064 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1065 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1066 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1067 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1068 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1069 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1070 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1071 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1072 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1073 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1074 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1075 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1076 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1077 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1078 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1079 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1080 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1081 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1082 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1083 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1084 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1085 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1086 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1087 */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1088  - AIF1 DRC1 (1) */
-	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R1089  - AIF1 DRC1 (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1090  - AIF1 DRC1 (3) */
-	{ 0x07FF, 0x07FF, 0x0000 }, /* R1091  - AIF1 DRC1 (4) */
-	{ 0x03FF, 0x03FF, 0x0000 }, /* R1092  - AIF1 DRC1 (5) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1093 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1094 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1095 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1096 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1097 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1098 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1099 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1100 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1101 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1102 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1103 */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1104  - AIF1 DRC2 (1) */
-	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R1105  - AIF1 DRC2 (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1106  - AIF1 DRC2 (3) */
-	{ 0x07FF, 0x07FF, 0x0000 }, /* R1107  - AIF1 DRC2 (4) */
-	{ 0x03FF, 0x03FF, 0x0000 }, /* R1108  - AIF1 DRC2 (5) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1109 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1110 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1111 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1112 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1113 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1114 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1115 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1116 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1117 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1118 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1119 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1120 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1121 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1122 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1123 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1124 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1125 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1126 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1127 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1128 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1129 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1130 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1131 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1132 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1133 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1134 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1135 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1136 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1137 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1138 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1139 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1140 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1141 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1142 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1143 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1144 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1145 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1146 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1147 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1148 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1149 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1150 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1151 */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1152  - AIF1 DAC1 EQ Gains (1) */
-	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R1153  - AIF1 DAC1 EQ Gains (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1154  - AIF1 DAC1 EQ Band 1 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1155  - AIF1 DAC1 EQ Band 1 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1156  - AIF1 DAC1 EQ Band 1 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1157  - AIF1 DAC1 EQ Band 2 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1158  - AIF1 DAC1 EQ Band 2 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1159  - AIF1 DAC1 EQ Band 2 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1160  - AIF1 DAC1 EQ Band 2 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1161  - AIF1 DAC1 EQ Band 3 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1162  - AIF1 DAC1 EQ Band 3 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1163  - AIF1 DAC1 EQ Band 3 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1164  - AIF1 DAC1 EQ Band 3 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1165  - AIF1 DAC1 EQ Band 4 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1166  - AIF1 DAC1 EQ Band 4 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1167  - AIF1 DAC1 EQ Band 4 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1168  - AIF1 DAC1 EQ Band 4 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1169  - AIF1 DAC1 EQ Band 5 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1170  - AIF1 DAC1 EQ Band 5 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1171  - AIF1 DAC1 EQ Band 5 PG */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1172 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1173 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1174 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1175 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1176 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1177 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1178 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1179 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1180 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1181 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1182 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1183 */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1184  - AIF1 DAC2 EQ Gains (1) */
-	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R1185  - AIF1 DAC2 EQ Gains (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1186  - AIF1 DAC2 EQ Band 1 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1187  - AIF1 DAC2 EQ Band 1 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1188  - AIF1 DAC2 EQ Band 1 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1189  - AIF1 DAC2 EQ Band 2 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1190  - AIF1 DAC2 EQ Band 2 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1191  - AIF1 DAC2 EQ Band 2 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1192  - AIF1 DAC2 EQ Band 2 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1193  - AIF1 DAC2 EQ Band 3 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1194  - AIF1 DAC2 EQ Band 3 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1195  - AIF1 DAC2 EQ Band 3 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1196  - AIF1 DAC2 EQ Band 3 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1197  - AIF1 DAC2 EQ Band 4 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1198  - AIF1 DAC2 EQ Band 4 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1199  - AIF1 DAC2 EQ Band 4 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1200  - AIF1 DAC2 EQ Band 4 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1201  - AIF1 DAC2 EQ Band 5 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1202  - AIF1 DAC2 EQ Band 5 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1203  - AIF1 DAC2 EQ Band 5 PG */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1204 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1205 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1206 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1207 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1208 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1209 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1210 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1211 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1212 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1213 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1214 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1215 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1216 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1217 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1218 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1219 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1220 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1221 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1222 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1223 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1224 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1225 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1226 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1227 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1228 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1229 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1230 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1231 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1232 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1233 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1234 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1235 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1236 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1237 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1238 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1239 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1240 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1241 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1242 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1243 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1244 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1245 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1246 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1247 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1248 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1249 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1250 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1251 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1252 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1253 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1254 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1255 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1256 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1257 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1258 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1259 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1260 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1261 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1262 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1263 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1264 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1265 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1266 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1267 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1268 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1269 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1270 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1271 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1272 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1273 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1274 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1275 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1276 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1277 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1278 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1279 */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1280  - AIF2 ADC Left Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1281  - AIF2 ADC Right Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1282  - AIF2 DAC Left Volume */
-	{ 0x00FF, 0x01FF, 0x0000 }, /* R1283  - AIF2 DAC Right Volume */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1284 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1285 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1286 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1287 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1288 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1289 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1290 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1291 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1292 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1293 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1294 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1295 */
-	{ 0xF800, 0xF800, 0x0000 }, /* R1296  - AIF2 ADC Filters */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1297 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1298 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1299 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1300 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1301 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1302 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1303 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1304 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1305 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1306 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1307 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1308 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1309 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1310 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1311 */
-	{ 0x02B6, 0x02B6, 0x0000 }, /* R1312  - AIF2 DAC Filters (1) */
-	{ 0x3F00, 0x3F00, 0x0000 }, /* R1313  - AIF2 DAC Filters (2) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1314 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1315 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1316 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1317 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1318 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1319 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1320 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1321 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1322 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1323 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1324 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1325 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1326 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1327 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1328 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1329 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1330 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1331 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1332 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1333 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1334 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1335 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1336 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1337 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1338 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1339 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1340 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1341 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1342 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1343 */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1344  - AIF2 DRC (1) */
-	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R1345  - AIF2 DRC (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1346  - AIF2 DRC (3) */
-	{ 0x07FF, 0x07FF, 0x0000 }, /* R1347  - AIF2 DRC (4) */
-	{ 0x03FF, 0x03FF, 0x0000 }, /* R1348  - AIF2 DRC (5) */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1349 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1350 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1351 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1352 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1353 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1354 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1355 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1356 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1357 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1358 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1359 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1360 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1361 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1362 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1363 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1364 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1365 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1366 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1367 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1368 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1369 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1370 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1371 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1372 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1373 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1374 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1375 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1376 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1377 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1378 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1379 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1380 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1381 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1382 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1383 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1384 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1385 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1386 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1387 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1388 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1389 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1390 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1391 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1392 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1393 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1394 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1395 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1396 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1397 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1398 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1399 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1400 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1401 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1402 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1403 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1404 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1405 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1406 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1407 */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1408  - AIF2 EQ Gains (1) */
-	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R1409  - AIF2 EQ Gains (2) */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1410  - AIF2 EQ Band 1 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1411  - AIF2 EQ Band 1 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1412  - AIF2 EQ Band 1 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1413  - AIF2 EQ Band 2 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1414  - AIF2 EQ Band 2 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1415  - AIF2 EQ Band 2 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1416  - AIF2 EQ Band 2 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1417  - AIF2 EQ Band 3 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1418  - AIF2 EQ Band 3 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1419  - AIF2 EQ Band 3 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1420  - AIF2 EQ Band 3 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1421  - AIF2 EQ Band 4 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1422  - AIF2 EQ Band 4 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1423  - AIF2 EQ Band 4 C */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1424  - AIF2 EQ Band 4 PG */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1425  - AIF2 EQ Band 5 A */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1426  - AIF2 EQ Band 5 B */
-	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1427  - AIF2 EQ Band 5 PG */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1428 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1429 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1430 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1431 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1432 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1433 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1434 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1435 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1436 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1437 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1438 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1439 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1440 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1441 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1442 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1443 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1444 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1445 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1446 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1447 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1448 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1449 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1450 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1451 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1452 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1453 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1454 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1455 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1456 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1457 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1458 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1459 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1460 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1461 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1462 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1463 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1464 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1465 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1466 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1467 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1468 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1469 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1470 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1471 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1472 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1473 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1474 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1475 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1476 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1477 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1478 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1479 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1480 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1481 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1482 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1483 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1484 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1485 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1486 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1487 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1488 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1489 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1490 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1491 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1492 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1493 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1494 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1495 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1496 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1497 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1498 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1499 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1500 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1501 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1502 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1503 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1504 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1505 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1506 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1507 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1508 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1509 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1510 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1511 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1512 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1513 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1514 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1515 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1516 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1517 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1518 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1519 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1520 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1521 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1522 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1523 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1524 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1525 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1526 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1527 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1528 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1529 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1530 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1531 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1532 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1533 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1534 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1535 */
-	{ 0x01EF, 0x01EF, 0x0000 }, /* R1536  - DAC1 Mixer Volumes */
-	{ 0x0037, 0x0037, 0x0000 }, /* R1537  - DAC1 Left Mixer Routing */
-	{ 0x0037, 0x0037, 0x0000 }, /* R1538  - DAC1 Right Mixer Routing */
-	{ 0x01EF, 0x01EF, 0x0000 }, /* R1539  - DAC2 Mixer Volumes */
-	{ 0x0037, 0x0037, 0x0000 }, /* R1540  - DAC2 Left Mixer Routing */
-	{ 0x0037, 0x0037, 0x0000 }, /* R1541  - DAC2 Right Mixer Routing */
-	{ 0x0003, 0x0003, 0x0000 }, /* R1542  - AIF1 ADC1 Left Mixer Routing */
-	{ 0x0003, 0x0003, 0x0000 }, /* R1543  - AIF1 ADC1 Right Mixer Routing */
-	{ 0x0003, 0x0003, 0x0000 }, /* R1544  - AIF1 ADC2 Left Mixer Routing */
-	{ 0x0003, 0x0003, 0x0000 }, /* R1545  - AIF1 ADC2 Right mixer Routing */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1546 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1547 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1548 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1549 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1550 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1551 */
-	{ 0x02FF, 0x03FF, 0x0000 }, /* R1552  - DAC1 Left Volume */
-	{ 0x02FF, 0x03FF, 0x0000 }, /* R1553  - DAC1 Right Volume */
-	{ 0x02FF, 0x03FF, 0x0000 }, /* R1554  - DAC2 Left Volume */
-	{ 0x02FF, 0x03FF, 0x0000 }, /* R1555  - DAC2 Right Volume */
-	{ 0x0003, 0x0003, 0x0000 }, /* R1556  - DAC Softmute */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1557 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1558 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1559 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1560 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1561 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1562 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1563 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1564 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1565 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1566 */
-	{ 0x0000, 0x0000, 0x0000 }, /* R1567 */
-	{ 0x0003, 0x0003, 0x0000 }, /* R1568  - Oversampling */
-	{ 0x03C3, 0x03C3, 0x0000 }, /* R1569  - Sidetone */
+	{ 0xFFFF, 0xFFFF }, /* R0     - Software Reset */
+	{ 0x3B37, 0x3B37 }, /* R1     - Power Management (1) */
+	{ 0x6BF0, 0x6BF0 }, /* R2     - Power Management (2) */
+	{ 0x3FF0, 0x3FF0 }, /* R3     - Power Management (3) */
+	{ 0x3F3F, 0x3F3F }, /* R4     - Power Management (4) */
+	{ 0x3F0F, 0x3F0F }, /* R5     - Power Management (5) */
+	{ 0x003F, 0x003F }, /* R6     - Power Management (6) */
+	{ 0x0000, 0x0000 }, /* R7 */
+	{ 0x0000, 0x0000 }, /* R8 */
+	{ 0x0000, 0x0000 }, /* R9 */
+	{ 0x0000, 0x0000 }, /* R10 */
+	{ 0x0000, 0x0000 }, /* R11 */
+	{ 0x0000, 0x0000 }, /* R12 */
+	{ 0x0000, 0x0000 }, /* R13 */
+	{ 0x0000, 0x0000 }, /* R14 */
+	{ 0x0000, 0x0000 }, /* R15 */
+	{ 0x0000, 0x0000 }, /* R16 */
+	{ 0x0000, 0x0000 }, /* R17 */
+	{ 0x0000, 0x0000 }, /* R18 */
+	{ 0x0000, 0x0000 }, /* R19 */
+	{ 0x0000, 0x0000 }, /* R20 */
+	{ 0x01C0, 0x01C0 }, /* R21    - Input Mixer (1) */
+	{ 0x0000, 0x0000 }, /* R22 */
+	{ 0x0000, 0x0000 }, /* R23 */
+	{ 0x00DF, 0x01DF }, /* R24    - Left Line Input 1&2 Volume */
+	{ 0x00DF, 0x01DF }, /* R25    - Left Line Input 3&4 Volume */
+	{ 0x00DF, 0x01DF }, /* R26    - Right Line Input 1&2 Volume */
+	{ 0x00DF, 0x01DF }, /* R27    - Right Line Input 3&4 Volume */
+	{ 0x00FF, 0x01FF }, /* R28    - Left Output Volume */
+	{ 0x00FF, 0x01FF }, /* R29    - Right Output Volume */
+	{ 0x0077, 0x0077 }, /* R30    - Line Outputs Volume */
+	{ 0x0030, 0x0030 }, /* R31    - HPOUT2 Volume */
+	{ 0x00FF, 0x01FF }, /* R32    - Left OPGA Volume */
+	{ 0x00FF, 0x01FF }, /* R33    - Right OPGA Volume */
+	{ 0x007F, 0x007F }, /* R34    - SPKMIXL Attenuation */
+	{ 0x017F, 0x017F }, /* R35    - SPKMIXR Attenuation */
+	{ 0x003F, 0x003F }, /* R36    - SPKOUT Mixers */
+	{ 0x003F, 0x003F }, /* R37    - ClassD */
+	{ 0x00FF, 0x01FF }, /* R38    - Speaker Volume Left */
+	{ 0x00FF, 0x01FF }, /* R39    - Speaker Volume Right */
+	{ 0x00FF, 0x00FF }, /* R40    - Input Mixer (2) */
+	{ 0x01B7, 0x01B7 }, /* R41    - Input Mixer (3) */
+	{ 0x01B7, 0x01B7 }, /* R42    - Input Mixer (4) */
+	{ 0x01C7, 0x01C7 }, /* R43    - Input Mixer (5) */
+	{ 0x01C7, 0x01C7 }, /* R44    - Input Mixer (6) */
+	{ 0x01FF, 0x01FF }, /* R45    - Output Mixer (1) */
+	{ 0x01FF, 0x01FF }, /* R46    - Output Mixer (2) */
+	{ 0x0FFF, 0x0FFF }, /* R47    - Output Mixer (3) */
+	{ 0x0FFF, 0x0FFF }, /* R48    - Output Mixer (4) */
+	{ 0x0FFF, 0x0FFF }, /* R49    - Output Mixer (5) */
+	{ 0x0FFF, 0x0FFF }, /* R50    - Output Mixer (6) */
+	{ 0x0038, 0x0038 }, /* R51    - HPOUT2 Mixer */
+	{ 0x0077, 0x0077 }, /* R52    - Line Mixer (1) */
+	{ 0x0077, 0x0077 }, /* R53    - Line Mixer (2) */
+	{ 0x03FF, 0x03FF }, /* R54    - Speaker Mixer */
+	{ 0x00C1, 0x00C1 }, /* R55    - Additional Control */
+	{ 0x00F0, 0x00F0 }, /* R56    - AntiPOP (1) */
+	{ 0x01EF, 0x01EF }, /* R57    - AntiPOP (2) */
+	{ 0x00FF, 0x00FF }, /* R58    - MICBIAS */
+	{ 0x000F, 0x000F }, /* R59    - LDO 1 */
+	{ 0x0007, 0x0007 }, /* R60    - LDO 2 */
+	{ 0x0000, 0x0000 }, /* R61 */
+	{ 0x0000, 0x0000 }, /* R62 */
+	{ 0x0000, 0x0000 }, /* R63 */
+	{ 0x0000, 0x0000 }, /* R64 */
+	{ 0x0000, 0x0000 }, /* R65 */
+	{ 0x0000, 0x0000 }, /* R66 */
+	{ 0x0000, 0x0000 }, /* R67 */
+	{ 0x0000, 0x0000 }, /* R68 */
+	{ 0x0000, 0x0000 }, /* R69 */
+	{ 0x0000, 0x0000 }, /* R70 */
+	{ 0x0000, 0x0000 }, /* R71 */
+	{ 0x0000, 0x0000 }, /* R72 */
+	{ 0x0000, 0x0000 }, /* R73 */
+	{ 0x0000, 0x0000 }, /* R74 */
+	{ 0x0000, 0x0000 }, /* R75 */
+	{ 0x8000, 0x8000 }, /* R76    - Charge Pump (1) */
+	{ 0x0000, 0x0000 }, /* R77 */
+	{ 0x0000, 0x0000 }, /* R78 */
+	{ 0x0000, 0x0000 }, /* R79 */
+	{ 0x0000, 0x0000 }, /* R80 */
+	{ 0x0301, 0x0301 }, /* R81    - Class W (1) */
+	{ 0x0000, 0x0000 }, /* R82 */
+	{ 0x0000, 0x0000 }, /* R83 */
+	{ 0x333F, 0x333F }, /* R84    - DC Servo (1) */
+	{ 0x0FEF, 0x0FEF }, /* R85    - DC Servo (2) */
+	{ 0x0000, 0x0000 }, /* R86 */
+	{ 0xFFFF, 0xFFFF }, /* R87    - DC Servo (4) */
+	{ 0x0333, 0x0000 }, /* R88    - DC Servo Readback */
+	{ 0x0000, 0x0000 }, /* R89 */
+	{ 0x0000, 0x0000 }, /* R90 */
+	{ 0x0000, 0x0000 }, /* R91 */
+	{ 0x0000, 0x0000 }, /* R92 */
+	{ 0x0000, 0x0000 }, /* R93 */
+	{ 0x0000, 0x0000 }, /* R94 */
+	{ 0x0000, 0x0000 }, /* R95 */
+	{ 0x00EE, 0x00EE }, /* R96    - Analogue HP (1) */
+	{ 0x0000, 0x0000 }, /* R97 */
+	{ 0x0000, 0x0000 }, /* R98 */
+	{ 0x0000, 0x0000 }, /* R99 */
+	{ 0x0000, 0x0000 }, /* R100 */
+	{ 0x0000, 0x0000 }, /* R101 */
+	{ 0x0000, 0x0000 }, /* R102 */
+	{ 0x0000, 0x0000 }, /* R103 */
+	{ 0x0000, 0x0000 }, /* R104 */
+	{ 0x0000, 0x0000 }, /* R105 */
+	{ 0x0000, 0x0000 }, /* R106 */
+	{ 0x0000, 0x0000 }, /* R107 */
+	{ 0x0000, 0x0000 }, /* R108 */
+	{ 0x0000, 0x0000 }, /* R109 */
+	{ 0x0000, 0x0000 }, /* R110 */
+	{ 0x0000, 0x0000 }, /* R111 */
+	{ 0x0000, 0x0000 }, /* R112 */
+	{ 0x0000, 0x0000 }, /* R113 */
+	{ 0x0000, 0x0000 }, /* R114 */
+	{ 0x0000, 0x0000 }, /* R115 */
+	{ 0x0000, 0x0000 }, /* R116 */
+	{ 0x0000, 0x0000 }, /* R117 */
+	{ 0x0000, 0x0000 }, /* R118 */
+	{ 0x0000, 0x0000 }, /* R119 */
+	{ 0x0000, 0x0000 }, /* R120 */
+	{ 0x0000, 0x0000 }, /* R121 */
+	{ 0x0000, 0x0000 }, /* R122 */
+	{ 0x0000, 0x0000 }, /* R123 */
+	{ 0x0000, 0x0000 }, /* R124 */
+	{ 0x0000, 0x0000 }, /* R125 */
+	{ 0x0000, 0x0000 }, /* R126 */
+	{ 0x0000, 0x0000 }, /* R127 */
+	{ 0x0000, 0x0000 }, /* R128 */
+	{ 0x0000, 0x0000 }, /* R129 */
+	{ 0x0000, 0x0000 }, /* R130 */
+	{ 0x0000, 0x0000 }, /* R131 */
+	{ 0x0000, 0x0000 }, /* R132 */
+	{ 0x0000, 0x0000 }, /* R133 */
+	{ 0x0000, 0x0000 }, /* R134 */
+	{ 0x0000, 0x0000 }, /* R135 */
+	{ 0x0000, 0x0000 }, /* R136 */
+	{ 0x0000, 0x0000 }, /* R137 */
+	{ 0x0000, 0x0000 }, /* R138 */
+	{ 0x0000, 0x0000 }, /* R139 */
+	{ 0x0000, 0x0000 }, /* R140 */
+	{ 0x0000, 0x0000 }, /* R141 */
+	{ 0x0000, 0x0000 }, /* R142 */
+	{ 0x0000, 0x0000 }, /* R143 */
+	{ 0x0000, 0x0000 }, /* R144 */
+	{ 0x0000, 0x0000 }, /* R145 */
+	{ 0x0000, 0x0000 }, /* R146 */
+	{ 0x0000, 0x0000 }, /* R147 */
+	{ 0x0000, 0x0000 }, /* R148 */
+	{ 0x0000, 0x0000 }, /* R149 */
+	{ 0x0000, 0x0000 }, /* R150 */
+	{ 0x0000, 0x0000 }, /* R151 */
+	{ 0x0000, 0x0000 }, /* R152 */
+	{ 0x0000, 0x0000 }, /* R153 */
+	{ 0x0000, 0x0000 }, /* R154 */
+	{ 0x0000, 0x0000 }, /* R155 */
+	{ 0x0000, 0x0000 }, /* R156 */
+	{ 0x0000, 0x0000 }, /* R157 */
+	{ 0x0000, 0x0000 }, /* R158 */
+	{ 0x0000, 0x0000 }, /* R159 */
+	{ 0x0000, 0x0000 }, /* R160 */
+	{ 0x0000, 0x0000 }, /* R161 */
+	{ 0x0000, 0x0000 }, /* R162 */
+	{ 0x0000, 0x0000 }, /* R163 */
+	{ 0x0000, 0x0000 }, /* R164 */
+	{ 0x0000, 0x0000 }, /* R165 */
+	{ 0x0000, 0x0000 }, /* R166 */
+	{ 0x0000, 0x0000 }, /* R167 */
+	{ 0x0000, 0x0000 }, /* R168 */
+	{ 0x0000, 0x0000 }, /* R169 */
+	{ 0x0000, 0x0000 }, /* R170 */
+	{ 0x0000, 0x0000 }, /* R171 */
+	{ 0x0000, 0x0000 }, /* R172 */
+	{ 0x0000, 0x0000 }, /* R173 */
+	{ 0x0000, 0x0000 }, /* R174 */
+	{ 0x0000, 0x0000 }, /* R175 */
+	{ 0x0000, 0x0000 }, /* R176 */
+	{ 0x0000, 0x0000 }, /* R177 */
+	{ 0x0000, 0x0000 }, /* R178 */
+	{ 0x0000, 0x0000 }, /* R179 */
+	{ 0x0000, 0x0000 }, /* R180 */
+	{ 0x0000, 0x0000 }, /* R181 */
+	{ 0x0000, 0x0000 }, /* R182 */
+	{ 0x0000, 0x0000 }, /* R183 */
+	{ 0x0000, 0x0000 }, /* R184 */
+	{ 0x0000, 0x0000 }, /* R185 */
+	{ 0x0000, 0x0000 }, /* R186 */
+	{ 0x0000, 0x0000 }, /* R187 */
+	{ 0x0000, 0x0000 }, /* R188 */
+	{ 0x0000, 0x0000 }, /* R189 */
+	{ 0x0000, 0x0000 }, /* R190 */
+	{ 0x0000, 0x0000 }, /* R191 */
+	{ 0x0000, 0x0000 }, /* R192 */
+	{ 0x0000, 0x0000 }, /* R193 */
+	{ 0x0000, 0x0000 }, /* R194 */
+	{ 0x0000, 0x0000 }, /* R195 */
+	{ 0x0000, 0x0000 }, /* R196 */
+	{ 0x0000, 0x0000 }, /* R197 */
+	{ 0x0000, 0x0000 }, /* R198 */
+	{ 0x0000, 0x0000 }, /* R199 */
+	{ 0x0000, 0x0000 }, /* R200 */
+	{ 0x0000, 0x0000 }, /* R201 */
+	{ 0x0000, 0x0000 }, /* R202 */
+	{ 0x0000, 0x0000 }, /* R203 */
+	{ 0x0000, 0x0000 }, /* R204 */
+	{ 0x0000, 0x0000 }, /* R205 */
+	{ 0x0000, 0x0000 }, /* R206 */
+	{ 0x0000, 0x0000 }, /* R207 */
+	{ 0x0000, 0x0000 }, /* R208 */
+	{ 0x0000, 0x0000 }, /* R209 */
+	{ 0x0000, 0x0000 }, /* R210 */
+	{ 0x0000, 0x0000 }, /* R211 */
+	{ 0x0000, 0x0000 }, /* R212 */
+	{ 0x0000, 0x0000 }, /* R213 */
+	{ 0x0000, 0x0000 }, /* R214 */
+	{ 0x0000, 0x0000 }, /* R215 */
+	{ 0x0000, 0x0000 }, /* R216 */
+	{ 0x0000, 0x0000 }, /* R217 */
+	{ 0x0000, 0x0000 }, /* R218 */
+	{ 0x0000, 0x0000 }, /* R219 */
+	{ 0x0000, 0x0000 }, /* R220 */
+	{ 0x0000, 0x0000 }, /* R221 */
+	{ 0x0000, 0x0000 }, /* R222 */
+	{ 0x0000, 0x0000 }, /* R223 */
+	{ 0x0000, 0x0000 }, /* R224 */
+	{ 0x0000, 0x0000 }, /* R225 */
+	{ 0x0000, 0x0000 }, /* R226 */
+	{ 0x0000, 0x0000 }, /* R227 */
+	{ 0x0000, 0x0000 }, /* R228 */
+	{ 0x0000, 0x0000 }, /* R229 */
+	{ 0x0000, 0x0000 }, /* R230 */
+	{ 0x0000, 0x0000 }, /* R231 */
+	{ 0x0000, 0x0000 }, /* R232 */
+	{ 0x0000, 0x0000 }, /* R233 */
+	{ 0x0000, 0x0000 }, /* R234 */
+	{ 0x0000, 0x0000 }, /* R235 */
+	{ 0x0000, 0x0000 }, /* R236 */
+	{ 0x0000, 0x0000 }, /* R237 */
+	{ 0x0000, 0x0000 }, /* R238 */
+	{ 0x0000, 0x0000 }, /* R239 */
+	{ 0x0000, 0x0000 }, /* R240 */
+	{ 0x0000, 0x0000 }, /* R241 */
+	{ 0x0000, 0x0000 }, /* R242 */
+	{ 0x0000, 0x0000 }, /* R243 */
+	{ 0x0000, 0x0000 }, /* R244 */
+	{ 0x0000, 0x0000 }, /* R245 */
+	{ 0x0000, 0x0000 }, /* R246 */
+	{ 0x0000, 0x0000 }, /* R247 */
+	{ 0x0000, 0x0000 }, /* R248 */
+	{ 0x0000, 0x0000 }, /* R249 */
+	{ 0x0000, 0x0000 }, /* R250 */
+	{ 0x0000, 0x0000 }, /* R251 */
+	{ 0x0000, 0x0000 }, /* R252 */
+	{ 0x0000, 0x0000 }, /* R253 */
+	{ 0x0000, 0x0000 }, /* R254 */
+	{ 0x0000, 0x0000 }, /* R255 */
+	{ 0x000F, 0x0000 }, /* R256   - Chip Revision */
+	{ 0x0074, 0x0074 }, /* R257   - Control Interface */
+	{ 0x0000, 0x0000 }, /* R258 */
+	{ 0x0000, 0x0000 }, /* R259 */
+	{ 0x0000, 0x0000 }, /* R260 */
+	{ 0x0000, 0x0000 }, /* R261 */
+	{ 0x0000, 0x0000 }, /* R262 */
+	{ 0x0000, 0x0000 }, /* R263 */
+	{ 0x0000, 0x0000 }, /* R264 */
+	{ 0x0000, 0x0000 }, /* R265 */
+	{ 0x0000, 0x0000 }, /* R266 */
+	{ 0x0000, 0x0000 }, /* R267 */
+	{ 0x0000, 0x0000 }, /* R268 */
+	{ 0x0000, 0x0000 }, /* R269 */
+	{ 0x0000, 0x0000 }, /* R270 */
+	{ 0x0000, 0x0000 }, /* R271 */
+	{ 0x807F, 0x837F }, /* R272   - Write Sequencer Ctrl (1) */
+	{ 0x017F, 0x0000 }, /* R273   - Write Sequencer Ctrl (2) */
+	{ 0x0000, 0x0000 }, /* R274 */
+	{ 0x0000, 0x0000 }, /* R275 */
+	{ 0x0000, 0x0000 }, /* R276 */
+	{ 0x0000, 0x0000 }, /* R277 */
+	{ 0x0000, 0x0000 }, /* R278 */
+	{ 0x0000, 0x0000 }, /* R279 */
+	{ 0x0000, 0x0000 }, /* R280 */
+	{ 0x0000, 0x0000 }, /* R281 */
+	{ 0x0000, 0x0000 }, /* R282 */
+	{ 0x0000, 0x0000 }, /* R283 */
+	{ 0x0000, 0x0000 }, /* R284 */
+	{ 0x0000, 0x0000 }, /* R285 */
+	{ 0x0000, 0x0000 }, /* R286 */
+	{ 0x0000, 0x0000 }, /* R287 */
+	{ 0x0000, 0x0000 }, /* R288 */
+	{ 0x0000, 0x0000 }, /* R289 */
+	{ 0x0000, 0x0000 }, /* R290 */
+	{ 0x0000, 0x0000 }, /* R291 */
+	{ 0x0000, 0x0000 }, /* R292 */
+	{ 0x0000, 0x0000 }, /* R293 */
+	{ 0x0000, 0x0000 }, /* R294 */
+	{ 0x0000, 0x0000 }, /* R295 */
+	{ 0x0000, 0x0000 }, /* R296 */
+	{ 0x0000, 0x0000 }, /* R297 */
+	{ 0x0000, 0x0000 }, /* R298 */
+	{ 0x0000, 0x0000 }, /* R299 */
+	{ 0x0000, 0x0000 }, /* R300 */
+	{ 0x0000, 0x0000 }, /* R301 */
+	{ 0x0000, 0x0000 }, /* R302 */
+	{ 0x0000, 0x0000 }, /* R303 */
+	{ 0x0000, 0x0000 }, /* R304 */
+	{ 0x0000, 0x0000 }, /* R305 */
+	{ 0x0000, 0x0000 }, /* R306 */
+	{ 0x0000, 0x0000 }, /* R307 */
+	{ 0x0000, 0x0000 }, /* R308 */
+	{ 0x0000, 0x0000 }, /* R309 */
+	{ 0x0000, 0x0000 }, /* R310 */
+	{ 0x0000, 0x0000 }, /* R311 */
+	{ 0x0000, 0x0000 }, /* R312 */
+	{ 0x0000, 0x0000 }, /* R313 */
+	{ 0x0000, 0x0000 }, /* R314 */
+	{ 0x0000, 0x0000 }, /* R315 */
+	{ 0x0000, 0x0000 }, /* R316 */
+	{ 0x0000, 0x0000 }, /* R317 */
+	{ 0x0000, 0x0000 }, /* R318 */
+	{ 0x0000, 0x0000 }, /* R319 */
+	{ 0x0000, 0x0000 }, /* R320 */
+	{ 0x0000, 0x0000 }, /* R321 */
+	{ 0x0000, 0x0000 }, /* R322 */
+	{ 0x0000, 0x0000 }, /* R323 */
+	{ 0x0000, 0x0000 }, /* R324 */
+	{ 0x0000, 0x0000 }, /* R325 */
+	{ 0x0000, 0x0000 }, /* R326 */
+	{ 0x0000, 0x0000 }, /* R327 */
+	{ 0x0000, 0x0000 }, /* R328 */
+	{ 0x0000, 0x0000 }, /* R329 */
+	{ 0x0000, 0x0000 }, /* R330 */
+	{ 0x0000, 0x0000 }, /* R331 */
+	{ 0x0000, 0x0000 }, /* R332 */
+	{ 0x0000, 0x0000 }, /* R333 */
+	{ 0x0000, 0x0000 }, /* R334 */
+	{ 0x0000, 0x0000 }, /* R335 */
+	{ 0x0000, 0x0000 }, /* R336 */
+	{ 0x0000, 0x0000 }, /* R337 */
+	{ 0x0000, 0x0000 }, /* R338 */
+	{ 0x0000, 0x0000 }, /* R339 */
+	{ 0x0000, 0x0000 }, /* R340 */
+	{ 0x0000, 0x0000 }, /* R341 */
+	{ 0x0000, 0x0000 }, /* R342 */
+	{ 0x0000, 0x0000 }, /* R343 */
+	{ 0x0000, 0x0000 }, /* R344 */
+	{ 0x0000, 0x0000 }, /* R345 */
+	{ 0x0000, 0x0000 }, /* R346 */
+	{ 0x0000, 0x0000 }, /* R347 */
+	{ 0x0000, 0x0000 }, /* R348 */
+	{ 0x0000, 0x0000 }, /* R349 */
+	{ 0x0000, 0x0000 }, /* R350 */
+	{ 0x0000, 0x0000 }, /* R351 */
+	{ 0x0000, 0x0000 }, /* R352 */
+	{ 0x0000, 0x0000 }, /* R353 */
+	{ 0x0000, 0x0000 }, /* R354 */
+	{ 0x0000, 0x0000 }, /* R355 */
+	{ 0x0000, 0x0000 }, /* R356 */
+	{ 0x0000, 0x0000 }, /* R357 */
+	{ 0x0000, 0x0000 }, /* R358 */
+	{ 0x0000, 0x0000 }, /* R359 */
+	{ 0x0000, 0x0000 }, /* R360 */
+	{ 0x0000, 0x0000 }, /* R361 */
+	{ 0x0000, 0x0000 }, /* R362 */
+	{ 0x0000, 0x0000 }, /* R363 */
+	{ 0x0000, 0x0000 }, /* R364 */
+	{ 0x0000, 0x0000 }, /* R365 */
+	{ 0x0000, 0x0000 }, /* R366 */
+	{ 0x0000, 0x0000 }, /* R367 */
+	{ 0x0000, 0x0000 }, /* R368 */
+	{ 0x0000, 0x0000 }, /* R369 */
+	{ 0x0000, 0x0000 }, /* R370 */
+	{ 0x0000, 0x0000 }, /* R371 */
+	{ 0x0000, 0x0000 }, /* R372 */
+	{ 0x0000, 0x0000 }, /* R373 */
+	{ 0x0000, 0x0000 }, /* R374 */
+	{ 0x0000, 0x0000 }, /* R375 */
+	{ 0x0000, 0x0000 }, /* R376 */
+	{ 0x0000, 0x0000 }, /* R377 */
+	{ 0x0000, 0x0000 }, /* R378 */
+	{ 0x0000, 0x0000 }, /* R379 */
+	{ 0x0000, 0x0000 }, /* R380 */
+	{ 0x0000, 0x0000 }, /* R381 */
+	{ 0x0000, 0x0000 }, /* R382 */
+	{ 0x0000, 0x0000 }, /* R383 */
+	{ 0x0000, 0x0000 }, /* R384 */
+	{ 0x0000, 0x0000 }, /* R385 */
+	{ 0x0000, 0x0000 }, /* R386 */
+	{ 0x0000, 0x0000 }, /* R387 */
+	{ 0x0000, 0x0000 }, /* R388 */
+	{ 0x0000, 0x0000 }, /* R389 */
+	{ 0x0000, 0x0000 }, /* R390 */
+	{ 0x0000, 0x0000 }, /* R391 */
+	{ 0x0000, 0x0000 }, /* R392 */
+	{ 0x0000, 0x0000 }, /* R393 */
+	{ 0x0000, 0x0000 }, /* R394 */
+	{ 0x0000, 0x0000 }, /* R395 */
+	{ 0x0000, 0x0000 }, /* R396 */
+	{ 0x0000, 0x0000 }, /* R397 */
+	{ 0x0000, 0x0000 }, /* R398 */
+	{ 0x0000, 0x0000 }, /* R399 */
+	{ 0x0000, 0x0000 }, /* R400 */
+	{ 0x0000, 0x0000 }, /* R401 */
+	{ 0x0000, 0x0000 }, /* R402 */
+	{ 0x0000, 0x0000 }, /* R403 */
+	{ 0x0000, 0x0000 }, /* R404 */
+	{ 0x0000, 0x0000 }, /* R405 */
+	{ 0x0000, 0x0000 }, /* R406 */
+	{ 0x0000, 0x0000 }, /* R407 */
+	{ 0x0000, 0x0000 }, /* R408 */
+	{ 0x0000, 0x0000 }, /* R409 */
+	{ 0x0000, 0x0000 }, /* R410 */
+	{ 0x0000, 0x0000 }, /* R411 */
+	{ 0x0000, 0x0000 }, /* R412 */
+	{ 0x0000, 0x0000 }, /* R413 */
+	{ 0x0000, 0x0000 }, /* R414 */
+	{ 0x0000, 0x0000 }, /* R415 */
+	{ 0x0000, 0x0000 }, /* R416 */
+	{ 0x0000, 0x0000 }, /* R417 */
+	{ 0x0000, 0x0000 }, /* R418 */
+	{ 0x0000, 0x0000 }, /* R419 */
+	{ 0x0000, 0x0000 }, /* R420 */
+	{ 0x0000, 0x0000 }, /* R421 */
+	{ 0x0000, 0x0000 }, /* R422 */
+	{ 0x0000, 0x0000 }, /* R423 */
+	{ 0x0000, 0x0000 }, /* R424 */
+	{ 0x0000, 0x0000 }, /* R425 */
+	{ 0x0000, 0x0000 }, /* R426 */
+	{ 0x0000, 0x0000 }, /* R427 */
+	{ 0x0000, 0x0000 }, /* R428 */
+	{ 0x0000, 0x0000 }, /* R429 */
+	{ 0x0000, 0x0000 }, /* R430 */
+	{ 0x0000, 0x0000 }, /* R431 */
+	{ 0x0000, 0x0000 }, /* R432 */
+	{ 0x0000, 0x0000 }, /* R433 */
+	{ 0x0000, 0x0000 }, /* R434 */
+	{ 0x0000, 0x0000 }, /* R435 */
+	{ 0x0000, 0x0000 }, /* R436 */
+	{ 0x0000, 0x0000 }, /* R437 */
+	{ 0x0000, 0x0000 }, /* R438 */
+	{ 0x0000, 0x0000 }, /* R439 */
+	{ 0x0000, 0x0000 }, /* R440 */
+	{ 0x0000, 0x0000 }, /* R441 */
+	{ 0x0000, 0x0000 }, /* R442 */
+	{ 0x0000, 0x0000 }, /* R443 */
+	{ 0x0000, 0x0000 }, /* R444 */
+	{ 0x0000, 0x0000 }, /* R445 */
+	{ 0x0000, 0x0000 }, /* R446 */
+	{ 0x0000, 0x0000 }, /* R447 */
+	{ 0x0000, 0x0000 }, /* R448 */
+	{ 0x0000, 0x0000 }, /* R449 */
+	{ 0x0000, 0x0000 }, /* R450 */
+	{ 0x0000, 0x0000 }, /* R451 */
+	{ 0x0000, 0x0000 }, /* R452 */
+	{ 0x0000, 0x0000 }, /* R453 */
+	{ 0x0000, 0x0000 }, /* R454 */
+	{ 0x0000, 0x0000 }, /* R455 */
+	{ 0x0000, 0x0000 }, /* R456 */
+	{ 0x0000, 0x0000 }, /* R457 */
+	{ 0x0000, 0x0000 }, /* R458 */
+	{ 0x0000, 0x0000 }, /* R459 */
+	{ 0x0000, 0x0000 }, /* R460 */
+	{ 0x0000, 0x0000 }, /* R461 */
+	{ 0x0000, 0x0000 }, /* R462 */
+	{ 0x0000, 0x0000 }, /* R463 */
+	{ 0x0000, 0x0000 }, /* R464 */
+	{ 0x0000, 0x0000 }, /* R465 */
+	{ 0x0000, 0x0000 }, /* R466 */
+	{ 0x0000, 0x0000 }, /* R467 */
+	{ 0x0000, 0x0000 }, /* R468 */
+	{ 0x0000, 0x0000 }, /* R469 */
+	{ 0x0000, 0x0000 }, /* R470 */
+	{ 0x0000, 0x0000 }, /* R471 */
+	{ 0x0000, 0x0000 }, /* R472 */
+	{ 0x0000, 0x0000 }, /* R473 */
+	{ 0x0000, 0x0000 }, /* R474 */
+	{ 0x0000, 0x0000 }, /* R475 */
+	{ 0x0000, 0x0000 }, /* R476 */
+	{ 0x0000, 0x0000 }, /* R477 */
+	{ 0x0000, 0x0000 }, /* R478 */
+	{ 0x0000, 0x0000 }, /* R479 */
+	{ 0x0000, 0x0000 }, /* R480 */
+	{ 0x0000, 0x0000 }, /* R481 */
+	{ 0x0000, 0x0000 }, /* R482 */
+	{ 0x0000, 0x0000 }, /* R483 */
+	{ 0x0000, 0x0000 }, /* R484 */
+	{ 0x0000, 0x0000 }, /* R485 */
+	{ 0x0000, 0x0000 }, /* R486 */
+	{ 0x0000, 0x0000 }, /* R487 */
+	{ 0x0000, 0x0000 }, /* R488 */
+	{ 0x0000, 0x0000 }, /* R489 */
+	{ 0x0000, 0x0000 }, /* R490 */
+	{ 0x0000, 0x0000 }, /* R491 */
+	{ 0x0000, 0x0000 }, /* R492 */
+	{ 0x0000, 0x0000 }, /* R493 */
+	{ 0x0000, 0x0000 }, /* R494 */
+	{ 0x0000, 0x0000 }, /* R495 */
+	{ 0x0000, 0x0000 }, /* R496 */
+	{ 0x0000, 0x0000 }, /* R497 */
+	{ 0x0000, 0x0000 }, /* R498 */
+	{ 0x0000, 0x0000 }, /* R499 */
+	{ 0x0000, 0x0000 }, /* R500 */
+	{ 0x0000, 0x0000 }, /* R501 */
+	{ 0x0000, 0x0000 }, /* R502 */
+	{ 0x0000, 0x0000 }, /* R503 */
+	{ 0x0000, 0x0000 }, /* R504 */
+	{ 0x0000, 0x0000 }, /* R505 */
+	{ 0x0000, 0x0000 }, /* R506 */
+	{ 0x0000, 0x0000 }, /* R507 */
+	{ 0x0000, 0x0000 }, /* R508 */
+	{ 0x0000, 0x0000 }, /* R509 */
+	{ 0x0000, 0x0000 }, /* R510 */
+	{ 0x0000, 0x0000 }, /* R511 */
+	{ 0x001F, 0x001F }, /* R512   - AIF1 Clocking (1) */
+	{ 0x003F, 0x003F }, /* R513   - AIF1 Clocking (2) */
+	{ 0x0000, 0x0000 }, /* R514 */
+	{ 0x0000, 0x0000 }, /* R515 */
+	{ 0x001F, 0x001F }, /* R516   - AIF2 Clocking (1) */
+	{ 0x003F, 0x003F }, /* R517   - AIF2 Clocking (2) */
+	{ 0x0000, 0x0000 }, /* R518 */
+	{ 0x0000, 0x0000 }, /* R519 */
+	{ 0x001F, 0x001F }, /* R520   - Clocking (1) */
+	{ 0x0777, 0x0777 }, /* R521   - Clocking (2) */
+	{ 0x0000, 0x0000 }, /* R522 */
+	{ 0x0000, 0x0000 }, /* R523 */
+	{ 0x0000, 0x0000 }, /* R524 */
+	{ 0x0000, 0x0000 }, /* R525 */
+	{ 0x0000, 0x0000 }, /* R526 */
+	{ 0x0000, 0x0000 }, /* R527 */
+	{ 0x00FF, 0x00FF }, /* R528   - AIF1 Rate */
+	{ 0x00FF, 0x00FF }, /* R529   - AIF2 Rate */
+	{ 0x000F, 0x0000 }, /* R530   - Rate Status */
+	{ 0x0000, 0x0000 }, /* R531 */
+	{ 0x0000, 0x0000 }, /* R532 */
+	{ 0x0000, 0x0000 }, /* R533 */
+	{ 0x0000, 0x0000 }, /* R534 */
+	{ 0x0000, 0x0000 }, /* R535 */
+	{ 0x0000, 0x0000 }, /* R536 */
+	{ 0x0000, 0x0000 }, /* R537 */
+	{ 0x0000, 0x0000 }, /* R538 */
+	{ 0x0000, 0x0000 }, /* R539 */
+	{ 0x0000, 0x0000 }, /* R540 */
+	{ 0x0000, 0x0000 }, /* R541 */
+	{ 0x0000, 0x0000 }, /* R542 */
+	{ 0x0000, 0x0000 }, /* R543 */
+	{ 0x0007, 0x0007 }, /* R544   - FLL1 Control (1) */
+	{ 0x3F77, 0x3F77 }, /* R545   - FLL1 Control (2) */
+	{ 0xFFFF, 0xFFFF }, /* R546   - FLL1 Control (3) */
+	{ 0x7FEF, 0x7FEF }, /* R547   - FLL1 Control (4) */
+	{ 0x1FDB, 0x1FDB }, /* R548   - FLL1 Control (5) */
+	{ 0x0000, 0x0000 }, /* R549 */
+	{ 0x0000, 0x0000 }, /* R550 */
+	{ 0x0000, 0x0000 }, /* R551 */
+	{ 0x0000, 0x0000 }, /* R552 */
+	{ 0x0000, 0x0000 }, /* R553 */
+	{ 0x0000, 0x0000 }, /* R554 */
+	{ 0x0000, 0x0000 }, /* R555 */
+	{ 0x0000, 0x0000 }, /* R556 */
+	{ 0x0000, 0x0000 }, /* R557 */
+	{ 0x0000, 0x0000 }, /* R558 */
+	{ 0x0000, 0x0000 }, /* R559 */
+	{ 0x0000, 0x0000 }, /* R560 */
+	{ 0x0000, 0x0000 }, /* R561 */
+	{ 0x0000, 0x0000 }, /* R562 */
+	{ 0x0000, 0x0000 }, /* R563 */
+	{ 0x0000, 0x0000 }, /* R564 */
+	{ 0x0000, 0x0000 }, /* R565 */
+	{ 0x0000, 0x0000 }, /* R566 */
+	{ 0x0000, 0x0000 }, /* R567 */
+	{ 0x0000, 0x0000 }, /* R568 */
+	{ 0x0000, 0x0000 }, /* R569 */
+	{ 0x0000, 0x0000 }, /* R570 */
+	{ 0x0000, 0x0000 }, /* R571 */
+	{ 0x0000, 0x0000 }, /* R572 */
+	{ 0x0000, 0x0000 }, /* R573 */
+	{ 0x0000, 0x0000 }, /* R574 */
+	{ 0x0000, 0x0000 }, /* R575 */
+	{ 0x0007, 0x0007 }, /* R576   - FLL2 Control (1) */
+	{ 0x3F77, 0x3F77 }, /* R577   - FLL2 Control (2) */
+	{ 0xFFFF, 0xFFFF }, /* R578   - FLL2 Control (3) */
+	{ 0x7FEF, 0x7FEF }, /* R579   - FLL2 Control (4) */
+	{ 0x1FDB, 0x1FDB }, /* R580   - FLL2 Control (5) */
+	{ 0x0000, 0x0000 }, /* R581 */
+	{ 0x0000, 0x0000 }, /* R582 */
+	{ 0x0000, 0x0000 }, /* R583 */
+	{ 0x0000, 0x0000 }, /* R584 */
+	{ 0x0000, 0x0000 }, /* R585 */
+	{ 0x0000, 0x0000 }, /* R586 */
+	{ 0x0000, 0x0000 }, /* R587 */
+	{ 0x0000, 0x0000 }, /* R588 */
+	{ 0x0000, 0x0000 }, /* R589 */
+	{ 0x0000, 0x0000 }, /* R590 */
+	{ 0x0000, 0x0000 }, /* R591 */
+	{ 0x0000, 0x0000 }, /* R592 */
+	{ 0x0000, 0x0000 }, /* R593 */
+	{ 0x0000, 0x0000 }, /* R594 */
+	{ 0x0000, 0x0000 }, /* R595 */
+	{ 0x0000, 0x0000 }, /* R596 */
+	{ 0x0000, 0x0000 }, /* R597 */
+	{ 0x0000, 0x0000 }, /* R598 */
+	{ 0x0000, 0x0000 }, /* R599 */
+	{ 0x0000, 0x0000 }, /* R600 */
+	{ 0x0000, 0x0000 }, /* R601 */
+	{ 0x0000, 0x0000 }, /* R602 */
+	{ 0x0000, 0x0000 }, /* R603 */
+	{ 0x0000, 0x0000 }, /* R604 */
+	{ 0x0000, 0x0000 }, /* R605 */
+	{ 0x0000, 0x0000 }, /* R606 */
+	{ 0x0000, 0x0000 }, /* R607 */
+	{ 0x0000, 0x0000 }, /* R608 */
+	{ 0x0000, 0x0000 }, /* R609 */
+	{ 0x0000, 0x0000 }, /* R610 */
+	{ 0x0000, 0x0000 }, /* R611 */
+	{ 0x0000, 0x0000 }, /* R612 */
+	{ 0x0000, 0x0000 }, /* R613 */
+	{ 0x0000, 0x0000 }, /* R614 */
+	{ 0x0000, 0x0000 }, /* R615 */
+	{ 0x0000, 0x0000 }, /* R616 */
+	{ 0x0000, 0x0000 }, /* R617 */
+	{ 0x0000, 0x0000 }, /* R618 */
+	{ 0x0000, 0x0000 }, /* R619 */
+	{ 0x0000, 0x0000 }, /* R620 */
+	{ 0x0000, 0x0000 }, /* R621 */
+	{ 0x0000, 0x0000 }, /* R622 */
+	{ 0x0000, 0x0000 }, /* R623 */
+	{ 0x0000, 0x0000 }, /* R624 */
+	{ 0x0000, 0x0000 }, /* R625 */
+	{ 0x0000, 0x0000 }, /* R626 */
+	{ 0x0000, 0x0000 }, /* R627 */
+	{ 0x0000, 0x0000 }, /* R628 */
+	{ 0x0000, 0x0000 }, /* R629 */
+	{ 0x0000, 0x0000 }, /* R630 */
+	{ 0x0000, 0x0000 }, /* R631 */
+	{ 0x0000, 0x0000 }, /* R632 */
+	{ 0x0000, 0x0000 }, /* R633 */
+	{ 0x0000, 0x0000 }, /* R634 */
+	{ 0x0000, 0x0000 }, /* R635 */
+	{ 0x0000, 0x0000 }, /* R636 */
+	{ 0x0000, 0x0000 }, /* R637 */
+	{ 0x0000, 0x0000 }, /* R638 */
+	{ 0x0000, 0x0000 }, /* R639 */
+	{ 0x0000, 0x0000 }, /* R640 */
+	{ 0x0000, 0x0000 }, /* R641 */
+	{ 0x0000, 0x0000 }, /* R642 */
+	{ 0x0000, 0x0000 }, /* R643 */
+	{ 0x0000, 0x0000 }, /* R644 */
+	{ 0x0000, 0x0000 }, /* R645 */
+	{ 0x0000, 0x0000 }, /* R646 */
+	{ 0x0000, 0x0000 }, /* R647 */
+	{ 0x0000, 0x0000 }, /* R648 */
+	{ 0x0000, 0x0000 }, /* R649 */
+	{ 0x0000, 0x0000 }, /* R650 */
+	{ 0x0000, 0x0000 }, /* R651 */
+	{ 0x0000, 0x0000 }, /* R652 */
+	{ 0x0000, 0x0000 }, /* R653 */
+	{ 0x0000, 0x0000 }, /* R654 */
+	{ 0x0000, 0x0000 }, /* R655 */
+	{ 0x0000, 0x0000 }, /* R656 */
+	{ 0x0000, 0x0000 }, /* R657 */
+	{ 0x0000, 0x0000 }, /* R658 */
+	{ 0x0000, 0x0000 }, /* R659 */
+	{ 0x0000, 0x0000 }, /* R660 */
+	{ 0x0000, 0x0000 }, /* R661 */
+	{ 0x0000, 0x0000 }, /* R662 */
+	{ 0x0000, 0x0000 }, /* R663 */
+	{ 0x0000, 0x0000 }, /* R664 */
+	{ 0x0000, 0x0000 }, /* R665 */
+	{ 0x0000, 0x0000 }, /* R666 */
+	{ 0x0000, 0x0000 }, /* R667 */
+	{ 0x0000, 0x0000 }, /* R668 */
+	{ 0x0000, 0x0000 }, /* R669 */
+	{ 0x0000, 0x0000 }, /* R670 */
+	{ 0x0000, 0x0000 }, /* R671 */
+	{ 0x0000, 0x0000 }, /* R672 */
+	{ 0x0000, 0x0000 }, /* R673 */
+	{ 0x0000, 0x0000 }, /* R674 */
+	{ 0x0000, 0x0000 }, /* R675 */
+	{ 0x0000, 0x0000 }, /* R676 */
+	{ 0x0000, 0x0000 }, /* R677 */
+	{ 0x0000, 0x0000 }, /* R678 */
+	{ 0x0000, 0x0000 }, /* R679 */
+	{ 0x0000, 0x0000 }, /* R680 */
+	{ 0x0000, 0x0000 }, /* R681 */
+	{ 0x0000, 0x0000 }, /* R682 */
+	{ 0x0000, 0x0000 }, /* R683 */
+	{ 0x0000, 0x0000 }, /* R684 */
+	{ 0x0000, 0x0000 }, /* R685 */
+	{ 0x0000, 0x0000 }, /* R686 */
+	{ 0x0000, 0x0000 }, /* R687 */
+	{ 0x0000, 0x0000 }, /* R688 */
+	{ 0x0000, 0x0000 }, /* R689 */
+	{ 0x0000, 0x0000 }, /* R690 */
+	{ 0x0000, 0x0000 }, /* R691 */
+	{ 0x0000, 0x0000 }, /* R692 */
+	{ 0x0000, 0x0000 }, /* R693 */
+	{ 0x0000, 0x0000 }, /* R694 */
+	{ 0x0000, 0x0000 }, /* R695 */
+	{ 0x0000, 0x0000 }, /* R696 */
+	{ 0x0000, 0x0000 }, /* R697 */
+	{ 0x0000, 0x0000 }, /* R698 */
+	{ 0x0000, 0x0000 }, /* R699 */
+	{ 0x0000, 0x0000 }, /* R700 */
+	{ 0x0000, 0x0000 }, /* R701 */
+	{ 0x0000, 0x0000 }, /* R702 */
+	{ 0x0000, 0x0000 }, /* R703 */
+	{ 0x0000, 0x0000 }, /* R704 */
+	{ 0x0000, 0x0000 }, /* R705 */
+	{ 0x0000, 0x0000 }, /* R706 */
+	{ 0x0000, 0x0000 }, /* R707 */
+	{ 0x0000, 0x0000 }, /* R708 */
+	{ 0x0000, 0x0000 }, /* R709 */
+	{ 0x0000, 0x0000 }, /* R710 */
+	{ 0x0000, 0x0000 }, /* R711 */
+	{ 0x0000, 0x0000 }, /* R712 */
+	{ 0x0000, 0x0000 }, /* R713 */
+	{ 0x0000, 0x0000 }, /* R714 */
+	{ 0x0000, 0x0000 }, /* R715 */
+	{ 0x0000, 0x0000 }, /* R716 */
+	{ 0x0000, 0x0000 }, /* R717 */
+	{ 0x0000, 0x0000 }, /* R718 */
+	{ 0x0000, 0x0000 }, /* R719 */
+	{ 0x0000, 0x0000 }, /* R720 */
+	{ 0x0000, 0x0000 }, /* R721 */
+	{ 0x0000, 0x0000 }, /* R722 */
+	{ 0x0000, 0x0000 }, /* R723 */
+	{ 0x0000, 0x0000 }, /* R724 */
+	{ 0x0000, 0x0000 }, /* R725 */
+	{ 0x0000, 0x0000 }, /* R726 */
+	{ 0x0000, 0x0000 }, /* R727 */
+	{ 0x0000, 0x0000 }, /* R728 */
+	{ 0x0000, 0x0000 }, /* R729 */
+	{ 0x0000, 0x0000 }, /* R730 */
+	{ 0x0000, 0x0000 }, /* R731 */
+	{ 0x0000, 0x0000 }, /* R732 */
+	{ 0x0000, 0x0000 }, /* R733 */
+	{ 0x0000, 0x0000 }, /* R734 */
+	{ 0x0000, 0x0000 }, /* R735 */
+	{ 0x0000, 0x0000 }, /* R736 */
+	{ 0x0000, 0x0000 }, /* R737 */
+	{ 0x0000, 0x0000 }, /* R738 */
+	{ 0x0000, 0x0000 }, /* R739 */
+	{ 0x0000, 0x0000 }, /* R740 */
+	{ 0x0000, 0x0000 }, /* R741 */
+	{ 0x0000, 0x0000 }, /* R742 */
+	{ 0x0000, 0x0000 }, /* R743 */
+	{ 0x0000, 0x0000 }, /* R744 */
+	{ 0x0000, 0x0000 }, /* R745 */
+	{ 0x0000, 0x0000 }, /* R746 */
+	{ 0x0000, 0x0000 }, /* R747 */
+	{ 0x0000, 0x0000 }, /* R748 */
+	{ 0x0000, 0x0000 }, /* R749 */
+	{ 0x0000, 0x0000 }, /* R750 */
+	{ 0x0000, 0x0000 }, /* R751 */
+	{ 0x0000, 0x0000 }, /* R752 */
+	{ 0x0000, 0x0000 }, /* R753 */
+	{ 0x0000, 0x0000 }, /* R754 */
+	{ 0x0000, 0x0000 }, /* R755 */
+	{ 0x0000, 0x0000 }, /* R756 */
+	{ 0x0000, 0x0000 }, /* R757 */
+	{ 0x0000, 0x0000 }, /* R758 */
+	{ 0x0000, 0x0000 }, /* R759 */
+	{ 0x0000, 0x0000 }, /* R760 */
+	{ 0x0000, 0x0000 }, /* R761 */
+	{ 0x0000, 0x0000 }, /* R762 */
+	{ 0x0000, 0x0000 }, /* R763 */
+	{ 0x0000, 0x0000 }, /* R764 */
+	{ 0x0000, 0x0000 }, /* R765 */
+	{ 0x0000, 0x0000 }, /* R766 */
+	{ 0x0000, 0x0000 }, /* R767 */
+	{ 0xE1F8, 0xE1F8 }, /* R768   - AIF1 Control (1) */
+	{ 0xCD1F, 0xCD1F }, /* R769   - AIF1 Control (2) */
+	{ 0xF000, 0xF000 }, /* R770   - AIF1 Master/Slave */
+	{ 0x01F0, 0x01F0 }, /* R771   - AIF1 BCLK */
+	{ 0x0FFF, 0x0FFF }, /* R772   - AIF1ADC LRCLK */
+	{ 0x0FFF, 0x0FFF }, /* R773   - AIF1DAC LRCLK */
+	{ 0x0003, 0x0003 }, /* R774   - AIF1DAC Data */
+	{ 0x0003, 0x0003 }, /* R775   - AIF1ADC Data */
+	{ 0x0000, 0x0000 }, /* R776 */
+	{ 0x0000, 0x0000 }, /* R777 */
+	{ 0x0000, 0x0000 }, /* R778 */
+	{ 0x0000, 0x0000 }, /* R779 */
+	{ 0x0000, 0x0000 }, /* R780 */
+	{ 0x0000, 0x0000 }, /* R781 */
+	{ 0x0000, 0x0000 }, /* R782 */
+	{ 0x0000, 0x0000 }, /* R783 */
+	{ 0xF1F8, 0xF1F8 }, /* R784   - AIF2 Control (1) */
+	{ 0xFD1F, 0xFD1F }, /* R785   - AIF2 Control (2) */
+	{ 0xF000, 0xF000 }, /* R786   - AIF2 Master/Slave */
+	{ 0x01F0, 0x01F0 }, /* R787   - AIF2 BCLK */
+	{ 0x0FFF, 0x0FFF }, /* R788   - AIF2ADC LRCLK */
+	{ 0x0FFF, 0x0FFF }, /* R789   - AIF2DAC LRCLK */
+	{ 0x0003, 0x0003 }, /* R790   - AIF2DAC Data */
+	{ 0x0003, 0x0003 }, /* R791   - AIF2ADC Data */
+	{ 0x0000, 0x0000 }, /* R792 */
+	{ 0x0000, 0x0000 }, /* R793 */
+	{ 0x0000, 0x0000 }, /* R794 */
+	{ 0x0000, 0x0000 }, /* R795 */
+	{ 0x0000, 0x0000 }, /* R796 */
+	{ 0x0000, 0x0000 }, /* R797 */
+	{ 0x0000, 0x0000 }, /* R798 */
+	{ 0x0000, 0x0000 }, /* R799 */
+	{ 0x0000, 0x0000 }, /* R800 */
+	{ 0x0000, 0x0000 }, /* R801 */
+	{ 0x0000, 0x0000 }, /* R802 */
+	{ 0x0000, 0x0000 }, /* R803 */
+	{ 0x0000, 0x0000 }, /* R804 */
+	{ 0x0000, 0x0000 }, /* R805 */
+	{ 0x0000, 0x0000 }, /* R806 */
+	{ 0x0000, 0x0000 }, /* R807 */
+	{ 0x0000, 0x0000 }, /* R808 */
+	{ 0x0000, 0x0000 }, /* R809 */
+	{ 0x0000, 0x0000 }, /* R810 */
+	{ 0x0000, 0x0000 }, /* R811 */
+	{ 0x0000, 0x0000 }, /* R812 */
+	{ 0x0000, 0x0000 }, /* R813 */
+	{ 0x0000, 0x0000 }, /* R814 */
+	{ 0x0000, 0x0000 }, /* R815 */
+	{ 0x0000, 0x0000 }, /* R816 */
+	{ 0x0000, 0x0000 }, /* R817 */
+	{ 0x0000, 0x0000 }, /* R818 */
+	{ 0x0000, 0x0000 }, /* R819 */
+	{ 0x0000, 0x0000 }, /* R820 */
+	{ 0x0000, 0x0000 }, /* R821 */
+	{ 0x0000, 0x0000 }, /* R822 */
+	{ 0x0000, 0x0000 }, /* R823 */
+	{ 0x0000, 0x0000 }, /* R824 */
+	{ 0x0000, 0x0000 }, /* R825 */
+	{ 0x0000, 0x0000 }, /* R826 */
+	{ 0x0000, 0x0000 }, /* R827 */
+	{ 0x0000, 0x0000 }, /* R828 */
+	{ 0x0000, 0x0000 }, /* R829 */
+	{ 0x0000, 0x0000 }, /* R830 */
+	{ 0x0000, 0x0000 }, /* R831 */
+	{ 0x0000, 0x0000 }, /* R832 */
+	{ 0x0000, 0x0000 }, /* R833 */
+	{ 0x0000, 0x0000 }, /* R834 */
+	{ 0x0000, 0x0000 }, /* R835 */
+	{ 0x0000, 0x0000 }, /* R836 */
+	{ 0x0000, 0x0000 }, /* R837 */
+	{ 0x0000, 0x0000 }, /* R838 */
+	{ 0x0000, 0x0000 }, /* R839 */
+	{ 0x0000, 0x0000 }, /* R840 */
+	{ 0x0000, 0x0000 }, /* R841 */
+	{ 0x0000, 0x0000 }, /* R842 */
+	{ 0x0000, 0x0000 }, /* R843 */
+	{ 0x0000, 0x0000 }, /* R844 */
+	{ 0x0000, 0x0000 }, /* R845 */
+	{ 0x0000, 0x0000 }, /* R846 */
+	{ 0x0000, 0x0000 }, /* R847 */
+	{ 0x0000, 0x0000 }, /* R848 */
+	{ 0x0000, 0x0000 }, /* R849 */
+	{ 0x0000, 0x0000 }, /* R850 */
+	{ 0x0000, 0x0000 }, /* R851 */
+	{ 0x0000, 0x0000 }, /* R852 */
+	{ 0x0000, 0x0000 }, /* R853 */
+	{ 0x0000, 0x0000 }, /* R854 */
+	{ 0x0000, 0x0000 }, /* R855 */
+	{ 0x0000, 0x0000 }, /* R856 */
+	{ 0x0000, 0x0000 }, /* R857 */
+	{ 0x0000, 0x0000 }, /* R858 */
+	{ 0x0000, 0x0000 }, /* R859 */
+	{ 0x0000, 0x0000 }, /* R860 */
+	{ 0x0000, 0x0000 }, /* R861 */
+	{ 0x0000, 0x0000 }, /* R862 */
+	{ 0x0000, 0x0000 }, /* R863 */
+	{ 0x0000, 0x0000 }, /* R864 */
+	{ 0x0000, 0x0000 }, /* R865 */
+	{ 0x0000, 0x0000 }, /* R866 */
+	{ 0x0000, 0x0000 }, /* R867 */
+	{ 0x0000, 0x0000 }, /* R868 */
+	{ 0x0000, 0x0000 }, /* R869 */
+	{ 0x0000, 0x0000 }, /* R870 */
+	{ 0x0000, 0x0000 }, /* R871 */
+	{ 0x0000, 0x0000 }, /* R872 */
+	{ 0x0000, 0x0000 }, /* R873 */
+	{ 0x0000, 0x0000 }, /* R874 */
+	{ 0x0000, 0x0000 }, /* R875 */
+	{ 0x0000, 0x0000 }, /* R876 */
+	{ 0x0000, 0x0000 }, /* R877 */
+	{ 0x0000, 0x0000 }, /* R878 */
+	{ 0x0000, 0x0000 }, /* R879 */
+	{ 0x0000, 0x0000 }, /* R880 */
+	{ 0x0000, 0x0000 }, /* R881 */
+	{ 0x0000, 0x0000 }, /* R882 */
+	{ 0x0000, 0x0000 }, /* R883 */
+	{ 0x0000, 0x0000 }, /* R884 */
+	{ 0x0000, 0x0000 }, /* R885 */
+	{ 0x0000, 0x0000 }, /* R886 */
+	{ 0x0000, 0x0000 }, /* R887 */
+	{ 0x0000, 0x0000 }, /* R888 */
+	{ 0x0000, 0x0000 }, /* R889 */
+	{ 0x0000, 0x0000 }, /* R890 */
+	{ 0x0000, 0x0000 }, /* R891 */
+	{ 0x0000, 0x0000 }, /* R892 */
+	{ 0x0000, 0x0000 }, /* R893 */
+	{ 0x0000, 0x0000 }, /* R894 */
+	{ 0x0000, 0x0000 }, /* R895 */
+	{ 0x0000, 0x0000 }, /* R896 */
+	{ 0x0000, 0x0000 }, /* R897 */
+	{ 0x0000, 0x0000 }, /* R898 */
+	{ 0x0000, 0x0000 }, /* R899 */
+	{ 0x0000, 0x0000 }, /* R900 */
+	{ 0x0000, 0x0000 }, /* R901 */
+	{ 0x0000, 0x0000 }, /* R902 */
+	{ 0x0000, 0x0000 }, /* R903 */
+	{ 0x0000, 0x0000 }, /* R904 */
+	{ 0x0000, 0x0000 }, /* R905 */
+	{ 0x0000, 0x0000 }, /* R906 */
+	{ 0x0000, 0x0000 }, /* R907 */
+	{ 0x0000, 0x0000 }, /* R908 */
+	{ 0x0000, 0x0000 }, /* R909 */
+	{ 0x0000, 0x0000 }, /* R910 */
+	{ 0x0000, 0x0000 }, /* R911 */
+	{ 0x0000, 0x0000 }, /* R912 */
+	{ 0x0000, 0x0000 }, /* R913 */
+	{ 0x0000, 0x0000 }, /* R914 */
+	{ 0x0000, 0x0000 }, /* R915 */
+	{ 0x0000, 0x0000 }, /* R916 */
+	{ 0x0000, 0x0000 }, /* R917 */
+	{ 0x0000, 0x0000 }, /* R918 */
+	{ 0x0000, 0x0000 }, /* R919 */
+	{ 0x0000, 0x0000 }, /* R920 */
+	{ 0x0000, 0x0000 }, /* R921 */
+	{ 0x0000, 0x0000 }, /* R922 */
+	{ 0x0000, 0x0000 }, /* R923 */
+	{ 0x0000, 0x0000 }, /* R924 */
+	{ 0x0000, 0x0000 }, /* R925 */
+	{ 0x0000, 0x0000 }, /* R926 */
+	{ 0x0000, 0x0000 }, /* R927 */
+	{ 0x0000, 0x0000 }, /* R928 */
+	{ 0x0000, 0x0000 }, /* R929 */
+	{ 0x0000, 0x0000 }, /* R930 */
+	{ 0x0000, 0x0000 }, /* R931 */
+	{ 0x0000, 0x0000 }, /* R932 */
+	{ 0x0000, 0x0000 }, /* R933 */
+	{ 0x0000, 0x0000 }, /* R934 */
+	{ 0x0000, 0x0000 }, /* R935 */
+	{ 0x0000, 0x0000 }, /* R936 */
+	{ 0x0000, 0x0000 }, /* R937 */
+	{ 0x0000, 0x0000 }, /* R938 */
+	{ 0x0000, 0x0000 }, /* R939 */
+	{ 0x0000, 0x0000 }, /* R940 */
+	{ 0x0000, 0x0000 }, /* R941 */
+	{ 0x0000, 0x0000 }, /* R942 */
+	{ 0x0000, 0x0000 }, /* R943 */
+	{ 0x0000, 0x0000 }, /* R944 */
+	{ 0x0000, 0x0000 }, /* R945 */
+	{ 0x0000, 0x0000 }, /* R946 */
+	{ 0x0000, 0x0000 }, /* R947 */
+	{ 0x0000, 0x0000 }, /* R948 */
+	{ 0x0000, 0x0000 }, /* R949 */
+	{ 0x0000, 0x0000 }, /* R950 */
+	{ 0x0000, 0x0000 }, /* R951 */
+	{ 0x0000, 0x0000 }, /* R952 */
+	{ 0x0000, 0x0000 }, /* R953 */
+	{ 0x0000, 0x0000 }, /* R954 */
+	{ 0x0000, 0x0000 }, /* R955 */
+	{ 0x0000, 0x0000 }, /* R956 */
+	{ 0x0000, 0x0000 }, /* R957 */
+	{ 0x0000, 0x0000 }, /* R958 */
+	{ 0x0000, 0x0000 }, /* R959 */
+	{ 0x0000, 0x0000 }, /* R960 */
+	{ 0x0000, 0x0000 }, /* R961 */
+	{ 0x0000, 0x0000 }, /* R962 */
+	{ 0x0000, 0x0000 }, /* R963 */
+	{ 0x0000, 0x0000 }, /* R964 */
+	{ 0x0000, 0x0000 }, /* R965 */
+	{ 0x0000, 0x0000 }, /* R966 */
+	{ 0x0000, 0x0000 }, /* R967 */
+	{ 0x0000, 0x0000 }, /* R968 */
+	{ 0x0000, 0x0000 }, /* R969 */
+	{ 0x0000, 0x0000 }, /* R970 */
+	{ 0x0000, 0x0000 }, /* R971 */
+	{ 0x0000, 0x0000 }, /* R972 */
+	{ 0x0000, 0x0000 }, /* R973 */
+	{ 0x0000, 0x0000 }, /* R974 */
+	{ 0x0000, 0x0000 }, /* R975 */
+	{ 0x0000, 0x0000 }, /* R976 */
+	{ 0x0000, 0x0000 }, /* R977 */
+	{ 0x0000, 0x0000 }, /* R978 */
+	{ 0x0000, 0x0000 }, /* R979 */
+	{ 0x0000, 0x0000 }, /* R980 */
+	{ 0x0000, 0x0000 }, /* R981 */
+	{ 0x0000, 0x0000 }, /* R982 */
+	{ 0x0000, 0x0000 }, /* R983 */
+	{ 0x0000, 0x0000 }, /* R984 */
+	{ 0x0000, 0x0000 }, /* R985 */
+	{ 0x0000, 0x0000 }, /* R986 */
+	{ 0x0000, 0x0000 }, /* R987 */
+	{ 0x0000, 0x0000 }, /* R988 */
+	{ 0x0000, 0x0000 }, /* R989 */
+	{ 0x0000, 0x0000 }, /* R990 */
+	{ 0x0000, 0x0000 }, /* R991 */
+	{ 0x0000, 0x0000 }, /* R992 */
+	{ 0x0000, 0x0000 }, /* R993 */
+	{ 0x0000, 0x0000 }, /* R994 */
+	{ 0x0000, 0x0000 }, /* R995 */
+	{ 0x0000, 0x0000 }, /* R996 */
+	{ 0x0000, 0x0000 }, /* R997 */
+	{ 0x0000, 0x0000 }, /* R998 */
+	{ 0x0000, 0x0000 }, /* R999 */
+	{ 0x0000, 0x0000 }, /* R1000 */
+	{ 0x0000, 0x0000 }, /* R1001 */
+	{ 0x0000, 0x0000 }, /* R1002 */
+	{ 0x0000, 0x0000 }, /* R1003 */
+	{ 0x0000, 0x0000 }, /* R1004 */
+	{ 0x0000, 0x0000 }, /* R1005 */
+	{ 0x0000, 0x0000 }, /* R1006 */
+	{ 0x0000, 0x0000 }, /* R1007 */
+	{ 0x0000, 0x0000 }, /* R1008 */
+	{ 0x0000, 0x0000 }, /* R1009 */
+	{ 0x0000, 0x0000 }, /* R1010 */
+	{ 0x0000, 0x0000 }, /* R1011 */
+	{ 0x0000, 0x0000 }, /* R1012 */
+	{ 0x0000, 0x0000 }, /* R1013 */
+	{ 0x0000, 0x0000 }, /* R1014 */
+	{ 0x0000, 0x0000 }, /* R1015 */
+	{ 0x0000, 0x0000 }, /* R1016 */
+	{ 0x0000, 0x0000 }, /* R1017 */
+	{ 0x0000, 0x0000 }, /* R1018 */
+	{ 0x0000, 0x0000 }, /* R1019 */
+	{ 0x0000, 0x0000 }, /* R1020 */
+	{ 0x0000, 0x0000 }, /* R1021 */
+	{ 0x0000, 0x0000 }, /* R1022 */
+	{ 0x0000, 0x0000 }, /* R1023 */
+	{ 0x00FF, 0x01FF }, /* R1024  - AIF1 ADC1 Left Volume */
+	{ 0x00FF, 0x01FF }, /* R1025  - AIF1 ADC1 Right Volume */
+	{ 0x00FF, 0x01FF }, /* R1026  - AIF1 DAC1 Left Volume */
+	{ 0x00FF, 0x01FF }, /* R1027  - AIF1 DAC1 Right Volume */
+	{ 0x00FF, 0x01FF }, /* R1028  - AIF1 ADC2 Left Volume */
+	{ 0x00FF, 0x01FF }, /* R1029  - AIF1 ADC2 Right Volume */
+	{ 0x00FF, 0x01FF }, /* R1030  - AIF1 DAC2 Left Volume */
+	{ 0x00FF, 0x01FF }, /* R1031  - AIF1 DAC2 Right Volume */
+	{ 0x0000, 0x0000 }, /* R1032 */
+	{ 0x0000, 0x0000 }, /* R1033 */
+	{ 0x0000, 0x0000 }, /* R1034 */
+	{ 0x0000, 0x0000 }, /* R1035 */
+	{ 0x0000, 0x0000 }, /* R1036 */
+	{ 0x0000, 0x0000 }, /* R1037 */
+	{ 0x0000, 0x0000 }, /* R1038 */
+	{ 0x0000, 0x0000 }, /* R1039 */
+	{ 0xF800, 0xF800 }, /* R1040  - AIF1 ADC1 Filters */
+	{ 0x7800, 0x7800 }, /* R1041  - AIF1 ADC2 Filters */
+	{ 0x0000, 0x0000 }, /* R1042 */
+	{ 0x0000, 0x0000 }, /* R1043 */
+	{ 0x0000, 0x0000 }, /* R1044 */
+	{ 0x0000, 0x0000 }, /* R1045 */
+	{ 0x0000, 0x0000 }, /* R1046 */
+	{ 0x0000, 0x0000 }, /* R1047 */
+	{ 0x0000, 0x0000 }, /* R1048 */
+	{ 0x0000, 0x0000 }, /* R1049 */
+	{ 0x0000, 0x0000 }, /* R1050 */
+	{ 0x0000, 0x0000 }, /* R1051 */
+	{ 0x0000, 0x0000 }, /* R1052 */
+	{ 0x0000, 0x0000 }, /* R1053 */
+	{ 0x0000, 0x0000 }, /* R1054 */
+	{ 0x0000, 0x0000 }, /* R1055 */
+	{ 0x02B6, 0x02B6 }, /* R1056  - AIF1 DAC1 Filters (1) */
+	{ 0x3F00, 0x3F00 }, /* R1057  - AIF1 DAC1 Filters (2) */
+	{ 0x02B6, 0x02B6 }, /* R1058  - AIF1 DAC2 Filters (1) */
+	{ 0x3F00, 0x3F00 }, /* R1059  - AIF1 DAC2 Filters (2) */
+	{ 0x0000, 0x0000 }, /* R1060 */
+	{ 0x0000, 0x0000 }, /* R1061 */
+	{ 0x0000, 0x0000 }, /* R1062 */
+	{ 0x0000, 0x0000 }, /* R1063 */
+	{ 0x0000, 0x0000 }, /* R1064 */
+	{ 0x0000, 0x0000 }, /* R1065 */
+	{ 0x0000, 0x0000 }, /* R1066 */
+	{ 0x0000, 0x0000 }, /* R1067 */
+	{ 0x0000, 0x0000 }, /* R1068 */
+	{ 0x0000, 0x0000 }, /* R1069 */
+	{ 0x0000, 0x0000 }, /* R1070 */
+	{ 0x0000, 0x0000 }, /* R1071 */
+	{ 0x0000, 0x0000 }, /* R1072 */
+	{ 0x0000, 0x0000 }, /* R1073 */
+	{ 0x0000, 0x0000 }, /* R1074 */
+	{ 0x0000, 0x0000 }, /* R1075 */
+	{ 0x0000, 0x0000 }, /* R1076 */
+	{ 0x0000, 0x0000 }, /* R1077 */
+	{ 0x0000, 0x0000 }, /* R1078 */
+	{ 0x0000, 0x0000 }, /* R1079 */
+	{ 0x0000, 0x0000 }, /* R1080 */
+	{ 0x0000, 0x0000 }, /* R1081 */
+	{ 0x0000, 0x0000 }, /* R1082 */
+	{ 0x0000, 0x0000 }, /* R1083 */
+	{ 0x0000, 0x0000 }, /* R1084 */
+	{ 0x0000, 0x0000 }, /* R1085 */
+	{ 0x0000, 0x0000 }, /* R1086 */
+	{ 0x0000, 0x0000 }, /* R1087 */
+	{ 0xFFFF, 0xFFFF }, /* R1088  - AIF1 DRC1 (1) */
+	{ 0x1FFF, 0x1FFF }, /* R1089  - AIF1 DRC1 (2) */
+	{ 0xFFFF, 0xFFFF }, /* R1090  - AIF1 DRC1 (3) */
+	{ 0x07FF, 0x07FF }, /* R1091  - AIF1 DRC1 (4) */
+	{ 0x03FF, 0x03FF }, /* R1092  - AIF1 DRC1 (5) */
+	{ 0x0000, 0x0000 }, /* R1093 */
+	{ 0x0000, 0x0000 }, /* R1094 */
+	{ 0x0000, 0x0000 }, /* R1095 */
+	{ 0x0000, 0x0000 }, /* R1096 */
+	{ 0x0000, 0x0000 }, /* R1097 */
+	{ 0x0000, 0x0000 }, /* R1098 */
+	{ 0x0000, 0x0000 }, /* R1099 */
+	{ 0x0000, 0x0000 }, /* R1100 */
+	{ 0x0000, 0x0000 }, /* R1101 */
+	{ 0x0000, 0x0000 }, /* R1102 */
+	{ 0x0000, 0x0000 }, /* R1103 */
+	{ 0xFFFF, 0xFFFF }, /* R1104  - AIF1 DRC2 (1) */
+	{ 0x1FFF, 0x1FFF }, /* R1105  - AIF1 DRC2 (2) */
+	{ 0xFFFF, 0xFFFF }, /* R1106  - AIF1 DRC2 (3) */
+	{ 0x07FF, 0x07FF }, /* R1107  - AIF1 DRC2 (4) */
+	{ 0x03FF, 0x03FF }, /* R1108  - AIF1 DRC2 (5) */
+	{ 0x0000, 0x0000 }, /* R1109 */
+	{ 0x0000, 0x0000 }, /* R1110 */
+	{ 0x0000, 0x0000 }, /* R1111 */
+	{ 0x0000, 0x0000 }, /* R1112 */
+	{ 0x0000, 0x0000 }, /* R1113 */
+	{ 0x0000, 0x0000 }, /* R1114 */
+	{ 0x0000, 0x0000 }, /* R1115 */
+	{ 0x0000, 0x0000 }, /* R1116 */
+	{ 0x0000, 0x0000 }, /* R1117 */
+	{ 0x0000, 0x0000 }, /* R1118 */
+	{ 0x0000, 0x0000 }, /* R1119 */
+	{ 0x0000, 0x0000 }, /* R1120 */
+	{ 0x0000, 0x0000 }, /* R1121 */
+	{ 0x0000, 0x0000 }, /* R1122 */
+	{ 0x0000, 0x0000 }, /* R1123 */
+	{ 0x0000, 0x0000 }, /* R1124 */
+	{ 0x0000, 0x0000 }, /* R1125 */
+	{ 0x0000, 0x0000 }, /* R1126 */
+	{ 0x0000, 0x0000 }, /* R1127 */
+	{ 0x0000, 0x0000 }, /* R1128 */
+	{ 0x0000, 0x0000 }, /* R1129 */
+	{ 0x0000, 0x0000 }, /* R1130 */
+	{ 0x0000, 0x0000 }, /* R1131 */
+	{ 0x0000, 0x0000 }, /* R1132 */
+	{ 0x0000, 0x0000 }, /* R1133 */
+	{ 0x0000, 0x0000 }, /* R1134 */
+	{ 0x0000, 0x0000 }, /* R1135 */
+	{ 0x0000, 0x0000 }, /* R1136 */
+	{ 0x0000, 0x0000 }, /* R1137 */
+	{ 0x0000, 0x0000 }, /* R1138 */
+	{ 0x0000, 0x0000 }, /* R1139 */
+	{ 0x0000, 0x0000 }, /* R1140 */
+	{ 0x0000, 0x0000 }, /* R1141 */
+	{ 0x0000, 0x0000 }, /* R1142 */
+	{ 0x0000, 0x0000 }, /* R1143 */
+	{ 0x0000, 0x0000 }, /* R1144 */
+	{ 0x0000, 0x0000 }, /* R1145 */
+	{ 0x0000, 0x0000 }, /* R1146 */
+	{ 0x0000, 0x0000 }, /* R1147 */
+	{ 0x0000, 0x0000 }, /* R1148 */
+	{ 0x0000, 0x0000 }, /* R1149 */
+	{ 0x0000, 0x0000 }, /* R1150 */
+	{ 0x0000, 0x0000 }, /* R1151 */
+	{ 0xFFFF, 0xFFFF }, /* R1152  - AIF1 DAC1 EQ Gains (1) */
+	{ 0xFFC0, 0xFFC0 }, /* R1153  - AIF1 DAC1 EQ Gains (2) */
+	{ 0xFFFF, 0xFFFF }, /* R1154  - AIF1 DAC1 EQ Band 1 A */
+	{ 0xFFFF, 0xFFFF }, /* R1155  - AIF1 DAC1 EQ Band 1 B */
+	{ 0xFFFF, 0xFFFF }, /* R1156  - AIF1 DAC1 EQ Band 1 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1157  - AIF1 DAC1 EQ Band 2 A */
+	{ 0xFFFF, 0xFFFF }, /* R1158  - AIF1 DAC1 EQ Band 2 B */
+	{ 0xFFFF, 0xFFFF }, /* R1159  - AIF1 DAC1 EQ Band 2 C */
+	{ 0xFFFF, 0xFFFF }, /* R1160  - AIF1 DAC1 EQ Band 2 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1161  - AIF1 DAC1 EQ Band 3 A */
+	{ 0xFFFF, 0xFFFF }, /* R1162  - AIF1 DAC1 EQ Band 3 B */
+	{ 0xFFFF, 0xFFFF }, /* R1163  - AIF1 DAC1 EQ Band 3 C */
+	{ 0xFFFF, 0xFFFF }, /* R1164  - AIF1 DAC1 EQ Band 3 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1165  - AIF1 DAC1 EQ Band 4 A */
+	{ 0xFFFF, 0xFFFF }, /* R1166  - AIF1 DAC1 EQ Band 4 B */
+	{ 0xFFFF, 0xFFFF }, /* R1167  - AIF1 DAC1 EQ Band 4 C */
+	{ 0xFFFF, 0xFFFF }, /* R1168  - AIF1 DAC1 EQ Band 4 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1169  - AIF1 DAC1 EQ Band 5 A */
+	{ 0xFFFF, 0xFFFF }, /* R1170  - AIF1 DAC1 EQ Band 5 B */
+	{ 0xFFFF, 0xFFFF }, /* R1171  - AIF1 DAC1 EQ Band 5 PG */
+	{ 0x0000, 0x0000 }, /* R1172 */
+	{ 0x0000, 0x0000 }, /* R1173 */
+	{ 0x0000, 0x0000 }, /* R1174 */
+	{ 0x0000, 0x0000 }, /* R1175 */
+	{ 0x0000, 0x0000 }, /* R1176 */
+	{ 0x0000, 0x0000 }, /* R1177 */
+	{ 0x0000, 0x0000 }, /* R1178 */
+	{ 0x0000, 0x0000 }, /* R1179 */
+	{ 0x0000, 0x0000 }, /* R1180 */
+	{ 0x0000, 0x0000 }, /* R1181 */
+	{ 0x0000, 0x0000 }, /* R1182 */
+	{ 0x0000, 0x0000 }, /* R1183 */
+	{ 0xFFFF, 0xFFFF }, /* R1184  - AIF1 DAC2 EQ Gains (1) */
+	{ 0xFFC0, 0xFFC0 }, /* R1185  - AIF1 DAC2 EQ Gains (2) */
+	{ 0xFFFF, 0xFFFF }, /* R1186  - AIF1 DAC2 EQ Band 1 A */
+	{ 0xFFFF, 0xFFFF }, /* R1187  - AIF1 DAC2 EQ Band 1 B */
+	{ 0xFFFF, 0xFFFF }, /* R1188  - AIF1 DAC2 EQ Band 1 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1189  - AIF1 DAC2 EQ Band 2 A */
+	{ 0xFFFF, 0xFFFF }, /* R1190  - AIF1 DAC2 EQ Band 2 B */
+	{ 0xFFFF, 0xFFFF }, /* R1191  - AIF1 DAC2 EQ Band 2 C */
+	{ 0xFFFF, 0xFFFF }, /* R1192  - AIF1 DAC2 EQ Band 2 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1193  - AIF1 DAC2 EQ Band 3 A */
+	{ 0xFFFF, 0xFFFF }, /* R1194  - AIF1 DAC2 EQ Band 3 B */
+	{ 0xFFFF, 0xFFFF }, /* R1195  - AIF1 DAC2 EQ Band 3 C */
+	{ 0xFFFF, 0xFFFF }, /* R1196  - AIF1 DAC2 EQ Band 3 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1197  - AIF1 DAC2 EQ Band 4 A */
+	{ 0xFFFF, 0xFFFF }, /* R1198  - AIF1 DAC2 EQ Band 4 B */
+	{ 0xFFFF, 0xFFFF }, /* R1199  - AIF1 DAC2 EQ Band 4 C */
+	{ 0xFFFF, 0xFFFF }, /* R1200  - AIF1 DAC2 EQ Band 4 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1201  - AIF1 DAC2 EQ Band 5 A */
+	{ 0xFFFF, 0xFFFF }, /* R1202  - AIF1 DAC2 EQ Band 5 B */
+	{ 0xFFFF, 0xFFFF }, /* R1203  - AIF1 DAC2 EQ Band 5 PG */
+	{ 0x0000, 0x0000 }, /* R1204 */
+	{ 0x0000, 0x0000 }, /* R1205 */
+	{ 0x0000, 0x0000 }, /* R1206 */
+	{ 0x0000, 0x0000 }, /* R1207 */
+	{ 0x0000, 0x0000 }, /* R1208 */
+	{ 0x0000, 0x0000 }, /* R1209 */
+	{ 0x0000, 0x0000 }, /* R1210 */
+	{ 0x0000, 0x0000 }, /* R1211 */
+	{ 0x0000, 0x0000 }, /* R1212 */
+	{ 0x0000, 0x0000 }, /* R1213 */
+	{ 0x0000, 0x0000 }, /* R1214 */
+	{ 0x0000, 0x0000 }, /* R1215 */
+	{ 0x0000, 0x0000 }, /* R1216 */
+	{ 0x0000, 0x0000 }, /* R1217 */
+	{ 0x0000, 0x0000 }, /* R1218 */
+	{ 0x0000, 0x0000 }, /* R1219 */
+	{ 0x0000, 0x0000 }, /* R1220 */
+	{ 0x0000, 0x0000 }, /* R1221 */
+	{ 0x0000, 0x0000 }, /* R1222 */
+	{ 0x0000, 0x0000 }, /* R1223 */
+	{ 0x0000, 0x0000 }, /* R1224 */
+	{ 0x0000, 0x0000 }, /* R1225 */
+	{ 0x0000, 0x0000 }, /* R1226 */
+	{ 0x0000, 0x0000 }, /* R1227 */
+	{ 0x0000, 0x0000 }, /* R1228 */
+	{ 0x0000, 0x0000 }, /* R1229 */
+	{ 0x0000, 0x0000 }, /* R1230 */
+	{ 0x0000, 0x0000 }, /* R1231 */
+	{ 0x0000, 0x0000 }, /* R1232 */
+	{ 0x0000, 0x0000 }, /* R1233 */
+	{ 0x0000, 0x0000 }, /* R1234 */
+	{ 0x0000, 0x0000 }, /* R1235 */
+	{ 0x0000, 0x0000 }, /* R1236 */
+	{ 0x0000, 0x0000 }, /* R1237 */
+	{ 0x0000, 0x0000 }, /* R1238 */
+	{ 0x0000, 0x0000 }, /* R1239 */
+	{ 0x0000, 0x0000 }, /* R1240 */
+	{ 0x0000, 0x0000 }, /* R1241 */
+	{ 0x0000, 0x0000 }, /* R1242 */
+	{ 0x0000, 0x0000 }, /* R1243 */
+	{ 0x0000, 0x0000 }, /* R1244 */
+	{ 0x0000, 0x0000 }, /* R1245 */
+	{ 0x0000, 0x0000 }, /* R1246 */
+	{ 0x0000, 0x0000 }, /* R1247 */
+	{ 0x0000, 0x0000 }, /* R1248 */
+	{ 0x0000, 0x0000 }, /* R1249 */
+	{ 0x0000, 0x0000 }, /* R1250 */
+	{ 0x0000, 0x0000 }, /* R1251 */
+	{ 0x0000, 0x0000 }, /* R1252 */
+	{ 0x0000, 0x0000 }, /* R1253 */
+	{ 0x0000, 0x0000 }, /* R1254 */
+	{ 0x0000, 0x0000 }, /* R1255 */
+	{ 0x0000, 0x0000 }, /* R1256 */
+	{ 0x0000, 0x0000 }, /* R1257 */
+	{ 0x0000, 0x0000 }, /* R1258 */
+	{ 0x0000, 0x0000 }, /* R1259 */
+	{ 0x0000, 0x0000 }, /* R1260 */
+	{ 0x0000, 0x0000 }, /* R1261 */
+	{ 0x0000, 0x0000 }, /* R1262 */
+	{ 0x0000, 0x0000 }, /* R1263 */
+	{ 0x0000, 0x0000 }, /* R1264 */
+	{ 0x0000, 0x0000 }, /* R1265 */
+	{ 0x0000, 0x0000 }, /* R1266 */
+	{ 0x0000, 0x0000 }, /* R1267 */
+	{ 0x0000, 0x0000 }, /* R1268 */
+	{ 0x0000, 0x0000 }, /* R1269 */
+	{ 0x0000, 0x0000 }, /* R1270 */
+	{ 0x0000, 0x0000 }, /* R1271 */
+	{ 0x0000, 0x0000 }, /* R1272 */
+	{ 0x0000, 0x0000 }, /* R1273 */
+	{ 0x0000, 0x0000 }, /* R1274 */
+	{ 0x0000, 0x0000 }, /* R1275 */
+	{ 0x0000, 0x0000 }, /* R1276 */
+	{ 0x0000, 0x0000 }, /* R1277 */
+	{ 0x0000, 0x0000 }, /* R1278 */
+	{ 0x0000, 0x0000 }, /* R1279 */
+	{ 0x00FF, 0x01FF }, /* R1280  - AIF2 ADC Left Volume */
+	{ 0x00FF, 0x01FF }, /* R1281  - AIF2 ADC Right Volume */
+	{ 0x00FF, 0x01FF }, /* R1282  - AIF2 DAC Left Volume */
+	{ 0x00FF, 0x01FF }, /* R1283  - AIF2 DAC Right Volume */
+	{ 0x0000, 0x0000 }, /* R1284 */
+	{ 0x0000, 0x0000 }, /* R1285 */
+	{ 0x0000, 0x0000 }, /* R1286 */
+	{ 0x0000, 0x0000 }, /* R1287 */
+	{ 0x0000, 0x0000 }, /* R1288 */
+	{ 0x0000, 0x0000 }, /* R1289 */
+	{ 0x0000, 0x0000 }, /* R1290 */
+	{ 0x0000, 0x0000 }, /* R1291 */
+	{ 0x0000, 0x0000 }, /* R1292 */
+	{ 0x0000, 0x0000 }, /* R1293 */
+	{ 0x0000, 0x0000 }, /* R1294 */
+	{ 0x0000, 0x0000 }, /* R1295 */
+	{ 0xF800, 0xF800 }, /* R1296  - AIF2 ADC Filters */
+	{ 0x0000, 0x0000 }, /* R1297 */
+	{ 0x0000, 0x0000 }, /* R1298 */
+	{ 0x0000, 0x0000 }, /* R1299 */
+	{ 0x0000, 0x0000 }, /* R1300 */
+	{ 0x0000, 0x0000 }, /* R1301 */
+	{ 0x0000, 0x0000 }, /* R1302 */
+	{ 0x0000, 0x0000 }, /* R1303 */
+	{ 0x0000, 0x0000 }, /* R1304 */
+	{ 0x0000, 0x0000 }, /* R1305 */
+	{ 0x0000, 0x0000 }, /* R1306 */
+	{ 0x0000, 0x0000 }, /* R1307 */
+	{ 0x0000, 0x0000 }, /* R1308 */
+	{ 0x0000, 0x0000 }, /* R1309 */
+	{ 0x0000, 0x0000 }, /* R1310 */
+	{ 0x0000, 0x0000 }, /* R1311 */
+	{ 0x02B6, 0x02B6 }, /* R1312  - AIF2 DAC Filters (1) */
+	{ 0x3F00, 0x3F00 }, /* R1313  - AIF2 DAC Filters (2) */
+	{ 0x0000, 0x0000 }, /* R1314 */
+	{ 0x0000, 0x0000 }, /* R1315 */
+	{ 0x0000, 0x0000 }, /* R1316 */
+	{ 0x0000, 0x0000 }, /* R1317 */
+	{ 0x0000, 0x0000 }, /* R1318 */
+	{ 0x0000, 0x0000 }, /* R1319 */
+	{ 0x0000, 0x0000 }, /* R1320 */
+	{ 0x0000, 0x0000 }, /* R1321 */
+	{ 0x0000, 0x0000 }, /* R1322 */
+	{ 0x0000, 0x0000 }, /* R1323 */
+	{ 0x0000, 0x0000 }, /* R1324 */
+	{ 0x0000, 0x0000 }, /* R1325 */
+	{ 0x0000, 0x0000 }, /* R1326 */
+	{ 0x0000, 0x0000 }, /* R1327 */
+	{ 0x0000, 0x0000 }, /* R1328 */
+	{ 0x0000, 0x0000 }, /* R1329 */
+	{ 0x0000, 0x0000 }, /* R1330 */
+	{ 0x0000, 0x0000 }, /* R1331 */
+	{ 0x0000, 0x0000 }, /* R1332 */
+	{ 0x0000, 0x0000 }, /* R1333 */
+	{ 0x0000, 0x0000 }, /* R1334 */
+	{ 0x0000, 0x0000 }, /* R1335 */
+	{ 0x0000, 0x0000 }, /* R1336 */
+	{ 0x0000, 0x0000 }, /* R1337 */
+	{ 0x0000, 0x0000 }, /* R1338 */
+	{ 0x0000, 0x0000 }, /* R1339 */
+	{ 0x0000, 0x0000 }, /* R1340 */
+	{ 0x0000, 0x0000 }, /* R1341 */
+	{ 0x0000, 0x0000 }, /* R1342 */
+	{ 0x0000, 0x0000 }, /* R1343 */
+	{ 0xFFFF, 0xFFFF }, /* R1344  - AIF2 DRC (1) */
+	{ 0x1FFF, 0x1FFF }, /* R1345  - AIF2 DRC (2) */
+	{ 0xFFFF, 0xFFFF }, /* R1346  - AIF2 DRC (3) */
+	{ 0x07FF, 0x07FF }, /* R1347  - AIF2 DRC (4) */
+	{ 0x03FF, 0x03FF }, /* R1348  - AIF2 DRC (5) */
+	{ 0x0000, 0x0000 }, /* R1349 */
+	{ 0x0000, 0x0000 }, /* R1350 */
+	{ 0x0000, 0x0000 }, /* R1351 */
+	{ 0x0000, 0x0000 }, /* R1352 */
+	{ 0x0000, 0x0000 }, /* R1353 */
+	{ 0x0000, 0x0000 }, /* R1354 */
+	{ 0x0000, 0x0000 }, /* R1355 */
+	{ 0x0000, 0x0000 }, /* R1356 */
+	{ 0x0000, 0x0000 }, /* R1357 */
+	{ 0x0000, 0x0000 }, /* R1358 */
+	{ 0x0000, 0x0000 }, /* R1359 */
+	{ 0x0000, 0x0000 }, /* R1360 */
+	{ 0x0000, 0x0000 }, /* R1361 */
+	{ 0x0000, 0x0000 }, /* R1362 */
+	{ 0x0000, 0x0000 }, /* R1363 */
+	{ 0x0000, 0x0000 }, /* R1364 */
+	{ 0x0000, 0x0000 }, /* R1365 */
+	{ 0x0000, 0x0000 }, /* R1366 */
+	{ 0x0000, 0x0000 }, /* R1367 */
+	{ 0x0000, 0x0000 }, /* R1368 */
+	{ 0x0000, 0x0000 }, /* R1369 */
+	{ 0x0000, 0x0000 }, /* R1370 */
+	{ 0x0000, 0x0000 }, /* R1371 */
+	{ 0x0000, 0x0000 }, /* R1372 */
+	{ 0x0000, 0x0000 }, /* R1373 */
+	{ 0x0000, 0x0000 }, /* R1374 */
+	{ 0x0000, 0x0000 }, /* R1375 */
+	{ 0x0000, 0x0000 }, /* R1376 */
+	{ 0x0000, 0x0000 }, /* R1377 */
+	{ 0x0000, 0x0000 }, /* R1378 */
+	{ 0x0000, 0x0000 }, /* R1379 */
+	{ 0x0000, 0x0000 }, /* R1380 */
+	{ 0x0000, 0x0000 }, /* R1381 */
+	{ 0x0000, 0x0000 }, /* R1382 */
+	{ 0x0000, 0x0000 }, /* R1383 */
+	{ 0x0000, 0x0000 }, /* R1384 */
+	{ 0x0000, 0x0000 }, /* R1385 */
+	{ 0x0000, 0x0000 }, /* R1386 */
+	{ 0x0000, 0x0000 }, /* R1387 */
+	{ 0x0000, 0x0000 }, /* R1388 */
+	{ 0x0000, 0x0000 }, /* R1389 */
+	{ 0x0000, 0x0000 }, /* R1390 */
+	{ 0x0000, 0x0000 }, /* R1391 */
+	{ 0x0000, 0x0000 }, /* R1392 */
+	{ 0x0000, 0x0000 }, /* R1393 */
+	{ 0x0000, 0x0000 }, /* R1394 */
+	{ 0x0000, 0x0000 }, /* R1395 */
+	{ 0x0000, 0x0000 }, /* R1396 */
+	{ 0x0000, 0x0000 }, /* R1397 */
+	{ 0x0000, 0x0000 }, /* R1398 */
+	{ 0x0000, 0x0000 }, /* R1399 */
+	{ 0x0000, 0x0000 }, /* R1400 */
+	{ 0x0000, 0x0000 }, /* R1401 */
+	{ 0x0000, 0x0000 }, /* R1402 */
+	{ 0x0000, 0x0000 }, /* R1403 */
+	{ 0x0000, 0x0000 }, /* R1404 */
+	{ 0x0000, 0x0000 }, /* R1405 */
+	{ 0x0000, 0x0000 }, /* R1406 */
+	{ 0x0000, 0x0000 }, /* R1407 */
+	{ 0xFFFF, 0xFFFF }, /* R1408  - AIF2 EQ Gains (1) */
+	{ 0xFFC0, 0xFFC0 }, /* R1409  - AIF2 EQ Gains (2) */
+	{ 0xFFFF, 0xFFFF }, /* R1410  - AIF2 EQ Band 1 A */
+	{ 0xFFFF, 0xFFFF }, /* R1411  - AIF2 EQ Band 1 B */
+	{ 0xFFFF, 0xFFFF }, /* R1412  - AIF2 EQ Band 1 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1413  - AIF2 EQ Band 2 A */
+	{ 0xFFFF, 0xFFFF }, /* R1414  - AIF2 EQ Band 2 B */
+	{ 0xFFFF, 0xFFFF }, /* R1415  - AIF2 EQ Band 2 C */
+	{ 0xFFFF, 0xFFFF }, /* R1416  - AIF2 EQ Band 2 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1417  - AIF2 EQ Band 3 A */
+	{ 0xFFFF, 0xFFFF }, /* R1418  - AIF2 EQ Band 3 B */
+	{ 0xFFFF, 0xFFFF }, /* R1419  - AIF2 EQ Band 3 C */
+	{ 0xFFFF, 0xFFFF }, /* R1420  - AIF2 EQ Band 3 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1421  - AIF2 EQ Band 4 A */
+	{ 0xFFFF, 0xFFFF }, /* R1422  - AIF2 EQ Band 4 B */
+	{ 0xFFFF, 0xFFFF }, /* R1423  - AIF2 EQ Band 4 C */
+	{ 0xFFFF, 0xFFFF }, /* R1424  - AIF2 EQ Band 4 PG */
+	{ 0xFFFF, 0xFFFF }, /* R1425  - AIF2 EQ Band 5 A */
+	{ 0xFFFF, 0xFFFF }, /* R1426  - AIF2 EQ Band 5 B */
+	{ 0xFFFF, 0xFFFF }, /* R1427  - AIF2 EQ Band 5 PG */
+	{ 0x0000, 0x0000 }, /* R1428 */
+	{ 0x0000, 0x0000 }, /* R1429 */
+	{ 0x0000, 0x0000 }, /* R1430 */
+	{ 0x0000, 0x0000 }, /* R1431 */
+	{ 0x0000, 0x0000 }, /* R1432 */
+	{ 0x0000, 0x0000 }, /* R1433 */
+	{ 0x0000, 0x0000 }, /* R1434 */
+	{ 0x0000, 0x0000 }, /* R1435 */
+	{ 0x0000, 0x0000 }, /* R1436 */
+	{ 0x0000, 0x0000 }, /* R1437 */
+	{ 0x0000, 0x0000 }, /* R1438 */
+	{ 0x0000, 0x0000 }, /* R1439 */
+	{ 0x0000, 0x0000 }, /* R1440 */
+	{ 0x0000, 0x0000 }, /* R1441 */
+	{ 0x0000, 0x0000 }, /* R1442 */
+	{ 0x0000, 0x0000 }, /* R1443 */
+	{ 0x0000, 0x0000 }, /* R1444 */
+	{ 0x0000, 0x0000 }, /* R1445 */
+	{ 0x0000, 0x0000 }, /* R1446 */
+	{ 0x0000, 0x0000 }, /* R1447 */
+	{ 0x0000, 0x0000 }, /* R1448 */
+	{ 0x0000, 0x0000 }, /* R1449 */
+	{ 0x0000, 0x0000 }, /* R1450 */
+	{ 0x0000, 0x0000 }, /* R1451 */
+	{ 0x0000, 0x0000 }, /* R1452 */
+	{ 0x0000, 0x0000 }, /* R1453 */
+	{ 0x0000, 0x0000 }, /* R1454 */
+	{ 0x0000, 0x0000 }, /* R1455 */
+	{ 0x0000, 0x0000 }, /* R1456 */
+	{ 0x0000, 0x0000 }, /* R1457 */
+	{ 0x0000, 0x0000 }, /* R1458 */
+	{ 0x0000, 0x0000 }, /* R1459 */
+	{ 0x0000, 0x0000 }, /* R1460 */
+	{ 0x0000, 0x0000 }, /* R1461 */
+	{ 0x0000, 0x0000 }, /* R1462 */
+	{ 0x0000, 0x0000 }, /* R1463 */
+	{ 0x0000, 0x0000 }, /* R1464 */
+	{ 0x0000, 0x0000 }, /* R1465 */
+	{ 0x0000, 0x0000 }, /* R1466 */
+	{ 0x0000, 0x0000 }, /* R1467 */
+	{ 0x0000, 0x0000 }, /* R1468 */
+	{ 0x0000, 0x0000 }, /* R1469 */
+	{ 0x0000, 0x0000 }, /* R1470 */
+	{ 0x0000, 0x0000 }, /* R1471 */
+	{ 0x0000, 0x0000 }, /* R1472 */
+	{ 0x0000, 0x0000 }, /* R1473 */
+	{ 0x0000, 0x0000 }, /* R1474 */
+	{ 0x0000, 0x0000 }, /* R1475 */
+	{ 0x0000, 0x0000 }, /* R1476 */
+	{ 0x0000, 0x0000 }, /* R1477 */
+	{ 0x0000, 0x0000 }, /* R1478 */
+	{ 0x0000, 0x0000 }, /* R1479 */
+	{ 0x0000, 0x0000 }, /* R1480 */
+	{ 0x0000, 0x0000 }, /* R1481 */
+	{ 0x0000, 0x0000 }, /* R1482 */
+	{ 0x0000, 0x0000 }, /* R1483 */
+	{ 0x0000, 0x0000 }, /* R1484 */
+	{ 0x0000, 0x0000 }, /* R1485 */
+	{ 0x0000, 0x0000 }, /* R1486 */
+	{ 0x0000, 0x0000 }, /* R1487 */
+	{ 0x0000, 0x0000 }, /* R1488 */
+	{ 0x0000, 0x0000 }, /* R1489 */
+	{ 0x0000, 0x0000 }, /* R1490 */
+	{ 0x0000, 0x0000 }, /* R1491 */
+	{ 0x0000, 0x0000 }, /* R1492 */
+	{ 0x0000, 0x0000 }, /* R1493 */
+	{ 0x0000, 0x0000 }, /* R1494 */
+	{ 0x0000, 0x0000 }, /* R1495 */
+	{ 0x0000, 0x0000 }, /* R1496 */
+	{ 0x0000, 0x0000 }, /* R1497 */
+	{ 0x0000, 0x0000 }, /* R1498 */
+	{ 0x0000, 0x0000 }, /* R1499 */
+	{ 0x0000, 0x0000 }, /* R1500 */
+	{ 0x0000, 0x0000 }, /* R1501 */
+	{ 0x0000, 0x0000 }, /* R1502 */
+	{ 0x0000, 0x0000 }, /* R1503 */
+	{ 0x0000, 0x0000 }, /* R1504 */
+	{ 0x0000, 0x0000 }, /* R1505 */
+	{ 0x0000, 0x0000 }, /* R1506 */
+	{ 0x0000, 0x0000 }, /* R1507 */
+	{ 0x0000, 0x0000 }, /* R1508 */
+	{ 0x0000, 0x0000 }, /* R1509 */
+	{ 0x0000, 0x0000 }, /* R1510 */
+	{ 0x0000, 0x0000 }, /* R1511 */
+	{ 0x0000, 0x0000 }, /* R1512 */
+	{ 0x0000, 0x0000 }, /* R1513 */
+	{ 0x0000, 0x0000 }, /* R1514 */
+	{ 0x0000, 0x0000 }, /* R1515 */
+	{ 0x0000, 0x0000 }, /* R1516 */
+	{ 0x0000, 0x0000 }, /* R1517 */
+	{ 0x0000, 0x0000 }, /* R1518 */
+	{ 0x0000, 0x0000 }, /* R1519 */
+	{ 0x0000, 0x0000 }, /* R1520 */
+	{ 0x0000, 0x0000 }, /* R1521 */
+	{ 0x0000, 0x0000 }, /* R1522 */
+	{ 0x0000, 0x0000 }, /* R1523 */
+	{ 0x0000, 0x0000 }, /* R1524 */
+	{ 0x0000, 0x0000 }, /* R1525 */
+	{ 0x0000, 0x0000 }, /* R1526 */
+	{ 0x0000, 0x0000 }, /* R1527 */
+	{ 0x0000, 0x0000 }, /* R1528 */
+	{ 0x0000, 0x0000 }, /* R1529 */
+	{ 0x0000, 0x0000 }, /* R1530 */
+	{ 0x0000, 0x0000 }, /* R1531 */
+	{ 0x0000, 0x0000 }, /* R1532 */
+	{ 0x0000, 0x0000 }, /* R1533 */
+	{ 0x0000, 0x0000 }, /* R1534 */
+	{ 0x0000, 0x0000 }, /* R1535 */
+	{ 0x01EF, 0x01EF }, /* R1536  - DAC1 Mixer Volumes */
+	{ 0x0037, 0x0037 }, /* R1537  - DAC1 Left Mixer Routing */
+	{ 0x0037, 0x0037 }, /* R1538  - DAC1 Right Mixer Routing */
+	{ 0x01EF, 0x01EF }, /* R1539  - DAC2 Mixer Volumes */
+	{ 0x0037, 0x0037 }, /* R1540  - DAC2 Left Mixer Routing */
+	{ 0x0037, 0x0037 }, /* R1541  - DAC2 Right Mixer Routing */
+	{ 0x0003, 0x0003 }, /* R1542  - AIF1 ADC1 Left Mixer Routing */
+	{ 0x0003, 0x0003 }, /* R1543  - AIF1 ADC1 Right Mixer Routing */
+	{ 0x0003, 0x0003 }, /* R1544  - AIF1 ADC2 Left Mixer Routing */
+	{ 0x0003, 0x0003 }, /* R1545  - AIF1 ADC2 Right mixer Routing */
+	{ 0x0000, 0x0000 }, /* R1546 */
+	{ 0x0000, 0x0000 }, /* R1547 */
+	{ 0x0000, 0x0000 }, /* R1548 */
+	{ 0x0000, 0x0000 }, /* R1549 */
+	{ 0x0000, 0x0000 }, /* R1550 */
+	{ 0x0000, 0x0000 }, /* R1551 */
+	{ 0x02FF, 0x03FF }, /* R1552  - DAC1 Left Volume */
+	{ 0x02FF, 0x03FF }, /* R1553  - DAC1 Right Volume */
+	{ 0x02FF, 0x03FF }, /* R1554  - DAC2 Left Volume */
+	{ 0x02FF, 0x03FF }, /* R1555  - DAC2 Right Volume */
+	{ 0x0003, 0x0003 }, /* R1556  - DAC Softmute */
+	{ 0x0000, 0x0000 }, /* R1557 */
+	{ 0x0000, 0x0000 }, /* R1558 */
+	{ 0x0000, 0x0000 }, /* R1559 */
+	{ 0x0000, 0x0000 }, /* R1560 */
+	{ 0x0000, 0x0000 }, /* R1561 */
+	{ 0x0000, 0x0000 }, /* R1562 */
+	{ 0x0000, 0x0000 }, /* R1563 */
+	{ 0x0000, 0x0000 }, /* R1564 */
+	{ 0x0000, 0x0000 }, /* R1565 */
+	{ 0x0000, 0x0000 }, /* R1566 */
+	{ 0x0000, 0x0000 }, /* R1567 */
+	{ 0x0003, 0x0003 }, /* R1568  - Oversampling */
+	{ 0x03C3, 0x03C3 }, /* R1569  - Sidetone */
 };
 
 static int wm8994_readable(unsigned int reg)



^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
  2010-10-13 15:11               ` Mark Brown
  (?)
@ 2010-10-13 15:27               ` Joe Perches
  2010-10-13 15:29                   ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Joe Perches @ 2010-10-13 15:27 UTC (permalink / raw)
  To: Mark Brown
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, 2010-10-13 at 16:11 +0100, Mark Brown wrote:
> On Wed, Oct 13, 2010 at 05:55:48AM -0700, Joe Perches wrote:
> > Looking at the 2 register access routines where
> > volatile_register/vol is used, I'd say those
> > routines aren't useful at all and the checks
> > should be inline instead.
> If you check the code again you will notice that these functons are all
> used in ops structures which presents obvious issues when trying to
> replace with open coded checks.
> I hope their usefulness is clear given the above.

There is some value in using consistent
function styles for table driven and non-table
driven instances.

> > > It's used as the field name for volatility in all the
> > > drivers I can remember that use a table to look volatility up in
> > > register properties.
> > I did a grep for vol in sound, I found the uses
> > where it was for volatile and patched them.
> You're missing the "for this" here, I believe.  To reiterate what I said
> originally as far as I can remember every ASoC driver which uses a table
> to look up volatility uses this naming.

So all in this case means 3, 1 of which
doesn't use it.  I sent patches for them.

The last patch saves ~3kb which I think
is useful for ASoC.

cheers, Joe


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
  2010-10-13 15:27               ` Joe Perches
@ 2010-10-13 15:29                   ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 15:29 UTC (permalink / raw)
  To: Joe Perches
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010 at 08:27:47AM -0700, Joe Perches wrote:
> On Wed, 2010-10-13 at 16:11 +0100, Mark Brown wrote:

> > If you check the code again you will notice that these functons are all
> > used in ops structures which presents obvious issues when trying to
> > replace with open coded checks.
> > I hope their usefulness is clear given the above.

> There is some value in using consistent
> function styles for table driven and non-table
> driven instances.

I'm sorry, I don't understand what you are saying here.  What do you
believe to be inconsistent?

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
@ 2010-10-13 15:29                   ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 15:29 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Peter Hsiang, Jesse Marroquin, Liam Girdwood

On Wed, Oct 13, 2010 at 08:27:47AM -0700, Joe Perches wrote:
> On Wed, 2010-10-13 at 16:11 +0100, Mark Brown wrote:

> > If you check the code again you will notice that these functons are all
> > used in ops structures which presents obvious issues when trying to
> > replace with open coded checks.
> > I hope their usefulness is clear given the above.

> There is some value in using consistent
> function styles for table driven and non-table
> driven instances.

I'm sorry, I don't understand what you are saying here.  What do you
believe to be inconsistent?

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc: rename vol to volatile_register as appropriate
  2010-10-13 15:29                   ` Mark Brown
  (?)
@ 2010-10-13 15:35                   ` Joe Perches
  -1 siblings, 0 replies; 81+ messages in thread
From: Joe Perches @ 2010-10-13 15:35 UTC (permalink / raw)
  To: Mark Brown
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, 2010-10-13 at 16:29 +0100, Mark Brown wrote:
> On Wed, Oct 13, 2010 at 08:27:47AM -0700, Joe Perches wrote:
> > On Wed, 2010-10-13 at 16:11 +0100, Mark Brown wrote:
> > > If you check the code again you will notice that these functons are all
> > > used in ops structures which presents obvious issues when trying to
> > > replace with open coded checks.
> > > I hope their usefulness is clear given the above.
> > There is some value in using consistent
> > function styles for table driven and non-table
> > driven instances.
> I'm sorry, I don't understand what you are saying here.  What do you
> believe to be inconsistent?

I don't believe anything to be inconsistent about
the volatile uses and was agreeing with you.

Some codecs functions for volatile access use
a switch/case,

static int wm8994_volatile(unsigned int reg)
{
	if (reg >= WM8994_REG_CACHE_SIZE)
		return 1;

	switch (reg) {
	case WM8994_SOFTWARE_RESET:
	case WM8994_CHIP_REVISION:
	case WM8994_DC_SERVO_1:
	case WM8994_DC_SERVO_READBACK:
	case WM8994_RATE_STATUS:
	case WM8994_LDO_1:
	case WM8994_LDO_2:
		return 1;
	default:
		return 0;
	}
}

others use the register variable from the struct

static int wm8962_volatile_register(unsigned int reg)
{
	if (wm8962_reg_access[reg].volatile_register)
		return 1;
	else
		return 0;
}

so I'm agreeing that it's useful to keep the
same access style in multiple codecs instead
of using separate styles in each one.

It'd be even better to use a similarly consistent
function naming scheme.

cheers, Joe


^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text
  2010-10-13 15:29                   ` Mark Brown
  (?)
  (?)
@ 2010-10-13 19:10                   ` Joe Perches
  2010-10-13 19:40                       ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Joe Perches @ 2010-10-13 19:10 UTC (permalink / raw)
  To: Mark Brown
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

Perhaps something like this is useful?

I don't know if the binary chop is too cpu
intensive or if too much data access would be
a problem.

There are about 1200 used array entries, so
it may take up to 12 array accesses to find a
match or not-match.

Replace the large array with an indexed array
and routine to find the index.

The array must be kept in increasing index order.

Compiled, untested.

$ size sound/soc/codecs/wm8962.o.*
   text	   data	    bss	    dec	    hex	filename
  81313	   3852	   3944	  89109	  15c15	sound/soc/codecs/wm8962.o.new
 198845	   3852	   3880	 206577	  326f1	sound/soc/codecs/wm8962.o.old

---
 sound/soc/codecs/wm8962.c | 2311 +++++++++++++++++++++++----------------------
 1 files changed, 1167 insertions(+), 1144 deletions(-)

diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index e5009d0..a2da5ef 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -794,1155 +794,1174 @@ static const u16 wm8962_reg[WM8962_MAX_REGISTER + 1] = {
 };
 
 static const struct wm8962_reg_access {
+	u16 reg;
 	u16 read;
 	u16 write;
-	u16 vol;
-} wm8962_reg_access[WM8962_MAX_REGISTER + 1] = {
-	[0] = { 0x00FF, 0x01FF, 0x0000 }, /* R0     - Left Input volume */
-	[1] = { 0xFEFF, 0x01FF, 0xFFFF }, /* R1     - Right Input volume */
-	[2] = { 0x00FF, 0x01FF, 0x0000 }, /* R2     - HPOUTL volume */
-	[3] = { 0x00FF, 0x01FF, 0x0000 }, /* R3     - HPOUTR volume */
-	[4] = { 0x07FE, 0x07FE, 0xFFFF }, /* R4     - Clocking1 */
-	[5] = { 0x007F, 0x007F, 0x0000 }, /* R5     - ADC & DAC Control 1 */
-	[6] = { 0x37ED, 0x37ED, 0x0000 }, /* R6     - ADC & DAC Control 2 */
-	[7] = { 0x1FFF, 0x1FFF, 0x0000 }, /* R7     - Audio Interface 0 */
-	[8] = { 0x0FEF, 0x0FEF, 0xFFFF }, /* R8     - Clocking2 */
-	[9] = { 0x0B9F, 0x039F, 0x0000 }, /* R9     - Audio Interface 1 */
-	[10] = { 0x00FF, 0x01FF, 0x0000 }, /* R10    - Left DAC volume */
-	[11] = { 0x00FF, 0x01FF, 0x0000 }, /* R11    - Right DAC volume */
-	[14] = { 0x07FF, 0x07FF, 0x0000 }, /* R14    - Audio Interface 2 */
-	[15] = { 0xFFFF, 0xFFFF, 0xFFFF }, /* R15    - Software Reset */
-	[17] = { 0x07FF, 0x07FF, 0x0000 }, /* R17    - ALC1 */
-	[18] = { 0xF8FF, 0x00FF, 0xFFFF }, /* R18    - ALC2 */
-	[19] = { 0x1DFF, 0x1DFF, 0x0000 }, /* R19    - ALC3 */
-	[20] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20    - Noise Gate */
-	[21] = { 0x00FF, 0x01FF, 0x0000 }, /* R21    - Left ADC volume */
-	[22] = { 0x00FF, 0x01FF, 0x0000 }, /* R22    - Right ADC volume */
-	[23] = { 0x0161, 0x0161, 0x0000 }, /* R23    - Additional control(1) */
-	[24] = { 0x0008, 0x0008, 0x0000 }, /* R24    - Additional control(2) */
-	[25] = { 0x07FE, 0x07FE, 0x0000 }, /* R25    - Pwr Mgmt (1) */
-	[26] = { 0x01FB, 0x01FB, 0x0000 }, /* R26    - Pwr Mgmt (2) */
-	[27] = { 0x0017, 0x0017, 0x0000 }, /* R27    - Additional Control (3) */
-	[28] = { 0x001C, 0x001C, 0x0000 }, /* R28    - Anti-pop */
-
-	[30] = { 0xFFFE, 0xFFFE, 0x0000 }, /* R30    - Clocking 3 */
-	[31] = { 0x000F, 0x000F, 0x0000 }, /* R31    - Input mixer control (1) */
-	[32] = { 0x01FF, 0x01FF, 0x0000 }, /* R32    - Left input mixer volume */
-	[33] = { 0x01FF, 0x01FF, 0x0000 }, /* R33    - Right input mixer volume */
-	[34] = { 0x003F, 0x003F, 0x0000 }, /* R34    - Input mixer control (2) */
-	[35] = { 0x003F, 0x003F, 0x0000 }, /* R35    - Input bias control */
-	[37] = { 0x001F, 0x001F, 0x0000 }, /* R37    - Left input PGA control */
-	[38] = { 0x001F, 0x001F, 0x0000 }, /* R38    - Right input PGA control */
-	[40] = { 0x00FF, 0x01FF, 0x0000 }, /* R40    - SPKOUTL volume */
-	[41] = { 0x00FF, 0x01FF, 0x0000 }, /* R41    - SPKOUTR volume */
-
-	[47] = { 0x000F, 0x0000, 0x0000 }, /* R47    - Thermal Shutdown Status */
-	[48] = { 0x7EC7, 0x7E07, 0xFFFF }, /* R48    - Additional Control (4) */
-	[49] = { 0x00D3, 0x00D7, 0xFFFF }, /* R49    - Class D Control 1 */
-	[51] = { 0x0047, 0x0047, 0x0000 }, /* R51    - Class D Control 2 */
-	[56] = { 0x001E, 0x001E, 0x0000 }, /* R56    - Clocking 4 */
-	[57] = { 0x02FC, 0x02FC, 0x0000 }, /* R57    - DAC DSP Mixing (1) */
-	[58] = { 0x00FC, 0x00FC, 0x0000 }, /* R58    - DAC DSP Mixing (2) */
-	[60] = { 0x00CC, 0x00CC, 0x0000 }, /* R60    - DC Servo 0 */
-	[61] = { 0x00DD, 0x00DD, 0x0000 }, /* R61    - DC Servo 1 */
-	[64] = { 0x3F80, 0x3F80, 0x0000 }, /* R64    - DC Servo 4 */
-	[66] = { 0x0780, 0x0000, 0xFFFF }, /* R66    - DC Servo 6 */
-	[68] = { 0x0007, 0x0007, 0x0000 }, /* R68    - Analogue PGA Bias */
-	[69] = { 0x00FF, 0x00FF, 0x0000 }, /* R69    - Analogue HP 0 */
-	[71] = { 0x01FF, 0x01FF, 0x0000 }, /* R71    - Analogue HP 2 */
-	[72] = { 0x0001, 0x0001, 0x0000 }, /* R72    - Charge Pump 1 */
-	[82] = { 0x0001, 0x0001, 0x0000 }, /* R82    - Charge Pump B */
-	[87] = { 0x00A0, 0x00A0, 0x0000 }, /* R87    - Write Sequencer Control 1 */
-	[90] = { 0x007F, 0x01FF, 0x0000 }, /* R90    - Write Sequencer Control 2 */
-	[93] = { 0x03F9, 0x0000, 0x0000 }, /* R93    - Write Sequencer Control 3 */
-	[94] = { 0x0070, 0x0070, 0x0000 }, /* R94    - Control Interface */
-	[99] = { 0x000F, 0x000F, 0x0000 }, /* R99    - Mixer Enables */
-	[100] = { 0x00BF, 0x00BF, 0x0000 }, /* R100   - Headphone Mixer (1) */
-	[101] = { 0x00BF, 0x00BF, 0x0000 }, /* R101   - Headphone Mixer (2) */
-	[102] = { 0x01FF, 0x01FF, 0x0000 }, /* R102   - Headphone Mixer (3) */
-	[103] = { 0x01FF, 0x01FF, 0x0000 }, /* R103   - Headphone Mixer (4) */
-	[105] = { 0x00BF, 0x00BF, 0x0000 }, /* R105   - Speaker Mixer (1) */
-	[106] = { 0x00BF, 0x00BF, 0x0000 }, /* R106   - Speaker Mixer (2) */
-	[107] = { 0x01FF, 0x01FF, 0x0000 }, /* R107   - Speaker Mixer (3) */
-	[108] = { 0x01FF, 0x01FF, 0x0000 }, /* R108   - Speaker Mixer (4) */
-	[109] = { 0x00F0, 0x00F0, 0x0000 }, /* R109   - Speaker Mixer (5) */
-	[110] = { 0x00F7, 0x00F7, 0x0000 }, /* R110   - Beep Generator (1) */
-	[115] = { 0x001F, 0x001F, 0x0000 }, /* R115   - Oscillator Trim (3) */
-	[116] = { 0x001F, 0x001F, 0x0000 }, /* R116   - Oscillator Trim (4) */
-	[119] = { 0x00FF, 0x00FF, 0x0000 }, /* R119   - Oscillator Trim (7) */
-	[124] = { 0x0079, 0x0079, 0x0000 }, /* R124   - Analogue Clocking1 */
-	[125] = { 0x00DF, 0x00DF, 0x0000 }, /* R125   - Analogue Clocking2 */
-	[126] = { 0x000D, 0x000D, 0x0000 }, /* R126   - Analogue Clocking3 */
-	[127] = { 0x0000, 0xFFFF, 0x0000 }, /* R127   - PLL Software Reset */
-	[129] = { 0x00B0, 0x00B0, 0x0000 }, /* R129   - PLL2 */
-	[131] = { 0x0003, 0x0003, 0x0000 }, /* R131   - PLL 4 */
-	[136] = { 0x005F, 0x005F, 0x0000 }, /* R136   - PLL 9 */
-	[137] = { 0x00FF, 0x00FF, 0x0000 }, /* R137   - PLL 10 */
-	[138] = { 0x00FF, 0x00FF, 0x0000 }, /* R138   - PLL 11 */
-	[139] = { 0x00FF, 0x00FF, 0x0000 }, /* R139   - PLL 12 */
-	[140] = { 0x005F, 0x005F, 0x0000 }, /* R140   - PLL 13 */
-	[141] = { 0x00FF, 0x00FF, 0x0000 }, /* R141   - PLL 14 */
-	[142] = { 0x00FF, 0x00FF, 0x0000 }, /* R142   - PLL 15 */
-	[143] = { 0x00FF, 0x00FF, 0x0000 }, /* R143   - PLL 16 */
-	[155] = { 0x0067, 0x0067, 0x0000 }, /* R155   - FLL Control (1) */
-	[156] = { 0x01FB, 0x01FB, 0x0000 }, /* R156   - FLL Control (2) */
-	[157] = { 0x0007, 0x0007, 0x0000 }, /* R157   - FLL Control (3) */
-	[159] = { 0x007F, 0x007F, 0x0000 }, /* R159   - FLL Control (5) */
-	[160] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R160   - FLL Control (6) */
-	[161] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R161   - FLL Control (7) */
-	[162] = { 0x03FF, 0x03FF, 0x0000 }, /* R162   - FLL Control (8) */
-	[252] = { 0x0005, 0x0005, 0x0000 }, /* R252   - General test 1 */
-	[256] = { 0x000F, 0x000F, 0x0000 }, /* R256   - DF1 */
-	[257] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R257   - DF2 */
-	[258] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R258   - DF3 */
-	[259] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R259   - DF4 */
-	[260] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R260   - DF5 */
-	[261] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R261   - DF6 */
-	[262] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R262   - DF7 */
-	[264] = { 0x0003, 0x0003, 0x0000 }, /* R264   - LHPF1 */
-	[265] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R265   - LHPF2 */
-	[268] = { 0x0077, 0x0077, 0x0000 }, /* R268   - THREED1 */
-	[269] = { 0xFFFC, 0xFFFC, 0x0000 }, /* R269   - THREED2 */
-	[270] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R270   - THREED3 */
-	[271] = { 0xFFFC, 0xFFFC, 0x0000 }, /* R271   - THREED4 */
-	[276] = { 0x7FFF, 0x7FFF, 0x0000 }, /* R276   - DRC 1 */
-	[277] = { 0x1FFF, 0x1FFF, 0x0000 }, /* R277   - DRC 2 */
-	[278] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R278   - DRC 3 */
-	[279] = { 0x07FF, 0x07FF, 0x0000 }, /* R279   - DRC 4 */
-	[280] = { 0x03FF, 0x03FF, 0x0000 }, /* R280   - DRC 5 */
-	[285] = { 0x0003, 0x0003, 0x0000 }, /* R285   - Tloopback */
-	[335] = { 0x0007, 0x0007, 0x0000 }, /* R335   - EQ1 */
-	[336] = { 0xFFFE, 0xFFFE, 0x0000 }, /* R336   - EQ2 */
-	[337] = { 0xFFC0, 0xFFC0, 0x0000 }, /* R337   - EQ3 */
-	[338] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R338   - EQ4 */
-	[339] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R339   - EQ5 */
-	[340] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R340   - EQ6 */
-	[341] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R341   - EQ7 */
-	[342] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R342   - EQ8 */
-	[343] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R343   - EQ9 */
-	[344] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R344   - EQ10 */
-	[345] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R345   - EQ11 */
-	[346] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R346   - EQ12 */
-	[347] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R347   - EQ13 */
-	[348] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R348   - EQ14 */
-	[349] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R349   - EQ15 */
-	[350] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R350   - EQ16 */
-	[351] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R351   - EQ17 */
-	[352] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R352   - EQ18 */
-	[353] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R353   - EQ19 */
-	[354] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R354   - EQ20 */
-	[355] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R355   - EQ21 */
-	[356] = { 0xFFFE, 0xFFFE, 0x0000 }, /* R356   - EQ22 */
-	[357] = { 0xFFC0, 0xFFC0, 0x0000 }, /* R357   - EQ23 */
-	[358] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R358   - EQ24 */
-	[359] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R359   - EQ25 */
-	[360] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R360   - EQ26 */
-	[361] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R361   - EQ27 */
-	[362] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R362   - EQ28 */
-	[363] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R363   - EQ29 */
-	[364] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R364   - EQ30 */
-	[365] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R365   - EQ31 */
-	[366] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R366   - EQ32 */
-	[367] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R367   - EQ33 */
-	[368] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R368   - EQ34 */
-	[369] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R369   - EQ35 */
-	[370] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R370   - EQ36 */
-	[371] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R371   - EQ37 */
-	[372] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R372   - EQ38 */
-	[373] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R373   - EQ39 */
-	[374] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R374   - EQ40 */
-	[375] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R375   - EQ41 */
-	[513] = { 0x045F, 0x045F, 0x0000 }, /* R513   - GPIO 2 */
-	[514] = { 0x045F, 0x045F, 0x0000 }, /* R514   - GPIO 3 */
-	[516] = { 0xE75F, 0xE75F, 0x0000 }, /* R516   - GPIO 5 */
-	[517] = { 0xE75F, 0xE75F, 0x0000 }, /* R517   - GPIO 6 */
-	[560] = { 0x0030, 0x0030, 0xFFFF }, /* R560   - Interrupt Status 1 */
-	[561] = { 0xFFED, 0xFFED, 0xFFFF }, /* R561   - Interrupt Status 2 */
-	[568] = { 0x0030, 0x0030, 0x0000 }, /* R568   - Interrupt Status 1 Mask */
-	[569] = { 0xFFED, 0xFFED, 0x0000 }, /* R569   - Interrupt Status 2 Mask */
-	[576] = { 0x0001, 0x0001, 0x0000 }, /* R576   - Interrupt Control */
-	[584] = { 0x002D, 0x002D, 0x0000 }, /* R584   - IRQ Debounce */
-	[586] = { 0xC000, 0xC000, 0x0000 }, /* R586   -  MICINT Source Pol */
-	[768] = { 0x0001, 0x0001, 0x0000 }, /* R768   - DSP2 Power Management */
-	[1037] = { 0x0000, 0x003F, 0x0000 }, /* R1037  - DSP2_ExecControl */
-	[4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096  - Write Sequencer 0 */
-	[4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097  - Write Sequencer 1 */
-	[4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098  - Write Sequencer 2 */
-	[4099] = { 0x010F, 0x010F, 0x0000 }, /* R4099  - Write Sequencer 3 */
-	[4100] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4100  - Write Sequencer 4 */
-	[4101] = { 0x00FF, 0x00FF, 0x0000 }, /* R4101  - Write Sequencer 5 */
-	[4102] = { 0x070F, 0x070F, 0x0000 }, /* R4102  - Write Sequencer 6 */
-	[4103] = { 0x010F, 0x010F, 0x0000 }, /* R4103  - Write Sequencer 7 */
-	[4104] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4104  - Write Sequencer 8 */
-	[4105] = { 0x00FF, 0x00FF, 0x0000 }, /* R4105  - Write Sequencer 9 */
-	[4106] = { 0x070F, 0x070F, 0x0000 }, /* R4106  - Write Sequencer 10 */
-	[4107] = { 0x010F, 0x010F, 0x0000 }, /* R4107  - Write Sequencer 11 */
-	[4108] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4108  - Write Sequencer 12 */
-	[4109] = { 0x00FF, 0x00FF, 0x0000 }, /* R4109  - Write Sequencer 13 */
-	[4110] = { 0x070F, 0x070F, 0x0000 }, /* R4110  - Write Sequencer 14 */
-	[4111] = { 0x010F, 0x010F, 0x0000 }, /* R4111  - Write Sequencer 15 */
-	[4112] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4112  - Write Sequencer 16 */
-	[4113] = { 0x00FF, 0x00FF, 0x0000 }, /* R4113  - Write Sequencer 17 */
-	[4114] = { 0x070F, 0x070F, 0x0000 }, /* R4114  - Write Sequencer 18 */
-	[4115] = { 0x010F, 0x010F, 0x0000 }, /* R4115  - Write Sequencer 19 */
-	[4116] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4116  - Write Sequencer 20 */
-	[4117] = { 0x00FF, 0x00FF, 0x0000 }, /* R4117  - Write Sequencer 21 */
-	[4118] = { 0x070F, 0x070F, 0x0000 }, /* R4118  - Write Sequencer 22 */
-	[4119] = { 0x010F, 0x010F, 0x0000 }, /* R4119  - Write Sequencer 23 */
-	[4120] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4120  - Write Sequencer 24 */
-	[4121] = { 0x00FF, 0x00FF, 0x0000 }, /* R4121  - Write Sequencer 25 */
-	[4122] = { 0x070F, 0x070F, 0x0000 }, /* R4122  - Write Sequencer 26 */
-	[4123] = { 0x010F, 0x010F, 0x0000 }, /* R4123  - Write Sequencer 27 */
-	[4124] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4124  - Write Sequencer 28 */
-	[4125] = { 0x00FF, 0x00FF, 0x0000 }, /* R4125  - Write Sequencer 29 */
-	[4126] = { 0x070F, 0x070F, 0x0000 }, /* R4126  - Write Sequencer 30 */
-	[4127] = { 0x010F, 0x010F, 0x0000 }, /* R4127  - Write Sequencer 31 */
-	[4128] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4128  - Write Sequencer 32 */
-	[4129] = { 0x00FF, 0x00FF, 0x0000 }, /* R4129  - Write Sequencer 33 */
-	[4130] = { 0x070F, 0x070F, 0x0000 }, /* R4130  - Write Sequencer 34 */
-	[4131] = { 0x010F, 0x010F, 0x0000 }, /* R4131  - Write Sequencer 35 */
-	[4132] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4132  - Write Sequencer 36 */
-	[4133] = { 0x00FF, 0x00FF, 0x0000 }, /* R4133  - Write Sequencer 37 */
-	[4134] = { 0x070F, 0x070F, 0x0000 }, /* R4134  - Write Sequencer 38 */
-	[4135] = { 0x010F, 0x010F, 0x0000 }, /* R4135  - Write Sequencer 39 */
-	[4136] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4136  - Write Sequencer 40 */
-	[4137] = { 0x00FF, 0x00FF, 0x0000 }, /* R4137  - Write Sequencer 41 */
-	[4138] = { 0x070F, 0x070F, 0x0000 }, /* R4138  - Write Sequencer 42 */
-	[4139] = { 0x010F, 0x010F, 0x0000 }, /* R4139  - Write Sequencer 43 */
-	[4140] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4140  - Write Sequencer 44 */
-	[4141] = { 0x00FF, 0x00FF, 0x0000 }, /* R4141  - Write Sequencer 45 */
-	[4142] = { 0x070F, 0x070F, 0x0000 }, /* R4142  - Write Sequencer 46 */
-	[4143] = { 0x010F, 0x010F, 0x0000 }, /* R4143  - Write Sequencer 47 */
-	[4144] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4144  - Write Sequencer 48 */
-	[4145] = { 0x00FF, 0x00FF, 0x0000 }, /* R4145  - Write Sequencer 49 */
-	[4146] = { 0x070F, 0x070F, 0x0000 }, /* R4146  - Write Sequencer 50 */
-	[4147] = { 0x010F, 0x010F, 0x0000 }, /* R4147  - Write Sequencer 51 */
-	[4148] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4148  - Write Sequencer 52 */
-	[4149] = { 0x00FF, 0x00FF, 0x0000 }, /* R4149  - Write Sequencer 53 */
-	[4150] = { 0x070F, 0x070F, 0x0000 }, /* R4150  - Write Sequencer 54 */
-	[4151] = { 0x010F, 0x010F, 0x0000 }, /* R4151  - Write Sequencer 55 */
-	[4152] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4152  - Write Sequencer 56 */
-	[4153] = { 0x00FF, 0x00FF, 0x0000 }, /* R4153  - Write Sequencer 57 */
-	[4154] = { 0x070F, 0x070F, 0x0000 }, /* R4154  - Write Sequencer 58 */
-	[4155] = { 0x010F, 0x010F, 0x0000 }, /* R4155  - Write Sequencer 59 */
-	[4156] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4156  - Write Sequencer 60 */
-	[4157] = { 0x00FF, 0x00FF, 0x0000 }, /* R4157  - Write Sequencer 61 */
-	[4158] = { 0x070F, 0x070F, 0x0000 }, /* R4158  - Write Sequencer 62 */
-	[4159] = { 0x010F, 0x010F, 0x0000 }, /* R4159  - Write Sequencer 63 */
-	[4160] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4160  - Write Sequencer 64 */
-	[4161] = { 0x00FF, 0x00FF, 0x0000 }, /* R4161  - Write Sequencer 65 */
-	[4162] = { 0x070F, 0x070F, 0x0000 }, /* R4162  - Write Sequencer 66 */
-	[4163] = { 0x010F, 0x010F, 0x0000 }, /* R4163  - Write Sequencer 67 */
-	[4164] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4164  - Write Sequencer 68 */
-	[4165] = { 0x00FF, 0x00FF, 0x0000 }, /* R4165  - Write Sequencer 69 */
-	[4166] = { 0x070F, 0x070F, 0x0000 }, /* R4166  - Write Sequencer 70 */
-	[4167] = { 0x010F, 0x010F, 0x0000 }, /* R4167  - Write Sequencer 71 */
-	[4168] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4168  - Write Sequencer 72 */
-	[4169] = { 0x00FF, 0x00FF, 0x0000 }, /* R4169  - Write Sequencer 73 */
-	[4170] = { 0x070F, 0x070F, 0x0000 }, /* R4170  - Write Sequencer 74 */
-	[4171] = { 0x010F, 0x010F, 0x0000 }, /* R4171  - Write Sequencer 75 */
-	[4172] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4172  - Write Sequencer 76 */
-	[4173] = { 0x00FF, 0x00FF, 0x0000 }, /* R4173  - Write Sequencer 77 */
-	[4174] = { 0x070F, 0x070F, 0x0000 }, /* R4174  - Write Sequencer 78 */
-	[4175] = { 0x010F, 0x010F, 0x0000 }, /* R4175  - Write Sequencer 79 */
-	[4176] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4176  - Write Sequencer 80 */
-	[4177] = { 0x00FF, 0x00FF, 0x0000 }, /* R4177  - Write Sequencer 81 */
-	[4178] = { 0x070F, 0x070F, 0x0000 }, /* R4178  - Write Sequencer 82 */
-	[4179] = { 0x010F, 0x010F, 0x0000 }, /* R4179  - Write Sequencer 83 */
-	[4180] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4180  - Write Sequencer 84 */
-	[4181] = { 0x00FF, 0x00FF, 0x0000 }, /* R4181  - Write Sequencer 85 */
-	[4182] = { 0x070F, 0x070F, 0x0000 }, /* R4182  - Write Sequencer 86 */
-	[4183] = { 0x010F, 0x010F, 0x0000 }, /* R4183  - Write Sequencer 87 */
-	[4184] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4184  - Write Sequencer 88 */
-	[4185] = { 0x00FF, 0x00FF, 0x0000 }, /* R4185  - Write Sequencer 89 */
-	[4186] = { 0x070F, 0x070F, 0x0000 }, /* R4186  - Write Sequencer 90 */
-	[4187] = { 0x010F, 0x010F, 0x0000 }, /* R4187  - Write Sequencer 91 */
-	[4188] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4188  - Write Sequencer 92 */
-	[4189] = { 0x00FF, 0x00FF, 0x0000 }, /* R4189  - Write Sequencer 93 */
-	[4190] = { 0x070F, 0x070F, 0x0000 }, /* R4190  - Write Sequencer 94 */
-	[4191] = { 0x010F, 0x010F, 0x0000 }, /* R4191  - Write Sequencer 95 */
-	[4192] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4192  - Write Sequencer 96 */
-	[4193] = { 0x00FF, 0x00FF, 0x0000 }, /* R4193  - Write Sequencer 97 */
-	[4194] = { 0x070F, 0x070F, 0x0000 }, /* R4194  - Write Sequencer 98 */
-	[4195] = { 0x010F, 0x010F, 0x0000 }, /* R4195  - Write Sequencer 99 */
-	[4196] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4196  - Write Sequencer 100 */
-	[4197] = { 0x00FF, 0x00FF, 0x0000 }, /* R4197  - Write Sequencer 101 */
-	[4198] = { 0x070F, 0x070F, 0x0000 }, /* R4198  - Write Sequencer 102 */
-	[4199] = { 0x010F, 0x010F, 0x0000 }, /* R4199  - Write Sequencer 103 */
-	[4200] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4200  - Write Sequencer 104 */
-	[4201] = { 0x00FF, 0x00FF, 0x0000 }, /* R4201  - Write Sequencer 105 */
-	[4202] = { 0x070F, 0x070F, 0x0000 }, /* R4202  - Write Sequencer 106 */
-	[4203] = { 0x010F, 0x010F, 0x0000 }, /* R4203  - Write Sequencer 107 */
-	[4204] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4204  - Write Sequencer 108 */
-	[4205] = { 0x00FF, 0x00FF, 0x0000 }, /* R4205  - Write Sequencer 109 */
-	[4206] = { 0x070F, 0x070F, 0x0000 }, /* R4206  - Write Sequencer 110 */
-	[4207] = { 0x010F, 0x010F, 0x0000 }, /* R4207  - Write Sequencer 111 */
-	[4208] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4208  - Write Sequencer 112 */
-	[4209] = { 0x00FF, 0x00FF, 0x0000 }, /* R4209  - Write Sequencer 113 */
-	[4210] = { 0x070F, 0x070F, 0x0000 }, /* R4210  - Write Sequencer 114 */
-	[4211] = { 0x010F, 0x010F, 0x0000 }, /* R4211  - Write Sequencer 115 */
-	[4212] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4212  - Write Sequencer 116 */
-	[4213] = { 0x00FF, 0x00FF, 0x0000 }, /* R4213  - Write Sequencer 117 */
-	[4214] = { 0x070F, 0x070F, 0x0000 }, /* R4214  - Write Sequencer 118 */
-	[4215] = { 0x010F, 0x010F, 0x0000 }, /* R4215  - Write Sequencer 119 */
-	[4216] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4216  - Write Sequencer 120 */
-	[4217] = { 0x00FF, 0x00FF, 0x0000 }, /* R4217  - Write Sequencer 121 */
-	[4218] = { 0x070F, 0x070F, 0x0000 }, /* R4218  - Write Sequencer 122 */
-	[4219] = { 0x010F, 0x010F, 0x0000 }, /* R4219  - Write Sequencer 123 */
-	[4220] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4220  - Write Sequencer 124 */
-	[4221] = { 0x00FF, 0x00FF, 0x0000 }, /* R4221  - Write Sequencer 125 */
-	[4222] = { 0x070F, 0x070F, 0x0000 }, /* R4222  - Write Sequencer 126 */
-	[4223] = { 0x010F, 0x010F, 0x0000 }, /* R4223  - Write Sequencer 127 */
-	[4224] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4224  - Write Sequencer 128 */
-	[4225] = { 0x00FF, 0x00FF, 0x0000 }, /* R4225  - Write Sequencer 129 */
-	[4226] = { 0x070F, 0x070F, 0x0000 }, /* R4226  - Write Sequencer 130 */
-	[4227] = { 0x010F, 0x010F, 0x0000 }, /* R4227  - Write Sequencer 131 */
-	[4228] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4228  - Write Sequencer 132 */
-	[4229] = { 0x00FF, 0x00FF, 0x0000 }, /* R4229  - Write Sequencer 133 */
-	[4230] = { 0x070F, 0x070F, 0x0000 }, /* R4230  - Write Sequencer 134 */
-	[4231] = { 0x010F, 0x010F, 0x0000 }, /* R4231  - Write Sequencer 135 */
-	[4232] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4232  - Write Sequencer 136 */
-	[4233] = { 0x00FF, 0x00FF, 0x0000 }, /* R4233  - Write Sequencer 137 */
-	[4234] = { 0x070F, 0x070F, 0x0000 }, /* R4234  - Write Sequencer 138 */
-	[4235] = { 0x010F, 0x010F, 0x0000 }, /* R4235  - Write Sequencer 139 */
-	[4236] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4236  - Write Sequencer 140 */
-	[4237] = { 0x00FF, 0x00FF, 0x0000 }, /* R4237  - Write Sequencer 141 */
-	[4238] = { 0x070F, 0x070F, 0x0000 }, /* R4238  - Write Sequencer 142 */
-	[4239] = { 0x010F, 0x010F, 0x0000 }, /* R4239  - Write Sequencer 143 */
-	[4240] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4240  - Write Sequencer 144 */
-	[4241] = { 0x00FF, 0x00FF, 0x0000 }, /* R4241  - Write Sequencer 145 */
-	[4242] = { 0x070F, 0x070F, 0x0000 }, /* R4242  - Write Sequencer 146 */
-	[4243] = { 0x010F, 0x010F, 0x0000 }, /* R4243  - Write Sequencer 147 */
-	[4244] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4244  - Write Sequencer 148 */
-	[4245] = { 0x00FF, 0x00FF, 0x0000 }, /* R4245  - Write Sequencer 149 */
-	[4246] = { 0x070F, 0x070F, 0x0000 }, /* R4246  - Write Sequencer 150 */
-	[4247] = { 0x010F, 0x010F, 0x0000 }, /* R4247  - Write Sequencer 151 */
-	[4248] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4248  - Write Sequencer 152 */
-	[4249] = { 0x00FF, 0x00FF, 0x0000 }, /* R4249  - Write Sequencer 153 */
-	[4250] = { 0x070F, 0x070F, 0x0000 }, /* R4250  - Write Sequencer 154 */
-	[4251] = { 0x010F, 0x010F, 0x0000 }, /* R4251  - Write Sequencer 155 */
-	[4252] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4252  - Write Sequencer 156 */
-	[4253] = { 0x00FF, 0x00FF, 0x0000 }, /* R4253  - Write Sequencer 157 */
-	[4254] = { 0x070F, 0x070F, 0x0000 }, /* R4254  - Write Sequencer 158 */
-	[4255] = { 0x010F, 0x010F, 0x0000 }, /* R4255  - Write Sequencer 159 */
-	[4256] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4256  - Write Sequencer 160 */
-	[4257] = { 0x00FF, 0x00FF, 0x0000 }, /* R4257  - Write Sequencer 161 */
-	[4258] = { 0x070F, 0x070F, 0x0000 }, /* R4258  - Write Sequencer 162 */
-	[4259] = { 0x010F, 0x010F, 0x0000 }, /* R4259  - Write Sequencer 163 */
-	[4260] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4260  - Write Sequencer 164 */
-	[4261] = { 0x00FF, 0x00FF, 0x0000 }, /* R4261  - Write Sequencer 165 */
-	[4262] = { 0x070F, 0x070F, 0x0000 }, /* R4262  - Write Sequencer 166 */
-	[4263] = { 0x010F, 0x010F, 0x0000 }, /* R4263  - Write Sequencer 167 */
-	[4264] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4264  - Write Sequencer 168 */
-	[4265] = { 0x00FF, 0x00FF, 0x0000 }, /* R4265  - Write Sequencer 169 */
-	[4266] = { 0x070F, 0x070F, 0x0000 }, /* R4266  - Write Sequencer 170 */
-	[4267] = { 0x010F, 0x010F, 0x0000 }, /* R4267  - Write Sequencer 171 */
-	[4268] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4268  - Write Sequencer 172 */
-	[4269] = { 0x00FF, 0x00FF, 0x0000 }, /* R4269  - Write Sequencer 173 */
-	[4270] = { 0x070F, 0x070F, 0x0000 }, /* R4270  - Write Sequencer 174 */
-	[4271] = { 0x010F, 0x010F, 0x0000 }, /* R4271  - Write Sequencer 175 */
-	[4272] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4272  - Write Sequencer 176 */
-	[4273] = { 0x00FF, 0x00FF, 0x0000 }, /* R4273  - Write Sequencer 177 */
-	[4274] = { 0x070F, 0x070F, 0x0000 }, /* R4274  - Write Sequencer 178 */
-	[4275] = { 0x010F, 0x010F, 0x0000 }, /* R4275  - Write Sequencer 179 */
-	[4276] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4276  - Write Sequencer 180 */
-	[4277] = { 0x00FF, 0x00FF, 0x0000 }, /* R4277  - Write Sequencer 181 */
-	[4278] = { 0x070F, 0x070F, 0x0000 }, /* R4278  - Write Sequencer 182 */
-	[4279] = { 0x010F, 0x010F, 0x0000 }, /* R4279  - Write Sequencer 183 */
-	[4280] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4280  - Write Sequencer 184 */
-	[4281] = { 0x00FF, 0x00FF, 0x0000 }, /* R4281  - Write Sequencer 185 */
-	[4282] = { 0x070F, 0x070F, 0x0000 }, /* R4282  - Write Sequencer 186 */
-	[4283] = { 0x010F, 0x010F, 0x0000 }, /* R4283  - Write Sequencer 187 */
-	[4284] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4284  - Write Sequencer 188 */
-	[4285] = { 0x00FF, 0x00FF, 0x0000 }, /* R4285  - Write Sequencer 189 */
-	[4286] = { 0x070F, 0x070F, 0x0000 }, /* R4286  - Write Sequencer 190 */
-	[4287] = { 0x010F, 0x010F, 0x0000 }, /* R4287  - Write Sequencer 191 */
-	[4288] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4288  - Write Sequencer 192 */
-	[4289] = { 0x00FF, 0x00FF, 0x0000 }, /* R4289  - Write Sequencer 193 */
-	[4290] = { 0x070F, 0x070F, 0x0000 }, /* R4290  - Write Sequencer 194 */
-	[4291] = { 0x010F, 0x010F, 0x0000 }, /* R4291  - Write Sequencer 195 */
-	[4292] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4292  - Write Sequencer 196 */
-	[4293] = { 0x00FF, 0x00FF, 0x0000 }, /* R4293  - Write Sequencer 197 */
-	[4294] = { 0x070F, 0x070F, 0x0000 }, /* R4294  - Write Sequencer 198 */
-	[4295] = { 0x010F, 0x010F, 0x0000 }, /* R4295  - Write Sequencer 199 */
-	[4296] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4296  - Write Sequencer 200 */
-	[4297] = { 0x00FF, 0x00FF, 0x0000 }, /* R4297  - Write Sequencer 201 */
-	[4298] = { 0x070F, 0x070F, 0x0000 }, /* R4298  - Write Sequencer 202 */
-	[4299] = { 0x010F, 0x010F, 0x0000 }, /* R4299  - Write Sequencer 203 */
-	[4300] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4300  - Write Sequencer 204 */
-	[4301] = { 0x00FF, 0x00FF, 0x0000 }, /* R4301  - Write Sequencer 205 */
-	[4302] = { 0x070F, 0x070F, 0x0000 }, /* R4302  - Write Sequencer 206 */
-	[4303] = { 0x010F, 0x010F, 0x0000 }, /* R4303  - Write Sequencer 207 */
-	[4304] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4304  - Write Sequencer 208 */
-	[4305] = { 0x00FF, 0x00FF, 0x0000 }, /* R4305  - Write Sequencer 209 */
-	[4306] = { 0x070F, 0x070F, 0x0000 }, /* R4306  - Write Sequencer 210 */
-	[4307] = { 0x010F, 0x010F, 0x0000 }, /* R4307  - Write Sequencer 211 */
-	[4308] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4308  - Write Sequencer 212 */
-	[4309] = { 0x00FF, 0x00FF, 0x0000 }, /* R4309  - Write Sequencer 213 */
-	[4310] = { 0x070F, 0x070F, 0x0000 }, /* R4310  - Write Sequencer 214 */
-	[4311] = { 0x010F, 0x010F, 0x0000 }, /* R4311  - Write Sequencer 215 */
-	[4312] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4312  - Write Sequencer 216 */
-	[4313] = { 0x00FF, 0x00FF, 0x0000 }, /* R4313  - Write Sequencer 217 */
-	[4314] = { 0x070F, 0x070F, 0x0000 }, /* R4314  - Write Sequencer 218 */
-	[4315] = { 0x010F, 0x010F, 0x0000 }, /* R4315  - Write Sequencer 219 */
-	[4316] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4316  - Write Sequencer 220 */
-	[4317] = { 0x00FF, 0x00FF, 0x0000 }, /* R4317  - Write Sequencer 221 */
-	[4318] = { 0x070F, 0x070F, 0x0000 }, /* R4318  - Write Sequencer 222 */
-	[4319] = { 0x010F, 0x010F, 0x0000 }, /* R4319  - Write Sequencer 223 */
-	[4320] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4320  - Write Sequencer 224 */
-	[4321] = { 0x00FF, 0x00FF, 0x0000 }, /* R4321  - Write Sequencer 225 */
-	[4322] = { 0x070F, 0x070F, 0x0000 }, /* R4322  - Write Sequencer 226 */
-	[4323] = { 0x010F, 0x010F, 0x0000 }, /* R4323  - Write Sequencer 227 */
-	[4324] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4324  - Write Sequencer 228 */
-	[4325] = { 0x00FF, 0x00FF, 0x0000 }, /* R4325  - Write Sequencer 229 */
-	[4326] = { 0x070F, 0x070F, 0x0000 }, /* R4326  - Write Sequencer 230 */
-	[4327] = { 0x010F, 0x010F, 0x0000 }, /* R4327  - Write Sequencer 231 */
-	[4328] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4328  - Write Sequencer 232 */
-	[4329] = { 0x00FF, 0x00FF, 0x0000 }, /* R4329  - Write Sequencer 233 */
-	[4330] = { 0x070F, 0x070F, 0x0000 }, /* R4330  - Write Sequencer 234 */
-	[4331] = { 0x010F, 0x010F, 0x0000 }, /* R4331  - Write Sequencer 235 */
-	[4332] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4332  - Write Sequencer 236 */
-	[4333] = { 0x00FF, 0x00FF, 0x0000 }, /* R4333  - Write Sequencer 237 */
-	[4334] = { 0x070F, 0x070F, 0x0000 }, /* R4334  - Write Sequencer 238 */
-	[4335] = { 0x010F, 0x010F, 0x0000 }, /* R4335  - Write Sequencer 239 */
-	[4336] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4336  - Write Sequencer 240 */
-	[4337] = { 0x00FF, 0x00FF, 0x0000 }, /* R4337  - Write Sequencer 241 */
-	[4338] = { 0x070F, 0x070F, 0x0000 }, /* R4338  - Write Sequencer 242 */
-	[4339] = { 0x010F, 0x010F, 0x0000 }, /* R4339  - Write Sequencer 243 */
-	[4340] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4340  - Write Sequencer 244 */
-	[4341] = { 0x00FF, 0x00FF, 0x0000 }, /* R4341  - Write Sequencer 245 */
-	[4342] = { 0x070F, 0x070F, 0x0000 }, /* R4342  - Write Sequencer 246 */
-	[4343] = { 0x010F, 0x010F, 0x0000 }, /* R4343  - Write Sequencer 247 */
-	[4344] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4344  - Write Sequencer 248 */
-	[4345] = { 0x00FF, 0x00FF, 0x0000 }, /* R4345  - Write Sequencer 249 */
-	[4346] = { 0x070F, 0x070F, 0x0000 }, /* R4346  - Write Sequencer 250 */
-	[4347] = { 0x010F, 0x010F, 0x0000 }, /* R4347  - Write Sequencer 251 */
-	[4348] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4348  - Write Sequencer 252 */
-	[4349] = { 0x00FF, 0x00FF, 0x0000 }, /* R4349  - Write Sequencer 253 */
-	[4350] = { 0x070F, 0x070F, 0x0000 }, /* R4350  - Write Sequencer 254 */
-	[4351] = { 0x010F, 0x010F, 0x0000 }, /* R4351  - Write Sequencer 255 */
-	[4352] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4352  - Write Sequencer 256 */
-	[4353] = { 0x00FF, 0x00FF, 0x0000 }, /* R4353  - Write Sequencer 257 */
-	[4354] = { 0x070F, 0x070F, 0x0000 }, /* R4354  - Write Sequencer 258 */
-	[4355] = { 0x010F, 0x010F, 0x0000 }, /* R4355  - Write Sequencer 259 */
-	[4356] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4356  - Write Sequencer 260 */
-	[4357] = { 0x00FF, 0x00FF, 0x0000 }, /* R4357  - Write Sequencer 261 */
-	[4358] = { 0x070F, 0x070F, 0x0000 }, /* R4358  - Write Sequencer 262 */
-	[4359] = { 0x010F, 0x010F, 0x0000 }, /* R4359  - Write Sequencer 263 */
-	[4360] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4360  - Write Sequencer 264 */
-	[4361] = { 0x00FF, 0x00FF, 0x0000 }, /* R4361  - Write Sequencer 265 */
-	[4362] = { 0x070F, 0x070F, 0x0000 }, /* R4362  - Write Sequencer 266 */
-	[4363] = { 0x010F, 0x010F, 0x0000 }, /* R4363  - Write Sequencer 267 */
-	[4364] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4364  - Write Sequencer 268 */
-	[4365] = { 0x00FF, 0x00FF, 0x0000 }, /* R4365  - Write Sequencer 269 */
-	[4366] = { 0x070F, 0x070F, 0x0000 }, /* R4366  - Write Sequencer 270 */
-	[4367] = { 0x010F, 0x010F, 0x0000 }, /* R4367  - Write Sequencer 271 */
-	[4368] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4368  - Write Sequencer 272 */
-	[4369] = { 0x00FF, 0x00FF, 0x0000 }, /* R4369  - Write Sequencer 273 */
-	[4370] = { 0x070F, 0x070F, 0x0000 }, /* R4370  - Write Sequencer 274 */
-	[4371] = { 0x010F, 0x010F, 0x0000 }, /* R4371  - Write Sequencer 275 */
-	[4372] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4372  - Write Sequencer 276 */
-	[4373] = { 0x00FF, 0x00FF, 0x0000 }, /* R4373  - Write Sequencer 277 */
-	[4374] = { 0x070F, 0x070F, 0x0000 }, /* R4374  - Write Sequencer 278 */
-	[4375] = { 0x010F, 0x010F, 0x0000 }, /* R4375  - Write Sequencer 279 */
-	[4376] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4376  - Write Sequencer 280 */
-	[4377] = { 0x00FF, 0x00FF, 0x0000 }, /* R4377  - Write Sequencer 281 */
-	[4378] = { 0x070F, 0x070F, 0x0000 }, /* R4378  - Write Sequencer 282 */
-	[4379] = { 0x010F, 0x010F, 0x0000 }, /* R4379  - Write Sequencer 283 */
-	[4380] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4380  - Write Sequencer 284 */
-	[4381] = { 0x00FF, 0x00FF, 0x0000 }, /* R4381  - Write Sequencer 285 */
-	[4382] = { 0x070F, 0x070F, 0x0000 }, /* R4382  - Write Sequencer 286 */
-	[4383] = { 0x010F, 0x010F, 0x0000 }, /* R4383  - Write Sequencer 287 */
-	[4384] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4384  - Write Sequencer 288 */
-	[4385] = { 0x00FF, 0x00FF, 0x0000 }, /* R4385  - Write Sequencer 289 */
-	[4386] = { 0x070F, 0x070F, 0x0000 }, /* R4386  - Write Sequencer 290 */
-	[4387] = { 0x010F, 0x010F, 0x0000 }, /* R4387  - Write Sequencer 291 */
-	[4388] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4388  - Write Sequencer 292 */
-	[4389] = { 0x00FF, 0x00FF, 0x0000 }, /* R4389  - Write Sequencer 293 */
-	[4390] = { 0x070F, 0x070F, 0x0000 }, /* R4390  - Write Sequencer 294 */
-	[4391] = { 0x010F, 0x010F, 0x0000 }, /* R4391  - Write Sequencer 295 */
-	[4392] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4392  - Write Sequencer 296 */
-	[4393] = { 0x00FF, 0x00FF, 0x0000 }, /* R4393  - Write Sequencer 297 */
-	[4394] = { 0x070F, 0x070F, 0x0000 }, /* R4394  - Write Sequencer 298 */
-	[4395] = { 0x010F, 0x010F, 0x0000 }, /* R4395  - Write Sequencer 299 */
-	[4396] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4396  - Write Sequencer 300 */
-	[4397] = { 0x00FF, 0x00FF, 0x0000 }, /* R4397  - Write Sequencer 301 */
-	[4398] = { 0x070F, 0x070F, 0x0000 }, /* R4398  - Write Sequencer 302 */
-	[4399] = { 0x010F, 0x010F, 0x0000 }, /* R4399  - Write Sequencer 303 */
-	[4400] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4400  - Write Sequencer 304 */
-	[4401] = { 0x00FF, 0x00FF, 0x0000 }, /* R4401  - Write Sequencer 305 */
-	[4402] = { 0x070F, 0x070F, 0x0000 }, /* R4402  - Write Sequencer 306 */
-	[4403] = { 0x010F, 0x010F, 0x0000 }, /* R4403  - Write Sequencer 307 */
-	[4404] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4404  - Write Sequencer 308 */
-	[4405] = { 0x00FF, 0x00FF, 0x0000 }, /* R4405  - Write Sequencer 309 */
-	[4406] = { 0x070F, 0x070F, 0x0000 }, /* R4406  - Write Sequencer 310 */
-	[4407] = { 0x010F, 0x010F, 0x0000 }, /* R4407  - Write Sequencer 311 */
-	[4408] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4408  - Write Sequencer 312 */
-	[4409] = { 0x00FF, 0x00FF, 0x0000 }, /* R4409  - Write Sequencer 313 */
-	[4410] = { 0x070F, 0x070F, 0x0000 }, /* R4410  - Write Sequencer 314 */
-	[4411] = { 0x010F, 0x010F, 0x0000 }, /* R4411  - Write Sequencer 315 */
-	[4412] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4412  - Write Sequencer 316 */
-	[4413] = { 0x00FF, 0x00FF, 0x0000 }, /* R4413  - Write Sequencer 317 */
-	[4414] = { 0x070F, 0x070F, 0x0000 }, /* R4414  - Write Sequencer 318 */
-	[4415] = { 0x010F, 0x010F, 0x0000 }, /* R4415  - Write Sequencer 319 */
-	[4416] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4416  - Write Sequencer 320 */
-	[4417] = { 0x00FF, 0x00FF, 0x0000 }, /* R4417  - Write Sequencer 321 */
-	[4418] = { 0x070F, 0x070F, 0x0000 }, /* R4418  - Write Sequencer 322 */
-	[4419] = { 0x010F, 0x010F, 0x0000 }, /* R4419  - Write Sequencer 323 */
-	[4420] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4420  - Write Sequencer 324 */
-	[4421] = { 0x00FF, 0x00FF, 0x0000 }, /* R4421  - Write Sequencer 325 */
-	[4422] = { 0x070F, 0x070F, 0x0000 }, /* R4422  - Write Sequencer 326 */
-	[4423] = { 0x010F, 0x010F, 0x0000 }, /* R4423  - Write Sequencer 327 */
-	[4424] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4424  - Write Sequencer 328 */
-	[4425] = { 0x00FF, 0x00FF, 0x0000 }, /* R4425  - Write Sequencer 329 */
-	[4426] = { 0x070F, 0x070F, 0x0000 }, /* R4426  - Write Sequencer 330 */
-	[4427] = { 0x010F, 0x010F, 0x0000 }, /* R4427  - Write Sequencer 331 */
-	[4428] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4428  - Write Sequencer 332 */
-	[4429] = { 0x00FF, 0x00FF, 0x0000 }, /* R4429  - Write Sequencer 333 */
-	[4430] = { 0x070F, 0x070F, 0x0000 }, /* R4430  - Write Sequencer 334 */
-	[4431] = { 0x010F, 0x010F, 0x0000 }, /* R4431  - Write Sequencer 335 */
-	[4432] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4432  - Write Sequencer 336 */
-	[4433] = { 0x00FF, 0x00FF, 0x0000 }, /* R4433  - Write Sequencer 337 */
-	[4434] = { 0x070F, 0x070F, 0x0000 }, /* R4434  - Write Sequencer 338 */
-	[4435] = { 0x010F, 0x010F, 0x0000 }, /* R4435  - Write Sequencer 339 */
-	[4436] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4436  - Write Sequencer 340 */
-	[4437] = { 0x00FF, 0x00FF, 0x0000 }, /* R4437  - Write Sequencer 341 */
-	[4438] = { 0x070F, 0x070F, 0x0000 }, /* R4438  - Write Sequencer 342 */
-	[4439] = { 0x010F, 0x010F, 0x0000 }, /* R4439  - Write Sequencer 343 */
-	[4440] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4440  - Write Sequencer 344 */
-	[4441] = { 0x00FF, 0x00FF, 0x0000 }, /* R4441  - Write Sequencer 345 */
-	[4442] = { 0x070F, 0x070F, 0x0000 }, /* R4442  - Write Sequencer 346 */
-	[4443] = { 0x010F, 0x010F, 0x0000 }, /* R4443  - Write Sequencer 347 */
-	[4444] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4444  - Write Sequencer 348 */
-	[4445] = { 0x00FF, 0x00FF, 0x0000 }, /* R4445  - Write Sequencer 349 */
-	[4446] = { 0x070F, 0x070F, 0x0000 }, /* R4446  - Write Sequencer 350 */
-	[4447] = { 0x010F, 0x010F, 0x0000 }, /* R4447  - Write Sequencer 351 */
-	[4448] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4448  - Write Sequencer 352 */
-	[4449] = { 0x00FF, 0x00FF, 0x0000 }, /* R4449  - Write Sequencer 353 */
-	[4450] = { 0x070F, 0x070F, 0x0000 }, /* R4450  - Write Sequencer 354 */
-	[4451] = { 0x010F, 0x010F, 0x0000 }, /* R4451  - Write Sequencer 355 */
-	[4452] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4452  - Write Sequencer 356 */
-	[4453] = { 0x00FF, 0x00FF, 0x0000 }, /* R4453  - Write Sequencer 357 */
-	[4454] = { 0x070F, 0x070F, 0x0000 }, /* R4454  - Write Sequencer 358 */
-	[4455] = { 0x010F, 0x010F, 0x0000 }, /* R4455  - Write Sequencer 359 */
-	[4456] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4456  - Write Sequencer 360 */
-	[4457] = { 0x00FF, 0x00FF, 0x0000 }, /* R4457  - Write Sequencer 361 */
-	[4458] = { 0x070F, 0x070F, 0x0000 }, /* R4458  - Write Sequencer 362 */
-	[4459] = { 0x010F, 0x010F, 0x0000 }, /* R4459  - Write Sequencer 363 */
-	[4460] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4460  - Write Sequencer 364 */
-	[4461] = { 0x00FF, 0x00FF, 0x0000 }, /* R4461  - Write Sequencer 365 */
-	[4462] = { 0x070F, 0x070F, 0x0000 }, /* R4462  - Write Sequencer 366 */
-	[4463] = { 0x010F, 0x010F, 0x0000 }, /* R4463  - Write Sequencer 367 */
-	[4464] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4464  - Write Sequencer 368 */
-	[4465] = { 0x00FF, 0x00FF, 0x0000 }, /* R4465  - Write Sequencer 369 */
-	[4466] = { 0x070F, 0x070F, 0x0000 }, /* R4466  - Write Sequencer 370 */
-	[4467] = { 0x010F, 0x010F, 0x0000 }, /* R4467  - Write Sequencer 371 */
-	[4468] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4468  - Write Sequencer 372 */
-	[4469] = { 0x00FF, 0x00FF, 0x0000 }, /* R4469  - Write Sequencer 373 */
-	[4470] = { 0x070F, 0x070F, 0x0000 }, /* R4470  - Write Sequencer 374 */
-	[4471] = { 0x010F, 0x010F, 0x0000 }, /* R4471  - Write Sequencer 375 */
-	[4472] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4472  - Write Sequencer 376 */
-	[4473] = { 0x00FF, 0x00FF, 0x0000 }, /* R4473  - Write Sequencer 377 */
-	[4474] = { 0x070F, 0x070F, 0x0000 }, /* R4474  - Write Sequencer 378 */
-	[4475] = { 0x010F, 0x010F, 0x0000 }, /* R4475  - Write Sequencer 379 */
-	[4476] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4476  - Write Sequencer 380 */
-	[4477] = { 0x00FF, 0x00FF, 0x0000 }, /* R4477  - Write Sequencer 381 */
-	[4478] = { 0x070F, 0x070F, 0x0000 }, /* R4478  - Write Sequencer 382 */
-	[4479] = { 0x010F, 0x010F, 0x0000 }, /* R4479  - Write Sequencer 383 */
-	[4480] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4480  - Write Sequencer 384 */
-	[4481] = { 0x00FF, 0x00FF, 0x0000 }, /* R4481  - Write Sequencer 385 */
-	[4482] = { 0x070F, 0x070F, 0x0000 }, /* R4482  - Write Sequencer 386 */
-	[4483] = { 0x010F, 0x010F, 0x0000 }, /* R4483  - Write Sequencer 387 */
-	[4484] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4484  - Write Sequencer 388 */
-	[4485] = { 0x00FF, 0x00FF, 0x0000 }, /* R4485  - Write Sequencer 389 */
-	[4486] = { 0x070F, 0x070F, 0x0000 }, /* R4486  - Write Sequencer 390 */
-	[4487] = { 0x010F, 0x010F, 0x0000 }, /* R4487  - Write Sequencer 391 */
-	[4488] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4488  - Write Sequencer 392 */
-	[4489] = { 0x00FF, 0x00FF, 0x0000 }, /* R4489  - Write Sequencer 393 */
-	[4490] = { 0x070F, 0x070F, 0x0000 }, /* R4490  - Write Sequencer 394 */
-	[4491] = { 0x010F, 0x010F, 0x0000 }, /* R4491  - Write Sequencer 395 */
-	[4492] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4492  - Write Sequencer 396 */
-	[4493] = { 0x00FF, 0x00FF, 0x0000 }, /* R4493  - Write Sequencer 397 */
-	[4494] = { 0x070F, 0x070F, 0x0000 }, /* R4494  - Write Sequencer 398 */
-	[4495] = { 0x010F, 0x010F, 0x0000 }, /* R4495  - Write Sequencer 399 */
-	[4496] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4496  - Write Sequencer 400 */
-	[4497] = { 0x00FF, 0x00FF, 0x0000 }, /* R4497  - Write Sequencer 401 */
-	[4498] = { 0x070F, 0x070F, 0x0000 }, /* R4498  - Write Sequencer 402 */
-	[4499] = { 0x010F, 0x010F, 0x0000 }, /* R4499  - Write Sequencer 403 */
-	[4500] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4500  - Write Sequencer 404 */
-	[4501] = { 0x00FF, 0x00FF, 0x0000 }, /* R4501  - Write Sequencer 405 */
-	[4502] = { 0x070F, 0x070F, 0x0000 }, /* R4502  - Write Sequencer 406 */
-	[4503] = { 0x010F, 0x010F, 0x0000 }, /* R4503  - Write Sequencer 407 */
-	[4504] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4504  - Write Sequencer 408 */
-	[4505] = { 0x00FF, 0x00FF, 0x0000 }, /* R4505  - Write Sequencer 409 */
-	[4506] = { 0x070F, 0x070F, 0x0000 }, /* R4506  - Write Sequencer 410 */
-	[4507] = { 0x010F, 0x010F, 0x0000 }, /* R4507  - Write Sequencer 411 */
-	[4508] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4508  - Write Sequencer 412 */
-	[4509] = { 0x00FF, 0x00FF, 0x0000 }, /* R4509  - Write Sequencer 413 */
-	[4510] = { 0x070F, 0x070F, 0x0000 }, /* R4510  - Write Sequencer 414 */
-	[4511] = { 0x010F, 0x010F, 0x0000 }, /* R4511  - Write Sequencer 415 */
-	[4512] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4512  - Write Sequencer 416 */
-	[4513] = { 0x00FF, 0x00FF, 0x0000 }, /* R4513  - Write Sequencer 417 */
-	[4514] = { 0x070F, 0x070F, 0x0000 }, /* R4514  - Write Sequencer 418 */
-	[4515] = { 0x010F, 0x010F, 0x0000 }, /* R4515  - Write Sequencer 419 */
-	[4516] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4516  - Write Sequencer 420 */
-	[4517] = { 0x00FF, 0x00FF, 0x0000 }, /* R4517  - Write Sequencer 421 */
-	[4518] = { 0x070F, 0x070F, 0x0000 }, /* R4518  - Write Sequencer 422 */
-	[4519] = { 0x010F, 0x010F, 0x0000 }, /* R4519  - Write Sequencer 423 */
-	[4520] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4520  - Write Sequencer 424 */
-	[4521] = { 0x00FF, 0x00FF, 0x0000 }, /* R4521  - Write Sequencer 425 */
-	[4522] = { 0x070F, 0x070F, 0x0000 }, /* R4522  - Write Sequencer 426 */
-	[4523] = { 0x010F, 0x010F, 0x0000 }, /* R4523  - Write Sequencer 427 */
-	[4524] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4524  - Write Sequencer 428 */
-	[4525] = { 0x00FF, 0x00FF, 0x0000 }, /* R4525  - Write Sequencer 429 */
-	[4526] = { 0x070F, 0x070F, 0x0000 }, /* R4526  - Write Sequencer 430 */
-	[4527] = { 0x010F, 0x010F, 0x0000 }, /* R4527  - Write Sequencer 431 */
-	[4528] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4528  - Write Sequencer 432 */
-	[4529] = { 0x00FF, 0x00FF, 0x0000 }, /* R4529  - Write Sequencer 433 */
-	[4530] = { 0x070F, 0x070F, 0x0000 }, /* R4530  - Write Sequencer 434 */
-	[4531] = { 0x010F, 0x010F, 0x0000 }, /* R4531  - Write Sequencer 435 */
-	[4532] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4532  - Write Sequencer 436 */
-	[4533] = { 0x00FF, 0x00FF, 0x0000 }, /* R4533  - Write Sequencer 437 */
-	[4534] = { 0x070F, 0x070F, 0x0000 }, /* R4534  - Write Sequencer 438 */
-	[4535] = { 0x010F, 0x010F, 0x0000 }, /* R4535  - Write Sequencer 439 */
-	[4536] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4536  - Write Sequencer 440 */
-	[4537] = { 0x00FF, 0x00FF, 0x0000 }, /* R4537  - Write Sequencer 441 */
-	[4538] = { 0x070F, 0x070F, 0x0000 }, /* R4538  - Write Sequencer 442 */
-	[4539] = { 0x010F, 0x010F, 0x0000 }, /* R4539  - Write Sequencer 443 */
-	[4540] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4540  - Write Sequencer 444 */
-	[4541] = { 0x00FF, 0x00FF, 0x0000 }, /* R4541  - Write Sequencer 445 */
-	[4542] = { 0x070F, 0x070F, 0x0000 }, /* R4542  - Write Sequencer 446 */
-	[4543] = { 0x010F, 0x010F, 0x0000 }, /* R4543  - Write Sequencer 447 */
-	[4544] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4544  - Write Sequencer 448 */
-	[4545] = { 0x00FF, 0x00FF, 0x0000 }, /* R4545  - Write Sequencer 449 */
-	[4546] = { 0x070F, 0x070F, 0x0000 }, /* R4546  - Write Sequencer 450 */
-	[4547] = { 0x010F, 0x010F, 0x0000 }, /* R4547  - Write Sequencer 451 */
-	[4548] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4548  - Write Sequencer 452 */
-	[4549] = { 0x00FF, 0x00FF, 0x0000 }, /* R4549  - Write Sequencer 453 */
-	[4550] = { 0x070F, 0x070F, 0x0000 }, /* R4550  - Write Sequencer 454 */
-	[4551] = { 0x010F, 0x010F, 0x0000 }, /* R4551  - Write Sequencer 455 */
-	[4552] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4552  - Write Sequencer 456 */
-	[4553] = { 0x00FF, 0x00FF, 0x0000 }, /* R4553  - Write Sequencer 457 */
-	[4554] = { 0x070F, 0x070F, 0x0000 }, /* R4554  - Write Sequencer 458 */
-	[4555] = { 0x010F, 0x010F, 0x0000 }, /* R4555  - Write Sequencer 459 */
-	[4556] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4556  - Write Sequencer 460 */
-	[4557] = { 0x00FF, 0x00FF, 0x0000 }, /* R4557  - Write Sequencer 461 */
-	[4558] = { 0x070F, 0x070F, 0x0000 }, /* R4558  - Write Sequencer 462 */
-	[4559] = { 0x010F, 0x010F, 0x0000 }, /* R4559  - Write Sequencer 463 */
-	[4560] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4560  - Write Sequencer 464 */
-	[4561] = { 0x00FF, 0x00FF, 0x0000 }, /* R4561  - Write Sequencer 465 */
-	[4562] = { 0x070F, 0x070F, 0x0000 }, /* R4562  - Write Sequencer 466 */
-	[4563] = { 0x010F, 0x010F, 0x0000 }, /* R4563  - Write Sequencer 467 */
-	[4564] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4564  - Write Sequencer 468 */
-	[4565] = { 0x00FF, 0x00FF, 0x0000 }, /* R4565  - Write Sequencer 469 */
-	[4566] = { 0x070F, 0x070F, 0x0000 }, /* R4566  - Write Sequencer 470 */
-	[4567] = { 0x010F, 0x010F, 0x0000 }, /* R4567  - Write Sequencer 471 */
-	[4568] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4568  - Write Sequencer 472 */
-	[4569] = { 0x00FF, 0x00FF, 0x0000 }, /* R4569  - Write Sequencer 473 */
-	[4570] = { 0x070F, 0x070F, 0x0000 }, /* R4570  - Write Sequencer 474 */
-	[4571] = { 0x010F, 0x010F, 0x0000 }, /* R4571  - Write Sequencer 475 */
-	[4572] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4572  - Write Sequencer 476 */
-	[4573] = { 0x00FF, 0x00FF, 0x0000 }, /* R4573  - Write Sequencer 477 */
-	[4574] = { 0x070F, 0x070F, 0x0000 }, /* R4574  - Write Sequencer 478 */
-	[4575] = { 0x010F, 0x010F, 0x0000 }, /* R4575  - Write Sequencer 479 */
-	[4576] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4576  - Write Sequencer 480 */
-	[4577] = { 0x00FF, 0x00FF, 0x0000 }, /* R4577  - Write Sequencer 481 */
-	[4578] = { 0x070F, 0x070F, 0x0000 }, /* R4578  - Write Sequencer 482 */
-	[4579] = { 0x010F, 0x010F, 0x0000 }, /* R4579  - Write Sequencer 483 */
-	[4580] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4580  - Write Sequencer 484 */
-	[4581] = { 0x00FF, 0x00FF, 0x0000 }, /* R4581  - Write Sequencer 485 */
-	[4582] = { 0x070F, 0x070F, 0x0000 }, /* R4582  - Write Sequencer 486 */
-	[4583] = { 0x010F, 0x010F, 0x0000 }, /* R4583  - Write Sequencer 487 */
-	[4584] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4584  - Write Sequencer 488 */
-	[4585] = { 0x00FF, 0x00FF, 0x0000 }, /* R4585  - Write Sequencer 489 */
-	[4586] = { 0x070F, 0x070F, 0x0000 }, /* R4586  - Write Sequencer 490 */
-	[4587] = { 0x010F, 0x010F, 0x0000 }, /* R4587  - Write Sequencer 491 */
-	[4588] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4588  - Write Sequencer 492 */
-	[4589] = { 0x00FF, 0x00FF, 0x0000 }, /* R4589  - Write Sequencer 493 */
-	[4590] = { 0x070F, 0x070F, 0x0000 }, /* R4590  - Write Sequencer 494 */
-	[4591] = { 0x010F, 0x010F, 0x0000 }, /* R4591  - Write Sequencer 495 */
-	[4592] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4592  - Write Sequencer 496 */
-	[4593] = { 0x00FF, 0x00FF, 0x0000 }, /* R4593  - Write Sequencer 497 */
-	[4594] = { 0x070F, 0x070F, 0x0000 }, /* R4594  - Write Sequencer 498 */
-	[4595] = { 0x010F, 0x010F, 0x0000 }, /* R4595  - Write Sequencer 499 */
-	[4596] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4596  - Write Sequencer 500 */
-	[4597] = { 0x00FF, 0x00FF, 0x0000 }, /* R4597  - Write Sequencer 501 */
-	[4598] = { 0x070F, 0x070F, 0x0000 }, /* R4598  - Write Sequencer 502 */
-	[4599] = { 0x010F, 0x010F, 0x0000 }, /* R4599  - Write Sequencer 503 */
-	[4600] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4600  - Write Sequencer 504 */
-	[4601] = { 0x00FF, 0x00FF, 0x0000 }, /* R4601  - Write Sequencer 505 */
-	[4602] = { 0x070F, 0x070F, 0x0000 }, /* R4602  - Write Sequencer 506 */
-	[4603] = { 0x010F, 0x010F, 0x0000 }, /* R4603  - Write Sequencer 507 */
-	[4604] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4604  - Write Sequencer 508 */
-	[4605] = { 0x00FF, 0x00FF, 0x0000 }, /* R4605  - Write Sequencer 509 */
-	[4606] = { 0x070F, 0x070F, 0x0000 }, /* R4606  - Write Sequencer 510 */
-	[4607] = { 0x010F, 0x010F, 0x0000 }, /* R4607  - Write Sequencer 511 */
-	[8192] = { 0x03FF, 0x03FF, 0x0000 }, /* R8192  - DSP2 Instruction RAM 0 */
-	[9216] = { 0x003F, 0x003F, 0x0000 }, /* R9216  - DSP2 Address RAM 2 */
-	[9217] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R9217  - DSP2 Address RAM 1 */
-	[9218] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R9218  - DSP2 Address RAM 0 */
-	[12288] = { 0x00FF, 0x00FF, 0x0000 }, /* R12288 - DSP2 Data1 RAM 1 */
-	[12289] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R12289 - DSP2 Data1 RAM 0 */
-	[13312] = { 0x00FF, 0x00FF, 0x0000 }, /* R13312 - DSP2 Data2 RAM 1 */
-	[13313] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R13313 - DSP2 Data2 RAM 0 */
-	[14336] = { 0x00FF, 0x00FF, 0x0000 }, /* R14336 - DSP2 Data3 RAM 1 */
-	[14337] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R14337 - DSP2 Data3 RAM 0 */
-	[15360] = { 0x07FF, 0x07FF, 0x0000 }, /* R15360 - DSP2 Coeff RAM 0 */
-	[16384] = { 0x00FF, 0x00FF, 0x0000 }, /* R16384 - RETUNEADC_SHARED_COEFF_1 */
-	[16385] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16385 - RETUNEADC_SHARED_COEFF_0 */
-	[16386] = { 0x00FF, 0x00FF, 0x0000 }, /* R16386 - RETUNEDAC_SHARED_COEFF_1 */
-	[16387] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16387 - RETUNEDAC_SHARED_COEFF_0 */
-	[16388] = { 0x00FF, 0x00FF, 0x0000 }, /* R16388 - SOUNDSTAGE_ENABLES_1 */
-	[16389] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16389 - SOUNDSTAGE_ENABLES_0 */
-	[16896] = { 0x00FF, 0x00FF, 0x0000 }, /* R16896 - HDBASS_AI_1 */
-	[16897] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16897 - HDBASS_AI_0 */
-	[16898] = { 0x00FF, 0x00FF, 0x0000 }, /* R16898 - HDBASS_AR_1 */
-	[16899] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16899 - HDBASS_AR_0 */
-	[16900] = { 0x00FF, 0x00FF, 0x0000 }, /* R16900 - HDBASS_B_1 */
-	[16901] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16901 - HDBASS_B_0 */
-	[16902] = { 0x00FF, 0x00FF, 0x0000 }, /* R16902 - HDBASS_K_1 */
-	[16903] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16903 - HDBASS_K_0 */
-	[16904] = { 0x00FF, 0x00FF, 0x0000 }, /* R16904 - HDBASS_N1_1 */
-	[16905] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16905 - HDBASS_N1_0 */
-	[16906] = { 0x00FF, 0x00FF, 0x0000 }, /* R16906 - HDBASS_N2_1 */
-	[16907] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16907 - HDBASS_N2_0 */
-	[16908] = { 0x00FF, 0x00FF, 0x0000 }, /* R16908 - HDBASS_N3_1 */
-	[16909] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16909 - HDBASS_N3_0 */
-	[16910] = { 0x00FF, 0x00FF, 0x0000 }, /* R16910 - HDBASS_N4_1 */
-	[16911] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16911 - HDBASS_N4_0 */
-	[16912] = { 0x00FF, 0x00FF, 0x0000 }, /* R16912 - HDBASS_N5_1 */
-	[16913] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16913 - HDBASS_N5_0 */
-	[16914] = { 0x00FF, 0x00FF, 0x0000 }, /* R16914 - HDBASS_X1_1 */
-	[16915] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16915 - HDBASS_X1_0 */
-	[16916] = { 0x00FF, 0x00FF, 0x0000 }, /* R16916 - HDBASS_X2_1 */
-	[16917] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16917 - HDBASS_X2_0 */
-	[16918] = { 0x00FF, 0x00FF, 0x0000 }, /* R16918 - HDBASS_X3_1 */
-	[16919] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16919 - HDBASS_X3_0 */
-	[16920] = { 0x00FF, 0x00FF, 0x0000 }, /* R16920 - HDBASS_ATK_1 */
-	[16921] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16921 - HDBASS_ATK_0 */
-	[16922] = { 0x00FF, 0x00FF, 0x0000 }, /* R16922 - HDBASS_DCY_1 */
-	[16923] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16923 - HDBASS_DCY_0 */
-	[16924] = { 0x00FF, 0x00FF, 0x0000 }, /* R16924 - HDBASS_PG_1 */
-	[16925] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R16925 - HDBASS_PG_0 */
-	[17408] = { 0x00FF, 0x00FF, 0x0000 }, /* R17408 - HPF_C_1 */
-	[17409] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17409 - HPF_C_0 */
-	[17920] = { 0x00FF, 0x00FF, 0x0000 }, /* R17920 - ADCL_RETUNE_C1_1 */
-	[17921] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17921 - ADCL_RETUNE_C1_0 */
-	[17922] = { 0x00FF, 0x00FF, 0x0000 }, /* R17922 - ADCL_RETUNE_C2_1 */
-	[17923] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17923 - ADCL_RETUNE_C2_0 */
-	[17924] = { 0x00FF, 0x00FF, 0x0000 }, /* R17924 - ADCL_RETUNE_C3_1 */
-	[17925] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17925 - ADCL_RETUNE_C3_0 */
-	[17926] = { 0x00FF, 0x00FF, 0x0000 }, /* R17926 - ADCL_RETUNE_C4_1 */
-	[17927] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17927 - ADCL_RETUNE_C4_0 */
-	[17928] = { 0x00FF, 0x00FF, 0x0000 }, /* R17928 - ADCL_RETUNE_C5_1 */
-	[17929] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17929 - ADCL_RETUNE_C5_0 */
-	[17930] = { 0x00FF, 0x00FF, 0x0000 }, /* R17930 - ADCL_RETUNE_C6_1 */
-	[17931] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17931 - ADCL_RETUNE_C6_0 */
-	[17932] = { 0x00FF, 0x00FF, 0x0000 }, /* R17932 - ADCL_RETUNE_C7_1 */
-	[17933] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17933 - ADCL_RETUNE_C7_0 */
-	[17934] = { 0x00FF, 0x00FF, 0x0000 }, /* R17934 - ADCL_RETUNE_C8_1 */
-	[17935] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17935 - ADCL_RETUNE_C8_0 */
-	[17936] = { 0x00FF, 0x00FF, 0x0000 }, /* R17936 - ADCL_RETUNE_C9_1 */
-	[17937] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17937 - ADCL_RETUNE_C9_0 */
-	[17938] = { 0x00FF, 0x00FF, 0x0000 }, /* R17938 - ADCL_RETUNE_C10_1 */
-	[17939] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17939 - ADCL_RETUNE_C10_0 */
-	[17940] = { 0x00FF, 0x00FF, 0x0000 }, /* R17940 - ADCL_RETUNE_C11_1 */
-	[17941] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17941 - ADCL_RETUNE_C11_0 */
-	[17942] = { 0x00FF, 0x00FF, 0x0000 }, /* R17942 - ADCL_RETUNE_C12_1 */
-	[17943] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17943 - ADCL_RETUNE_C12_0 */
-	[17944] = { 0x00FF, 0x00FF, 0x0000 }, /* R17944 - ADCL_RETUNE_C13_1 */
-	[17945] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17945 - ADCL_RETUNE_C13_0 */
-	[17946] = { 0x00FF, 0x00FF, 0x0000 }, /* R17946 - ADCL_RETUNE_C14_1 */
-	[17947] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17947 - ADCL_RETUNE_C14_0 */
-	[17948] = { 0x00FF, 0x00FF, 0x0000 }, /* R17948 - ADCL_RETUNE_C15_1 */
-	[17949] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17949 - ADCL_RETUNE_C15_0 */
-	[17950] = { 0x00FF, 0x00FF, 0x0000 }, /* R17950 - ADCL_RETUNE_C16_1 */
-	[17951] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17951 - ADCL_RETUNE_C16_0 */
-	[17952] = { 0x00FF, 0x00FF, 0x0000 }, /* R17952 - ADCL_RETUNE_C17_1 */
-	[17953] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17953 - ADCL_RETUNE_C17_0 */
-	[17954] = { 0x00FF, 0x00FF, 0x0000 }, /* R17954 - ADCL_RETUNE_C18_1 */
-	[17955] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17955 - ADCL_RETUNE_C18_0 */
-	[17956] = { 0x00FF, 0x00FF, 0x0000 }, /* R17956 - ADCL_RETUNE_C19_1 */
-	[17957] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17957 - ADCL_RETUNE_C19_0 */
-	[17958] = { 0x00FF, 0x00FF, 0x0000 }, /* R17958 - ADCL_RETUNE_C20_1 */
-	[17959] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17959 - ADCL_RETUNE_C20_0 */
-	[17960] = { 0x00FF, 0x00FF, 0x0000 }, /* R17960 - ADCL_RETUNE_C21_1 */
-	[17961] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17961 - ADCL_RETUNE_C21_0 */
-	[17962] = { 0x00FF, 0x00FF, 0x0000 }, /* R17962 - ADCL_RETUNE_C22_1 */
-	[17963] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17963 - ADCL_RETUNE_C22_0 */
-	[17964] = { 0x00FF, 0x00FF, 0x0000 }, /* R17964 - ADCL_RETUNE_C23_1 */
-	[17965] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17965 - ADCL_RETUNE_C23_0 */
-	[17966] = { 0x00FF, 0x00FF, 0x0000 }, /* R17966 - ADCL_RETUNE_C24_1 */
-	[17967] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17967 - ADCL_RETUNE_C24_0 */
-	[17968] = { 0x00FF, 0x00FF, 0x0000 }, /* R17968 - ADCL_RETUNE_C25_1 */
-	[17969] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17969 - ADCL_RETUNE_C25_0 */
-	[17970] = { 0x00FF, 0x00FF, 0x0000 }, /* R17970 - ADCL_RETUNE_C26_1 */
-	[17971] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17971 - ADCL_RETUNE_C26_0 */
-	[17972] = { 0x00FF, 0x00FF, 0x0000 }, /* R17972 - ADCL_RETUNE_C27_1 */
-	[17973] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17973 - ADCL_RETUNE_C27_0 */
-	[17974] = { 0x00FF, 0x00FF, 0x0000 }, /* R17974 - ADCL_RETUNE_C28_1 */
-	[17975] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17975 - ADCL_RETUNE_C28_0 */
-	[17976] = { 0x00FF, 0x00FF, 0x0000 }, /* R17976 - ADCL_RETUNE_C29_1 */
-	[17977] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17977 - ADCL_RETUNE_C29_0 */
-	[17978] = { 0x00FF, 0x00FF, 0x0000 }, /* R17978 - ADCL_RETUNE_C30_1 */
-	[17979] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17979 - ADCL_RETUNE_C30_0 */
-	[17980] = { 0x00FF, 0x00FF, 0x0000 }, /* R17980 - ADCL_RETUNE_C31_1 */
-	[17981] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17981 - ADCL_RETUNE_C31_0 */
-	[17982] = { 0x00FF, 0x00FF, 0x0000 }, /* R17982 - ADCL_RETUNE_C32_1 */
-	[17983] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R17983 - ADCL_RETUNE_C32_0 */
-	[18432] = { 0x00FF, 0x00FF, 0x0000 }, /* R18432 - RETUNEADC_PG2_1 */
-	[18433] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18433 - RETUNEADC_PG2_0 */
-	[18434] = { 0x00FF, 0x00FF, 0x0000 }, /* R18434 - RETUNEADC_PG_1 */
-	[18435] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18435 - RETUNEADC_PG_0 */
-	[18944] = { 0x00FF, 0x00FF, 0x0000 }, /* R18944 - ADCR_RETUNE_C1_1 */
-	[18945] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18945 - ADCR_RETUNE_C1_0 */
-	[18946] = { 0x00FF, 0x00FF, 0x0000 }, /* R18946 - ADCR_RETUNE_C2_1 */
-	[18947] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18947 - ADCR_RETUNE_C2_0 */
-	[18948] = { 0x00FF, 0x00FF, 0x0000 }, /* R18948 - ADCR_RETUNE_C3_1 */
-	[18949] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18949 - ADCR_RETUNE_C3_0 */
-	[18950] = { 0x00FF, 0x00FF, 0x0000 }, /* R18950 - ADCR_RETUNE_C4_1 */
-	[18951] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18951 - ADCR_RETUNE_C4_0 */
-	[18952] = { 0x00FF, 0x00FF, 0x0000 }, /* R18952 - ADCR_RETUNE_C5_1 */
-	[18953] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18953 - ADCR_RETUNE_C5_0 */
-	[18954] = { 0x00FF, 0x00FF, 0x0000 }, /* R18954 - ADCR_RETUNE_C6_1 */
-	[18955] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18955 - ADCR_RETUNE_C6_0 */
-	[18956] = { 0x00FF, 0x00FF, 0x0000 }, /* R18956 - ADCR_RETUNE_C7_1 */
-	[18957] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18957 - ADCR_RETUNE_C7_0 */
-	[18958] = { 0x00FF, 0x00FF, 0x0000 }, /* R18958 - ADCR_RETUNE_C8_1 */
-	[18959] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18959 - ADCR_RETUNE_C8_0 */
-	[18960] = { 0x00FF, 0x00FF, 0x0000 }, /* R18960 - ADCR_RETUNE_C9_1 */
-	[18961] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18961 - ADCR_RETUNE_C9_0 */
-	[18962] = { 0x00FF, 0x00FF, 0x0000 }, /* R18962 - ADCR_RETUNE_C10_1 */
-	[18963] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18963 - ADCR_RETUNE_C10_0 */
-	[18964] = { 0x00FF, 0x00FF, 0x0000 }, /* R18964 - ADCR_RETUNE_C11_1 */
-	[18965] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18965 - ADCR_RETUNE_C11_0 */
-	[18966] = { 0x00FF, 0x00FF, 0x0000 }, /* R18966 - ADCR_RETUNE_C12_1 */
-	[18967] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18967 - ADCR_RETUNE_C12_0 */
-	[18968] = { 0x00FF, 0x00FF, 0x0000 }, /* R18968 - ADCR_RETUNE_C13_1 */
-	[18969] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18969 - ADCR_RETUNE_C13_0 */
-	[18970] = { 0x00FF, 0x00FF, 0x0000 }, /* R18970 - ADCR_RETUNE_C14_1 */
-	[18971] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18971 - ADCR_RETUNE_C14_0 */
-	[18972] = { 0x00FF, 0x00FF, 0x0000 }, /* R18972 - ADCR_RETUNE_C15_1 */
-	[18973] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18973 - ADCR_RETUNE_C15_0 */
-	[18974] = { 0x00FF, 0x00FF, 0x0000 }, /* R18974 - ADCR_RETUNE_C16_1 */
-	[18975] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18975 - ADCR_RETUNE_C16_0 */
-	[18976] = { 0x00FF, 0x00FF, 0x0000 }, /* R18976 - ADCR_RETUNE_C17_1 */
-	[18977] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18977 - ADCR_RETUNE_C17_0 */
-	[18978] = { 0x00FF, 0x00FF, 0x0000 }, /* R18978 - ADCR_RETUNE_C18_1 */
-	[18979] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18979 - ADCR_RETUNE_C18_0 */
-	[18980] = { 0x00FF, 0x00FF, 0x0000 }, /* R18980 - ADCR_RETUNE_C19_1 */
-	[18981] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18981 - ADCR_RETUNE_C19_0 */
-	[18982] = { 0x00FF, 0x00FF, 0x0000 }, /* R18982 - ADCR_RETUNE_C20_1 */
-	[18983] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18983 - ADCR_RETUNE_C20_0 */
-	[18984] = { 0x00FF, 0x00FF, 0x0000 }, /* R18984 - ADCR_RETUNE_C21_1 */
-	[18985] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18985 - ADCR_RETUNE_C21_0 */
-	[18986] = { 0x00FF, 0x00FF, 0x0000 }, /* R18986 - ADCR_RETUNE_C22_1 */
-	[18987] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18987 - ADCR_RETUNE_C22_0 */
-	[18988] = { 0x00FF, 0x00FF, 0x0000 }, /* R18988 - ADCR_RETUNE_C23_1 */
-	[18989] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18989 - ADCR_RETUNE_C23_0 */
-	[18990] = { 0x00FF, 0x00FF, 0x0000 }, /* R18990 - ADCR_RETUNE_C24_1 */
-	[18991] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18991 - ADCR_RETUNE_C24_0 */
-	[18992] = { 0x00FF, 0x00FF, 0x0000 }, /* R18992 - ADCR_RETUNE_C25_1 */
-	[18993] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18993 - ADCR_RETUNE_C25_0 */
-	[18994] = { 0x00FF, 0x00FF, 0x0000 }, /* R18994 - ADCR_RETUNE_C26_1 */
-	[18995] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18995 - ADCR_RETUNE_C26_0 */
-	[18996] = { 0x00FF, 0x00FF, 0x0000 }, /* R18996 - ADCR_RETUNE_C27_1 */
-	[18997] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18997 - ADCR_RETUNE_C27_0 */
-	[18998] = { 0x00FF, 0x00FF, 0x0000 }, /* R18998 - ADCR_RETUNE_C28_1 */
-	[18999] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R18999 - ADCR_RETUNE_C28_0 */
-	[19000] = { 0x00FF, 0x00FF, 0x0000 }, /* R19000 - ADCR_RETUNE_C29_1 */
-	[19001] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19001 - ADCR_RETUNE_C29_0 */
-	[19002] = { 0x00FF, 0x00FF, 0x0000 }, /* R19002 - ADCR_RETUNE_C30_1 */
-	[19003] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19003 - ADCR_RETUNE_C30_0 */
-	[19004] = { 0x00FF, 0x00FF, 0x0000 }, /* R19004 - ADCR_RETUNE_C31_1 */
-	[19005] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19005 - ADCR_RETUNE_C31_0 */
-	[19006] = { 0x00FF, 0x00FF, 0x0000 }, /* R19006 - ADCR_RETUNE_C32_1 */
-	[19007] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19007 - ADCR_RETUNE_C32_0 */
-	[19456] = { 0x00FF, 0x00FF, 0x0000 }, /* R19456 - DACL_RETUNE_C1_1 */
-	[19457] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19457 - DACL_RETUNE_C1_0 */
-	[19458] = { 0x00FF, 0x00FF, 0x0000 }, /* R19458 - DACL_RETUNE_C2_1 */
-	[19459] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19459 - DACL_RETUNE_C2_0 */
-	[19460] = { 0x00FF, 0x00FF, 0x0000 }, /* R19460 - DACL_RETUNE_C3_1 */
-	[19461] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19461 - DACL_RETUNE_C3_0 */
-	[19462] = { 0x00FF, 0x00FF, 0x0000 }, /* R19462 - DACL_RETUNE_C4_1 */
-	[19463] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19463 - DACL_RETUNE_C4_0 */
-	[19464] = { 0x00FF, 0x00FF, 0x0000 }, /* R19464 - DACL_RETUNE_C5_1 */
-	[19465] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19465 - DACL_RETUNE_C5_0 */
-	[19466] = { 0x00FF, 0x00FF, 0x0000 }, /* R19466 - DACL_RETUNE_C6_1 */
-	[19467] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19467 - DACL_RETUNE_C6_0 */
-	[19468] = { 0x00FF, 0x00FF, 0x0000 }, /* R19468 - DACL_RETUNE_C7_1 */
-	[19469] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19469 - DACL_RETUNE_C7_0 */
-	[19470] = { 0x00FF, 0x00FF, 0x0000 }, /* R19470 - DACL_RETUNE_C8_1 */
-	[19471] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19471 - DACL_RETUNE_C8_0 */
-	[19472] = { 0x00FF, 0x00FF, 0x0000 }, /* R19472 - DACL_RETUNE_C9_1 */
-	[19473] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19473 - DACL_RETUNE_C9_0 */
-	[19474] = { 0x00FF, 0x00FF, 0x0000 }, /* R19474 - DACL_RETUNE_C10_1 */
-	[19475] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19475 - DACL_RETUNE_C10_0 */
-	[19476] = { 0x00FF, 0x00FF, 0x0000 }, /* R19476 - DACL_RETUNE_C11_1 */
-	[19477] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19477 - DACL_RETUNE_C11_0 */
-	[19478] = { 0x00FF, 0x00FF, 0x0000 }, /* R19478 - DACL_RETUNE_C12_1 */
-	[19479] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19479 - DACL_RETUNE_C12_0 */
-	[19480] = { 0x00FF, 0x00FF, 0x0000 }, /* R19480 - DACL_RETUNE_C13_1 */
-	[19481] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19481 - DACL_RETUNE_C13_0 */
-	[19482] = { 0x00FF, 0x00FF, 0x0000 }, /* R19482 - DACL_RETUNE_C14_1 */
-	[19483] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19483 - DACL_RETUNE_C14_0 */
-	[19484] = { 0x00FF, 0x00FF, 0x0000 }, /* R19484 - DACL_RETUNE_C15_1 */
-	[19485] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19485 - DACL_RETUNE_C15_0 */
-	[19486] = { 0x00FF, 0x00FF, 0x0000 }, /* R19486 - DACL_RETUNE_C16_1 */
-	[19487] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19487 - DACL_RETUNE_C16_0 */
-	[19488] = { 0x00FF, 0x00FF, 0x0000 }, /* R19488 - DACL_RETUNE_C17_1 */
-	[19489] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19489 - DACL_RETUNE_C17_0 */
-	[19490] = { 0x00FF, 0x00FF, 0x0000 }, /* R19490 - DACL_RETUNE_C18_1 */
-	[19491] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19491 - DACL_RETUNE_C18_0 */
-	[19492] = { 0x00FF, 0x00FF, 0x0000 }, /* R19492 - DACL_RETUNE_C19_1 */
-	[19493] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19493 - DACL_RETUNE_C19_0 */
-	[19494] = { 0x00FF, 0x00FF, 0x0000 }, /* R19494 - DACL_RETUNE_C20_1 */
-	[19495] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19495 - DACL_RETUNE_C20_0 */
-	[19496] = { 0x00FF, 0x00FF, 0x0000 }, /* R19496 - DACL_RETUNE_C21_1 */
-	[19497] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19497 - DACL_RETUNE_C21_0 */
-	[19498] = { 0x00FF, 0x00FF, 0x0000 }, /* R19498 - DACL_RETUNE_C22_1 */
-	[19499] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19499 - DACL_RETUNE_C22_0 */
-	[19500] = { 0x00FF, 0x00FF, 0x0000 }, /* R19500 - DACL_RETUNE_C23_1 */
-	[19501] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19501 - DACL_RETUNE_C23_0 */
-	[19502] = { 0x00FF, 0x00FF, 0x0000 }, /* R19502 - DACL_RETUNE_C24_1 */
-	[19503] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19503 - DACL_RETUNE_C24_0 */
-	[19504] = { 0x00FF, 0x00FF, 0x0000 }, /* R19504 - DACL_RETUNE_C25_1 */
-	[19505] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19505 - DACL_RETUNE_C25_0 */
-	[19506] = { 0x00FF, 0x00FF, 0x0000 }, /* R19506 - DACL_RETUNE_C26_1 */
-	[19507] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19507 - DACL_RETUNE_C26_0 */
-	[19508] = { 0x00FF, 0x00FF, 0x0000 }, /* R19508 - DACL_RETUNE_C27_1 */
-	[19509] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19509 - DACL_RETUNE_C27_0 */
-	[19510] = { 0x00FF, 0x00FF, 0x0000 }, /* R19510 - DACL_RETUNE_C28_1 */
-	[19511] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19511 - DACL_RETUNE_C28_0 */
-	[19512] = { 0x00FF, 0x00FF, 0x0000 }, /* R19512 - DACL_RETUNE_C29_1 */
-	[19513] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19513 - DACL_RETUNE_C29_0 */
-	[19514] = { 0x00FF, 0x00FF, 0x0000 }, /* R19514 - DACL_RETUNE_C30_1 */
-	[19515] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19515 - DACL_RETUNE_C30_0 */
-	[19516] = { 0x00FF, 0x00FF, 0x0000 }, /* R19516 - DACL_RETUNE_C31_1 */
-	[19517] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19517 - DACL_RETUNE_C31_0 */
-	[19518] = { 0x00FF, 0x00FF, 0x0000 }, /* R19518 - DACL_RETUNE_C32_1 */
-	[19519] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19519 - DACL_RETUNE_C32_0 */
-	[19968] = { 0x00FF, 0x00FF, 0x0000 }, /* R19968 - RETUNEDAC_PG2_1 */
-	[19969] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19969 - RETUNEDAC_PG2_0 */
-	[19970] = { 0x00FF, 0x00FF, 0x0000 }, /* R19970 - RETUNEDAC_PG_1 */
-	[19971] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R19971 - RETUNEDAC_PG_0 */
-	[20480] = { 0x00FF, 0x00FF, 0x0000 }, /* R20480 - DACR_RETUNE_C1_1 */
-	[20481] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20481 - DACR_RETUNE_C1_0 */
-	[20482] = { 0x00FF, 0x00FF, 0x0000 }, /* R20482 - DACR_RETUNE_C2_1 */
-	[20483] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20483 - DACR_RETUNE_C2_0 */
-	[20484] = { 0x00FF, 0x00FF, 0x0000 }, /* R20484 - DACR_RETUNE_C3_1 */
-	[20485] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20485 - DACR_RETUNE_C3_0 */
-	[20486] = { 0x00FF, 0x00FF, 0x0000 }, /* R20486 - DACR_RETUNE_C4_1 */
-	[20487] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20487 - DACR_RETUNE_C4_0 */
-	[20488] = { 0x00FF, 0x00FF, 0x0000 }, /* R20488 - DACR_RETUNE_C5_1 */
-	[20489] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20489 - DACR_RETUNE_C5_0 */
-	[20490] = { 0x00FF, 0x00FF, 0x0000 }, /* R20490 - DACR_RETUNE_C6_1 */
-	[20491] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20491 - DACR_RETUNE_C6_0 */
-	[20492] = { 0x00FF, 0x00FF, 0x0000 }, /* R20492 - DACR_RETUNE_C7_1 */
-	[20493] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20493 - DACR_RETUNE_C7_0 */
-	[20494] = { 0x00FF, 0x00FF, 0x0000 }, /* R20494 - DACR_RETUNE_C8_1 */
-	[20495] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20495 - DACR_RETUNE_C8_0 */
-	[20496] = { 0x00FF, 0x00FF, 0x0000 }, /* R20496 - DACR_RETUNE_C9_1 */
-	[20497] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20497 - DACR_RETUNE_C9_0 */
-	[20498] = { 0x00FF, 0x00FF, 0x0000 }, /* R20498 - DACR_RETUNE_C10_1 */
-	[20499] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20499 - DACR_RETUNE_C10_0 */
-	[20500] = { 0x00FF, 0x00FF, 0x0000 }, /* R20500 - DACR_RETUNE_C11_1 */
-	[20501] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20501 - DACR_RETUNE_C11_0 */
-	[20502] = { 0x00FF, 0x00FF, 0x0000 }, /* R20502 - DACR_RETUNE_C12_1 */
-	[20503] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20503 - DACR_RETUNE_C12_0 */
-	[20504] = { 0x00FF, 0x00FF, 0x0000 }, /* R20504 - DACR_RETUNE_C13_1 */
-	[20505] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20505 - DACR_RETUNE_C13_0 */
-	[20506] = { 0x00FF, 0x00FF, 0x0000 }, /* R20506 - DACR_RETUNE_C14_1 */
-	[20507] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20507 - DACR_RETUNE_C14_0 */
-	[20508] = { 0x00FF, 0x00FF, 0x0000 }, /* R20508 - DACR_RETUNE_C15_1 */
-	[20509] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20509 - DACR_RETUNE_C15_0 */
-	[20510] = { 0x00FF, 0x00FF, 0x0000 }, /* R20510 - DACR_RETUNE_C16_1 */
-	[20511] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20511 - DACR_RETUNE_C16_0 */
-	[20512] = { 0x00FF, 0x00FF, 0x0000 }, /* R20512 - DACR_RETUNE_C17_1 */
-	[20513] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20513 - DACR_RETUNE_C17_0 */
-	[20514] = { 0x00FF, 0x00FF, 0x0000 }, /* R20514 - DACR_RETUNE_C18_1 */
-	[20515] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20515 - DACR_RETUNE_C18_0 */
-	[20516] = { 0x00FF, 0x00FF, 0x0000 }, /* R20516 - DACR_RETUNE_C19_1 */
-	[20517] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20517 - DACR_RETUNE_C19_0 */
-	[20518] = { 0x00FF, 0x00FF, 0x0000 }, /* R20518 - DACR_RETUNE_C20_1 */
-	[20519] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20519 - DACR_RETUNE_C20_0 */
-	[20520] = { 0x00FF, 0x00FF, 0x0000 }, /* R20520 - DACR_RETUNE_C21_1 */
-	[20521] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20521 - DACR_RETUNE_C21_0 */
-	[20522] = { 0x00FF, 0x00FF, 0x0000 }, /* R20522 - DACR_RETUNE_C22_1 */
-	[20523] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20523 - DACR_RETUNE_C22_0 */
-	[20524] = { 0x00FF, 0x00FF, 0x0000 }, /* R20524 - DACR_RETUNE_C23_1 */
-	[20525] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20525 - DACR_RETUNE_C23_0 */
-	[20526] = { 0x00FF, 0x00FF, 0x0000 }, /* R20526 - DACR_RETUNE_C24_1 */
-	[20527] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20527 - DACR_RETUNE_C24_0 */
-	[20528] = { 0x00FF, 0x00FF, 0x0000 }, /* R20528 - DACR_RETUNE_C25_1 */
-	[20529] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20529 - DACR_RETUNE_C25_0 */
-	[20530] = { 0x00FF, 0x00FF, 0x0000 }, /* R20530 - DACR_RETUNE_C26_1 */
-	[20531] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20531 - DACR_RETUNE_C26_0 */
-	[20532] = { 0x00FF, 0x00FF, 0x0000 }, /* R20532 - DACR_RETUNE_C27_1 */
-	[20533] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20533 - DACR_RETUNE_C27_0 */
-	[20534] = { 0x00FF, 0x00FF, 0x0000 }, /* R20534 - DACR_RETUNE_C28_1 */
-	[20535] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20535 - DACR_RETUNE_C28_0 */
-	[20536] = { 0x00FF, 0x00FF, 0x0000 }, /* R20536 - DACR_RETUNE_C29_1 */
-	[20537] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20537 - DACR_RETUNE_C29_0 */
-	[20538] = { 0x00FF, 0x00FF, 0x0000 }, /* R20538 - DACR_RETUNE_C30_1 */
-	[20539] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20539 - DACR_RETUNE_C30_0 */
-	[20540] = { 0x00FF, 0x00FF, 0x0000 }, /* R20540 - DACR_RETUNE_C31_1 */
-	[20541] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20541 - DACR_RETUNE_C31_0 */
-	[20542] = { 0x00FF, 0x00FF, 0x0000 }, /* R20542 - DACR_RETUNE_C32_1 */
-	[20543] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20543 - DACR_RETUNE_C32_0 */
-	[20992] = { 0x00FF, 0x00FF, 0x0000 }, /* R20992 - VSS_XHD2_1 */
-	[20993] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20993 - VSS_XHD2_0 */
-	[20994] = { 0x00FF, 0x00FF, 0x0000 }, /* R20994 - VSS_XHD3_1 */
-	[20995] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20995 - VSS_XHD3_0 */
-	[20996] = { 0x00FF, 0x00FF, 0x0000 }, /* R20996 - VSS_XHN1_1 */
-	[20997] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20997 - VSS_XHN1_0 */
-	[20998] = { 0x00FF, 0x00FF, 0x0000 }, /* R20998 - VSS_XHN2_1 */
-	[20999] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R20999 - VSS_XHN2_0 */
-	[21000] = { 0x00FF, 0x00FF, 0x0000 }, /* R21000 - VSS_XHN3_1 */
-	[21001] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21001 - VSS_XHN3_0 */
-	[21002] = { 0x00FF, 0x00FF, 0x0000 }, /* R21002 - VSS_XLA_1 */
-	[21003] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21003 - VSS_XLA_0 */
-	[21004] = { 0x00FF, 0x00FF, 0x0000 }, /* R21004 - VSS_XLB_1 */
-	[21005] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21005 - VSS_XLB_0 */
-	[21006] = { 0x00FF, 0x00FF, 0x0000 }, /* R21006 - VSS_XLG_1 */
-	[21007] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21007 - VSS_XLG_0 */
-	[21008] = { 0x00FF, 0x00FF, 0x0000 }, /* R21008 - VSS_PG2_1 */
-	[21009] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21009 - VSS_PG2_0 */
-	[21010] = { 0x00FF, 0x00FF, 0x0000 }, /* R21010 - VSS_PG_1 */
-	[21011] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21011 - VSS_PG_0 */
-	[21012] = { 0x00FF, 0x00FF, 0x0000 }, /* R21012 - VSS_XTD1_1 */
-	[21013] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21013 - VSS_XTD1_0 */
-	[21014] = { 0x00FF, 0x00FF, 0x0000 }, /* R21014 - VSS_XTD2_1 */
-	[21015] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21015 - VSS_XTD2_0 */
-	[21016] = { 0x00FF, 0x00FF, 0x0000 }, /* R21016 - VSS_XTD3_1 */
-	[21017] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21017 - VSS_XTD3_0 */
-	[21018] = { 0x00FF, 0x00FF, 0x0000 }, /* R21018 - VSS_XTD4_1 */
-	[21019] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21019 - VSS_XTD4_0 */
-	[21020] = { 0x00FF, 0x00FF, 0x0000 }, /* R21020 - VSS_XTD5_1 */
-	[21021] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21021 - VSS_XTD5_0 */
-	[21022] = { 0x00FF, 0x00FF, 0x0000 }, /* R21022 - VSS_XTD6_1 */
-	[21023] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21023 - VSS_XTD6_0 */
-	[21024] = { 0x00FF, 0x00FF, 0x0000 }, /* R21024 - VSS_XTD7_1 */
-	[21025] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21025 - VSS_XTD7_0 */
-	[21026] = { 0x00FF, 0x00FF, 0x0000 }, /* R21026 - VSS_XTD8_1 */
-	[21027] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21027 - VSS_XTD8_0 */
-	[21028] = { 0x00FF, 0x00FF, 0x0000 }, /* R21028 - VSS_XTD9_1 */
-	[21029] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21029 - VSS_XTD9_0 */
-	[21030] = { 0x00FF, 0x00FF, 0x0000 }, /* R21030 - VSS_XTD10_1 */
-	[21031] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21031 - VSS_XTD10_0 */
-	[21032] = { 0x00FF, 0x00FF, 0x0000 }, /* R21032 - VSS_XTD11_1 */
-	[21033] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21033 - VSS_XTD11_0 */
-	[21034] = { 0x00FF, 0x00FF, 0x0000 }, /* R21034 - VSS_XTD12_1 */
-	[21035] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21035 - VSS_XTD12_0 */
-	[21036] = { 0x00FF, 0x00FF, 0x0000 }, /* R21036 - VSS_XTD13_1 */
-	[21037] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21037 - VSS_XTD13_0 */
-	[21038] = { 0x00FF, 0x00FF, 0x0000 }, /* R21038 - VSS_XTD14_1 */
-	[21039] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21039 - VSS_XTD14_0 */
-	[21040] = { 0x00FF, 0x00FF, 0x0000 }, /* R21040 - VSS_XTD15_1 */
-	[21041] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21041 - VSS_XTD15_0 */
-	[21042] = { 0x00FF, 0x00FF, 0x0000 }, /* R21042 - VSS_XTD16_1 */
-	[21043] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21043 - VSS_XTD16_0 */
-	[21044] = { 0x00FF, 0x00FF, 0x0000 }, /* R21044 - VSS_XTD17_1 */
-	[21045] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21045 - VSS_XTD17_0 */
-	[21046] = { 0x00FF, 0x00FF, 0x0000 }, /* R21046 - VSS_XTD18_1 */
-	[21047] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21047 - VSS_XTD18_0 */
-	[21048] = { 0x00FF, 0x00FF, 0x0000 }, /* R21048 - VSS_XTD19_1 */
-	[21049] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21049 - VSS_XTD19_0 */
-	[21050] = { 0x00FF, 0x00FF, 0x0000 }, /* R21050 - VSS_XTD20_1 */
-	[21051] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21051 - VSS_XTD20_0 */
-	[21052] = { 0x00FF, 0x00FF, 0x0000 }, /* R21052 - VSS_XTD21_1 */
-	[21053] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21053 - VSS_XTD21_0 */
-	[21054] = { 0x00FF, 0x00FF, 0x0000 }, /* R21054 - VSS_XTD22_1 */
-	[21055] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21055 - VSS_XTD22_0 */
-	[21056] = { 0x00FF, 0x00FF, 0x0000 }, /* R21056 - VSS_XTD23_1 */
-	[21057] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21057 - VSS_XTD23_0 */
-	[21058] = { 0x00FF, 0x00FF, 0x0000 }, /* R21058 - VSS_XTD24_1 */
-	[21059] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21059 - VSS_XTD24_0 */
-	[21060] = { 0x00FF, 0x00FF, 0x0000 }, /* R21060 - VSS_XTD25_1 */
-	[21061] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21061 - VSS_XTD25_0 */
-	[21062] = { 0x00FF, 0x00FF, 0x0000 }, /* R21062 - VSS_XTD26_1 */
-	[21063] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21063 - VSS_XTD26_0 */
-	[21064] = { 0x00FF, 0x00FF, 0x0000 }, /* R21064 - VSS_XTD27_1 */
-	[21065] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21065 - VSS_XTD27_0 */
-	[21066] = { 0x00FF, 0x00FF, 0x0000 }, /* R21066 - VSS_XTD28_1 */
-	[21067] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21067 - VSS_XTD28_0 */
-	[21068] = { 0x00FF, 0x00FF, 0x0000 }, /* R21068 - VSS_XTD29_1 */
-	[21069] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21069 - VSS_XTD29_0 */
-	[21070] = { 0x00FF, 0x00FF, 0x0000 }, /* R21070 - VSS_XTD30_1 */
-	[21071] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21071 - VSS_XTD30_0 */
-	[21072] = { 0x00FF, 0x00FF, 0x0000 }, /* R21072 - VSS_XTD31_1 */
-	[21073] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21073 - VSS_XTD31_0 */
-	[21074] = { 0x00FF, 0x00FF, 0x0000 }, /* R21074 - VSS_XTD32_1 */
-	[21075] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21075 - VSS_XTD32_0 */
-	[21076] = { 0x00FF, 0x00FF, 0x0000 }, /* R21076 - VSS_XTS1_1 */
-	[21077] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21077 - VSS_XTS1_0 */
-	[21078] = { 0x00FF, 0x00FF, 0x0000 }, /* R21078 - VSS_XTS2_1 */
-	[21079] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21079 - VSS_XTS2_0 */
-	[21080] = { 0x00FF, 0x00FF, 0x0000 }, /* R21080 - VSS_XTS3_1 */
-	[21081] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21081 - VSS_XTS3_0 */
-	[21082] = { 0x00FF, 0x00FF, 0x0000 }, /* R21082 - VSS_XTS4_1 */
-	[21083] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21083 - VSS_XTS4_0 */
-	[21084] = { 0x00FF, 0x00FF, 0x0000 }, /* R21084 - VSS_XTS5_1 */
-	[21085] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21085 - VSS_XTS5_0 */
-	[21086] = { 0x00FF, 0x00FF, 0x0000 }, /* R21086 - VSS_XTS6_1 */
-	[21087] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21087 - VSS_XTS6_0 */
-	[21088] = { 0x00FF, 0x00FF, 0x0000 }, /* R21088 - VSS_XTS7_1 */
-	[21089] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21089 - VSS_XTS7_0 */
-	[21090] = { 0x00FF, 0x00FF, 0x0000 }, /* R21090 - VSS_XTS8_1 */
-	[21091] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21091 - VSS_XTS8_0 */
-	[21092] = { 0x00FF, 0x00FF, 0x0000 }, /* R21092 - VSS_XTS9_1 */
-	[21093] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21093 - VSS_XTS9_0 */
-	[21094] = { 0x00FF, 0x00FF, 0x0000 }, /* R21094 - VSS_XTS10_1 */
-	[21095] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21095 - VSS_XTS10_0 */
-	[21096] = { 0x00FF, 0x00FF, 0x0000 }, /* R21096 - VSS_XTS11_1 */
-	[21097] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21097 - VSS_XTS11_0 */
-	[21098] = { 0x00FF, 0x00FF, 0x0000 }, /* R21098 - VSS_XTS12_1 */
-	[21099] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21099 - VSS_XTS12_0 */
-	[21100] = { 0x00FF, 0x00FF, 0x0000 }, /* R21100 - VSS_XTS13_1 */
-	[21101] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21101 - VSS_XTS13_0 */
-	[21102] = { 0x00FF, 0x00FF, 0x0000 }, /* R21102 - VSS_XTS14_1 */
-	[21103] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21103 - VSS_XTS14_0 */
-	[21104] = { 0x00FF, 0x00FF, 0x0000 }, /* R21104 - VSS_XTS15_1 */
-	[21105] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21105 - VSS_XTS15_0 */
-	[21106] = { 0x00FF, 0x00FF, 0x0000 }, /* R21106 - VSS_XTS16_1 */
-	[21107] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21107 - VSS_XTS16_0 */
-	[21108] = { 0x00FF, 0x00FF, 0x0000 }, /* R21108 - VSS_XTS17_1 */
-	[21109] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21109 - VSS_XTS17_0 */
-	[21110] = { 0x00FF, 0x00FF, 0x0000 }, /* R21110 - VSS_XTS18_1 */
-	[21111] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21111 - VSS_XTS18_0 */
-	[21112] = { 0x00FF, 0x00FF, 0x0000 }, /* R21112 - VSS_XTS19_1 */
-	[21113] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21113 - VSS_XTS19_0 */
-	[21114] = { 0x00FF, 0x00FF, 0x0000 }, /* R21114 - VSS_XTS20_1 */
-	[21115] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21115 - VSS_XTS20_0 */
-	[21116] = { 0x00FF, 0x00FF, 0x0000 }, /* R21116 - VSS_XTS21_1 */
-	[21117] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21117 - VSS_XTS21_0 */
-	[21118] = { 0x00FF, 0x00FF, 0x0000 }, /* R21118 - VSS_XTS22_1 */
-	[21119] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21119 - VSS_XTS22_0 */
-	[21120] = { 0x00FF, 0x00FF, 0x0000 }, /* R21120 - VSS_XTS23_1 */
-	[21121] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21121 - VSS_XTS23_0 */
-	[21122] = { 0x00FF, 0x00FF, 0x0000 }, /* R21122 - VSS_XTS24_1 */
-	[21123] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21123 - VSS_XTS24_0 */
-	[21124] = { 0x00FF, 0x00FF, 0x0000 }, /* R21124 - VSS_XTS25_1 */
-	[21125] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21125 - VSS_XTS25_0 */
-	[21126] = { 0x00FF, 0x00FF, 0x0000 }, /* R21126 - VSS_XTS26_1 */
-	[21127] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21127 - VSS_XTS26_0 */
-	[21128] = { 0x00FF, 0x00FF, 0x0000 }, /* R21128 - VSS_XTS27_1 */
-	[21129] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21129 - VSS_XTS27_0 */
-	[21130] = { 0x00FF, 0x00FF, 0x0000 }, /* R21130 - VSS_XTS28_1 */
-	[21131] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21131 - VSS_XTS28_0 */
-	[21132] = { 0x00FF, 0x00FF, 0x0000 }, /* R21132 - VSS_XTS29_1 */
-	[21133] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21133 - VSS_XTS29_0 */
-	[21134] = { 0x00FF, 0x00FF, 0x0000 }, /* R21134 - VSS_XTS30_1 */
-	[21135] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21135 - VSS_XTS30_0 */
-	[21136] = { 0x00FF, 0x00FF, 0x0000 }, /* R21136 - VSS_XTS31_1 */
-	[21137] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21137 - VSS_XTS31_0 */
-	[21138] = { 0x00FF, 0x00FF, 0x0000 }, /* R21138 - VSS_XTS32_1 */
-	[21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */
+	u16 volatile_register;
+} wm8962_reg_access[] = {
+	{0, 0x00FF, 0x01FF, 0x0000 }, /* R0     - Left Input volume */
+	{1, 0xFEFF, 0x01FF, 0xFFFF }, /* R1     - Right Input volume */
+	{2, 0x00FF, 0x01FF, 0x0000 }, /* R2     - HPOUTL volume */
+	{3, 0x00FF, 0x01FF, 0x0000 }, /* R3     - HPOUTR volume */
+	{4, 0x07FE, 0x07FE, 0xFFFF }, /* R4     - Clocking1 */
+	{5, 0x007F, 0x007F, 0x0000 }, /* R5     - ADC & DAC Control 1 */
+	{6, 0x37ED, 0x37ED, 0x0000 }, /* R6     - ADC & DAC Control 2 */
+	{7, 0x1FFF, 0x1FFF, 0x0000 }, /* R7     - Audio Interface 0 */
+	{8, 0x0FEF, 0x0FEF, 0xFFFF }, /* R8     - Clocking2 */
+	{9, 0x0B9F, 0x039F, 0x0000 }, /* R9     - Audio Interface 1 */
+	{10, 0x00FF, 0x01FF, 0x0000 }, /* R10    - Left DAC volume */
+	{11, 0x00FF, 0x01FF, 0x0000 }, /* R11    - Right DAC volume */
+	{14, 0x07FF, 0x07FF, 0x0000 }, /* R14    - Audio Interface 2 */
+	{15, 0xFFFF, 0xFFFF, 0xFFFF }, /* R15    - Software Reset */
+	{17, 0x07FF, 0x07FF, 0x0000 }, /* R17    - ALC1 */
+	{18, 0xF8FF, 0x00FF, 0xFFFF }, /* R18    - ALC2 */
+	{19, 0x1DFF, 0x1DFF, 0x0000 }, /* R19    - ALC3 */
+	{20, 0xFFFF, 0xFFFF, 0x0000 }, /* R20    - Noise Gate */
+	{21, 0x00FF, 0x01FF, 0x0000 }, /* R21    - Left ADC volume */
+	{22, 0x00FF, 0x01FF, 0x0000 }, /* R22    - Right ADC volume */
+	{23, 0x0161, 0x0161, 0x0000 }, /* R23    - Additional control(1) */
+	{24, 0x0008, 0x0008, 0x0000 }, /* R24    - Additional control(2) */
+	{25, 0x07FE, 0x07FE, 0x0000 }, /* R25    - Pwr Mgmt (1) */
+	{26, 0x01FB, 0x01FB, 0x0000 }, /* R26    - Pwr Mgmt (2) */
+	{27, 0x0017, 0x0017, 0x0000 }, /* R27    - Additional Control (3) */
+	{28, 0x001C, 0x001C, 0x0000 }, /* R28    - Anti-pop */
+	{30, 0xFFFE, 0xFFFE, 0x0000 }, /* R30    - Clocking 3 */
+	{31, 0x000F, 0x000F, 0x0000 }, /* R31    - Input mixer control (1) */
+	{32, 0x01FF, 0x01FF, 0x0000 }, /* R32    - Left input mixer volume */
+	{33, 0x01FF, 0x01FF, 0x0000 }, /* R33    - Right input mixer volume */
+	{34, 0x003F, 0x003F, 0x0000 }, /* R34    - Input mixer control (2) */
+	{35, 0x003F, 0x003F, 0x0000 }, /* R35    - Input bias control */
+	{37, 0x001F, 0x001F, 0x0000 }, /* R37    - Left input PGA control */
+	{38, 0x001F, 0x001F, 0x0000 }, /* R38    - Right input PGA control */
+	{40, 0x00FF, 0x01FF, 0x0000 }, /* R40    - SPKOUTL volume */
+	{41, 0x00FF, 0x01FF, 0x0000 }, /* R41    - SPKOUTR volume */
+	{47, 0x000F, 0x0000, 0x0000 }, /* R47    - Thermal Shutdown Status */
+	{48, 0x7EC7, 0x7E07, 0xFFFF }, /* R48    - Additional Control (4) */
+	{49, 0x00D3, 0x00D7, 0xFFFF }, /* R49    - Class D Control 1 */
+	{51, 0x0047, 0x0047, 0x0000 }, /* R51    - Class D Control 2 */
+	{56, 0x001E, 0x001E, 0x0000 }, /* R56    - Clocking 4 */
+	{57, 0x02FC, 0x02FC, 0x0000 }, /* R57    - DAC DSP Mixing (1) */
+	{58, 0x00FC, 0x00FC, 0x0000 }, /* R58    - DAC DSP Mixing (2) */
+	{60, 0x00CC, 0x00CC, 0x0000 }, /* R60    - DC Servo 0 */
+	{61, 0x00DD, 0x00DD, 0x0000 }, /* R61    - DC Servo 1 */
+	{64, 0x3F80, 0x3F80, 0x0000 }, /* R64    - DC Servo 4 */
+	{66, 0x0780, 0x0000, 0xFFFF }, /* R66    - DC Servo 6 */
+	{68, 0x0007, 0x0007, 0x0000 }, /* R68    - Analogue PGA Bias */
+	{69, 0x00FF, 0x00FF, 0x0000 }, /* R69    - Analogue HP 0 */
+	{71, 0x01FF, 0x01FF, 0x0000 }, /* R71    - Analogue HP 2 */
+	{72, 0x0001, 0x0001, 0x0000 }, /* R72    - Charge Pump 1 */
+	{82, 0x0001, 0x0001, 0x0000 }, /* R82    - Charge Pump B */
+	{87, 0x00A0, 0x00A0, 0x0000 }, /* R87    - Write Sequencer Control 1 */
+	{90, 0x007F, 0x01FF, 0x0000 }, /* R90    - Write Sequencer Control 2 */
+	{93, 0x03F9, 0x0000, 0x0000 }, /* R93    - Write Sequencer Control 3 */
+	{94, 0x0070, 0x0070, 0x0000 }, /* R94    - Control Interface */
+	{99, 0x000F, 0x000F, 0x0000 }, /* R99    - Mixer Enables */
+	{100, 0x00BF, 0x00BF, 0x0000 }, /* R100   - Headphone Mixer (1) */
+	{101, 0x00BF, 0x00BF, 0x0000 }, /* R101   - Headphone Mixer (2) */
+	{102, 0x01FF, 0x01FF, 0x0000 }, /* R102   - Headphone Mixer (3) */
+	{103, 0x01FF, 0x01FF, 0x0000 }, /* R103   - Headphone Mixer (4) */
+	{105, 0x00BF, 0x00BF, 0x0000 }, /* R105   - Speaker Mixer (1) */
+	{106, 0x00BF, 0x00BF, 0x0000 }, /* R106   - Speaker Mixer (2) */
+	{107, 0x01FF, 0x01FF, 0x0000 }, /* R107   - Speaker Mixer (3) */
+	{108, 0x01FF, 0x01FF, 0x0000 }, /* R108   - Speaker Mixer (4) */
+	{109, 0x00F0, 0x00F0, 0x0000 }, /* R109   - Speaker Mixer (5) */
+	{110, 0x00F7, 0x00F7, 0x0000 }, /* R110   - Beep Generator (1) */
+	{115, 0x001F, 0x001F, 0x0000 }, /* R115   - Oscillator Trim (3) */
+	{116, 0x001F, 0x001F, 0x0000 }, /* R116   - Oscillator Trim (4) */
+	{119, 0x00FF, 0x00FF, 0x0000 }, /* R119   - Oscillator Trim (7) */
+	{124, 0x0079, 0x0079, 0x0000 }, /* R124   - Analogue Clocking1 */
+	{125, 0x00DF, 0x00DF, 0x0000 }, /* R125   - Analogue Clocking2 */
+	{126, 0x000D, 0x000D, 0x0000 }, /* R126   - Analogue Clocking3 */
+	{127, 0x0000, 0xFFFF, 0x0000 }, /* R127   - PLL Software Reset */
+	{129, 0x00B0, 0x00B0, 0x0000 }, /* R129   - PLL2 */
+	{131, 0x0003, 0x0003, 0x0000 }, /* R131   - PLL 4 */
+	{136, 0x005F, 0x005F, 0x0000 }, /* R136   - PLL 9 */
+	{137, 0x00FF, 0x00FF, 0x0000 }, /* R137   - PLL 10 */
+	{138, 0x00FF, 0x00FF, 0x0000 }, /* R138   - PLL 11 */
+	{139, 0x00FF, 0x00FF, 0x0000 }, /* R139   - PLL 12 */
+	{140, 0x005F, 0x005F, 0x0000 }, /* R140   - PLL 13 */
+	{141, 0x00FF, 0x00FF, 0x0000 }, /* R141   - PLL 14 */
+	{142, 0x00FF, 0x00FF, 0x0000 }, /* R142   - PLL 15 */
+	{143, 0x00FF, 0x00FF, 0x0000 }, /* R143   - PLL 16 */
+	{155, 0x0067, 0x0067, 0x0000 }, /* R155   - FLL Control (1) */
+	{156, 0x01FB, 0x01FB, 0x0000 }, /* R156   - FLL Control (2) */
+	{157, 0x0007, 0x0007, 0x0000 }, /* R157   - FLL Control (3) */
+	{159, 0x007F, 0x007F, 0x0000 }, /* R159   - FLL Control (5) */
+	{160, 0xFFFF, 0xFFFF, 0x0000 }, /* R160   - FLL Control (6) */
+	{161, 0xFFFF, 0xFFFF, 0x0000 }, /* R161   - FLL Control (7) */
+	{162, 0x03FF, 0x03FF, 0x0000 }, /* R162   - FLL Control (8) */
+	{252, 0x0005, 0x0005, 0x0000 }, /* R252   - General test 1 */
+	{256, 0x000F, 0x000F, 0x0000 }, /* R256   - DF1 */
+	{257, 0xFFFF, 0xFFFF, 0x0000 }, /* R257   - DF2 */
+	{258, 0xFFFF, 0xFFFF, 0x0000 }, /* R258   - DF3 */
+	{259, 0xFFFF, 0xFFFF, 0x0000 }, /* R259   - DF4 */
+	{260, 0xFFFF, 0xFFFF, 0x0000 }, /* R260   - DF5 */
+	{261, 0xFFFF, 0xFFFF, 0x0000 }, /* R261   - DF6 */
+	{262, 0xFFFF, 0xFFFF, 0x0000 }, /* R262   - DF7 */
+	{264, 0x0003, 0x0003, 0x0000 }, /* R264   - LHPF1 */
+	{265, 0xFFFF, 0xFFFF, 0x0000 }, /* R265   - LHPF2 */
+	{268, 0x0077, 0x0077, 0x0000 }, /* R268   - THREED1 */
+	{269, 0xFFFC, 0xFFFC, 0x0000 }, /* R269   - THREED2 */
+	{270, 0xFFFF, 0xFFFF, 0x0000 }, /* R270   - THREED3 */
+	{271, 0xFFFC, 0xFFFC, 0x0000 }, /* R271   - THREED4 */
+	{276, 0x7FFF, 0x7FFF, 0x0000 }, /* R276   - DRC 1 */
+	{277, 0x1FFF, 0x1FFF, 0x0000 }, /* R277   - DRC 2 */
+	{278, 0xFFFF, 0xFFFF, 0x0000 }, /* R278   - DRC 3 */
+	{279, 0x07FF, 0x07FF, 0x0000 }, /* R279   - DRC 4 */
+	{280, 0x03FF, 0x03FF, 0x0000 }, /* R280   - DRC 5 */
+	{285, 0x0003, 0x0003, 0x0000 }, /* R285   - Tloopback */
+	{335, 0x0007, 0x0007, 0x0000 }, /* R335   - EQ1 */
+	{336, 0xFFFE, 0xFFFE, 0x0000 }, /* R336   - EQ2 */
+	{337, 0xFFC0, 0xFFC0, 0x0000 }, /* R337   - EQ3 */
+	{338, 0xFFFF, 0xFFFF, 0x0000 }, /* R338   - EQ4 */
+	{339, 0xFFFF, 0xFFFF, 0x0000 }, /* R339   - EQ5 */
+	{340, 0xFFFF, 0xFFFF, 0x0000 }, /* R340   - EQ6 */
+	{341, 0xFFFF, 0xFFFF, 0x0000 }, /* R341   - EQ7 */
+	{342, 0xFFFF, 0xFFFF, 0x0000 }, /* R342   - EQ8 */
+	{343, 0xFFFF, 0xFFFF, 0x0000 }, /* R343   - EQ9 */
+	{344, 0xFFFF, 0xFFFF, 0x0000 }, /* R344   - EQ10 */
+	{345, 0xFFFF, 0xFFFF, 0x0000 }, /* R345   - EQ11 */
+	{346, 0xFFFF, 0xFFFF, 0x0000 }, /* R346   - EQ12 */
+	{347, 0xFFFF, 0xFFFF, 0x0000 }, /* R347   - EQ13 */
+	{348, 0xFFFF, 0xFFFF, 0x0000 }, /* R348   - EQ14 */
+	{349, 0xFFFF, 0xFFFF, 0x0000 }, /* R349   - EQ15 */
+	{350, 0xFFFF, 0xFFFF, 0x0000 }, /* R350   - EQ16 */
+	{351, 0xFFFF, 0xFFFF, 0x0000 }, /* R351   - EQ17 */
+	{352, 0xFFFF, 0xFFFF, 0x0000 }, /* R352   - EQ18 */
+	{353, 0xFFFF, 0xFFFF, 0x0000 }, /* R353   - EQ19 */
+	{354, 0xFFFF, 0xFFFF, 0x0000 }, /* R354   - EQ20 */
+	{355, 0xFFFF, 0xFFFF, 0x0000 }, /* R355   - EQ21 */
+	{356, 0xFFFE, 0xFFFE, 0x0000 }, /* R356   - EQ22 */
+	{357, 0xFFC0, 0xFFC0, 0x0000 }, /* R357   - EQ23 */
+	{358, 0xFFFF, 0xFFFF, 0x0000 }, /* R358   - EQ24 */
+	{359, 0xFFFF, 0xFFFF, 0x0000 }, /* R359   - EQ25 */
+	{360, 0xFFFF, 0xFFFF, 0x0000 }, /* R360   - EQ26 */
+	{361, 0xFFFF, 0xFFFF, 0x0000 }, /* R361   - EQ27 */
+	{362, 0xFFFF, 0xFFFF, 0x0000 }, /* R362   - EQ28 */
+	{363, 0xFFFF, 0xFFFF, 0x0000 }, /* R363   - EQ29 */
+	{364, 0xFFFF, 0xFFFF, 0x0000 }, /* R364   - EQ30 */
+	{365, 0xFFFF, 0xFFFF, 0x0000 }, /* R365   - EQ31 */
+	{366, 0xFFFF, 0xFFFF, 0x0000 }, /* R366   - EQ32 */
+	{367, 0xFFFF, 0xFFFF, 0x0000 }, /* R367   - EQ33 */
+	{368, 0xFFFF, 0xFFFF, 0x0000 }, /* R368   - EQ34 */
+	{369, 0xFFFF, 0xFFFF, 0x0000 }, /* R369   - EQ35 */
+	{370, 0xFFFF, 0xFFFF, 0x0000 }, /* R370   - EQ36 */
+	{371, 0xFFFF, 0xFFFF, 0x0000 }, /* R371   - EQ37 */
+	{372, 0xFFFF, 0xFFFF, 0x0000 }, /* R372   - EQ38 */
+	{373, 0xFFFF, 0xFFFF, 0x0000 }, /* R373   - EQ39 */
+	{374, 0xFFFF, 0xFFFF, 0x0000 }, /* R374   - EQ40 */
+	{375, 0xFFFF, 0xFFFF, 0x0000 }, /* R375   - EQ41 */
+	{513, 0x045F, 0x045F, 0x0000 }, /* R513   - GPIO 2 */
+	{514, 0x045F, 0x045F, 0x0000 }, /* R514   - GPIO 3 */
+	{516, 0xE75F, 0xE75F, 0x0000 }, /* R516   - GPIO 5 */
+	{517, 0xE75F, 0xE75F, 0x0000 }, /* R517   - GPIO 6 */
+	{560, 0x0030, 0x0030, 0xFFFF }, /* R560   - Interrupt Status 1 */
+	{561, 0xFFED, 0xFFED, 0xFFFF }, /* R561   - Interrupt Status 2 */
+	{568, 0x0030, 0x0030, 0x0000 }, /* R568   - Interrupt Status 1 Mask */
+	{569, 0xFFED, 0xFFED, 0x0000 }, /* R569   - Interrupt Status 2 Mask */
+	{576, 0x0001, 0x0001, 0x0000 }, /* R576   - Interrupt Control */
+	{584, 0x002D, 0x002D, 0x0000 }, /* R584   - IRQ Debounce */
+	{586, 0xC000, 0xC000, 0x0000 }, /* R586   -  MICINT Source Pol */
+	{768, 0x0001, 0x0001, 0x0000 }, /* R768   - DSP2 Power Management */
+	{1037, 0x0000, 0x003F, 0x0000 }, /* R1037  - DSP2_ExecControl */
+	{4096, 0x3FFF, 0x3FFF, 0x0000 }, /* R4096  - Write Sequencer 0 */
+	{4097, 0x00FF, 0x00FF, 0x0000 }, /* R4097  - Write Sequencer 1 */
+	{4098, 0x070F, 0x070F, 0x0000 }, /* R4098  - Write Sequencer 2 */
+	{4099, 0x010F, 0x010F, 0x0000 }, /* R4099  - Write Sequencer 3 */
+	{4100, 0x3FFF, 0x3FFF, 0x0000 }, /* R4100  - Write Sequencer 4 */
+	{4101, 0x00FF, 0x00FF, 0x0000 }, /* R4101  - Write Sequencer 5 */
+	{4102, 0x070F, 0x070F, 0x0000 }, /* R4102  - Write Sequencer 6 */
+	{4103, 0x010F, 0x010F, 0x0000 }, /* R4103  - Write Sequencer 7 */
+	{4104, 0x3FFF, 0x3FFF, 0x0000 }, /* R4104  - Write Sequencer 8 */
+	{4105, 0x00FF, 0x00FF, 0x0000 }, /* R4105  - Write Sequencer 9 */
+	{4106, 0x070F, 0x070F, 0x0000 }, /* R4106  - Write Sequencer 10 */
+	{4107, 0x010F, 0x010F, 0x0000 }, /* R4107  - Write Sequencer 11 */
+	{4108, 0x3FFF, 0x3FFF, 0x0000 }, /* R4108  - Write Sequencer 12 */
+	{4109, 0x00FF, 0x00FF, 0x0000 }, /* R4109  - Write Sequencer 13 */
+	{4110, 0x070F, 0x070F, 0x0000 }, /* R4110  - Write Sequencer 14 */
+	{4111, 0x010F, 0x010F, 0x0000 }, /* R4111  - Write Sequencer 15 */
+	{4112, 0x3FFF, 0x3FFF, 0x0000 }, /* R4112  - Write Sequencer 16 */
+	{4113, 0x00FF, 0x00FF, 0x0000 }, /* R4113  - Write Sequencer 17 */
+	{4114, 0x070F, 0x070F, 0x0000 }, /* R4114  - Write Sequencer 18 */
+	{4115, 0x010F, 0x010F, 0x0000 }, /* R4115  - Write Sequencer 19 */
+	{4116, 0x3FFF, 0x3FFF, 0x0000 }, /* R4116  - Write Sequencer 20 */
+	{4117, 0x00FF, 0x00FF, 0x0000 }, /* R4117  - Write Sequencer 21 */
+	{4118, 0x070F, 0x070F, 0x0000 }, /* R4118  - Write Sequencer 22 */
+	{4119, 0x010F, 0x010F, 0x0000 }, /* R4119  - Write Sequencer 23 */
+	{4120, 0x3FFF, 0x3FFF, 0x0000 }, /* R4120  - Write Sequencer 24 */
+	{4121, 0x00FF, 0x00FF, 0x0000 }, /* R4121  - Write Sequencer 25 */
+	{4122, 0x070F, 0x070F, 0x0000 }, /* R4122  - Write Sequencer 26 */
+	{4123, 0x010F, 0x010F, 0x0000 }, /* R4123  - Write Sequencer 27 */
+	{4124, 0x3FFF, 0x3FFF, 0x0000 }, /* R4124  - Write Sequencer 28 */
+	{4125, 0x00FF, 0x00FF, 0x0000 }, /* R4125  - Write Sequencer 29 */
+	{4126, 0x070F, 0x070F, 0x0000 }, /* R4126  - Write Sequencer 30 */
+	{4127, 0x010F, 0x010F, 0x0000 }, /* R4127  - Write Sequencer 31 */
+	{4128, 0x3FFF, 0x3FFF, 0x0000 }, /* R4128  - Write Sequencer 32 */
+	{4129, 0x00FF, 0x00FF, 0x0000 }, /* R4129  - Write Sequencer 33 */
+	{4130, 0x070F, 0x070F, 0x0000 }, /* R4130  - Write Sequencer 34 */
+	{4131, 0x010F, 0x010F, 0x0000 }, /* R4131  - Write Sequencer 35 */
+	{4132, 0x3FFF, 0x3FFF, 0x0000 }, /* R4132  - Write Sequencer 36 */
+	{4133, 0x00FF, 0x00FF, 0x0000 }, /* R4133  - Write Sequencer 37 */
+	{4134, 0x070F, 0x070F, 0x0000 }, /* R4134  - Write Sequencer 38 */
+	{4135, 0x010F, 0x010F, 0x0000 }, /* R4135  - Write Sequencer 39 */
+	{4136, 0x3FFF, 0x3FFF, 0x0000 }, /* R4136  - Write Sequencer 40 */
+	{4137, 0x00FF, 0x00FF, 0x0000 }, /* R4137  - Write Sequencer 41 */
+	{4138, 0x070F, 0x070F, 0x0000 }, /* R4138  - Write Sequencer 42 */
+	{4139, 0x010F, 0x010F, 0x0000 }, /* R4139  - Write Sequencer 43 */
+	{4140, 0x3FFF, 0x3FFF, 0x0000 }, /* R4140  - Write Sequencer 44 */
+	{4141, 0x00FF, 0x00FF, 0x0000 }, /* R4141  - Write Sequencer 45 */
+	{4142, 0x070F, 0x070F, 0x0000 }, /* R4142  - Write Sequencer 46 */
+	{4143, 0x010F, 0x010F, 0x0000 }, /* R4143  - Write Sequencer 47 */
+	{4144, 0x3FFF, 0x3FFF, 0x0000 }, /* R4144  - Write Sequencer 48 */
+	{4145, 0x00FF, 0x00FF, 0x0000 }, /* R4145  - Write Sequencer 49 */
+	{4146, 0x070F, 0x070F, 0x0000 }, /* R4146  - Write Sequencer 50 */
+	{4147, 0x010F, 0x010F, 0x0000 }, /* R4147  - Write Sequencer 51 */
+	{4148, 0x3FFF, 0x3FFF, 0x0000 }, /* R4148  - Write Sequencer 52 */
+	{4149, 0x00FF, 0x00FF, 0x0000 }, /* R4149  - Write Sequencer 53 */
+	{4150, 0x070F, 0x070F, 0x0000 }, /* R4150  - Write Sequencer 54 */
+	{4151, 0x010F, 0x010F, 0x0000 }, /* R4151  - Write Sequencer 55 */
+	{4152, 0x3FFF, 0x3FFF, 0x0000 }, /* R4152  - Write Sequencer 56 */
+	{4153, 0x00FF, 0x00FF, 0x0000 }, /* R4153  - Write Sequencer 57 */
+	{4154, 0x070F, 0x070F, 0x0000 }, /* R4154  - Write Sequencer 58 */
+	{4155, 0x010F, 0x010F, 0x0000 }, /* R4155  - Write Sequencer 59 */
+	{4156, 0x3FFF, 0x3FFF, 0x0000 }, /* R4156  - Write Sequencer 60 */
+	{4157, 0x00FF, 0x00FF, 0x0000 }, /* R4157  - Write Sequencer 61 */
+	{4158, 0x070F, 0x070F, 0x0000 }, /* R4158  - Write Sequencer 62 */
+	{4159, 0x010F, 0x010F, 0x0000 }, /* R4159  - Write Sequencer 63 */
+	{4160, 0x3FFF, 0x3FFF, 0x0000 }, /* R4160  - Write Sequencer 64 */
+	{4161, 0x00FF, 0x00FF, 0x0000 }, /* R4161  - Write Sequencer 65 */
+	{4162, 0x070F, 0x070F, 0x0000 }, /* R4162  - Write Sequencer 66 */
+	{4163, 0x010F, 0x010F, 0x0000 }, /* R4163  - Write Sequencer 67 */
+	{4164, 0x3FFF, 0x3FFF, 0x0000 }, /* R4164  - Write Sequencer 68 */
+	{4165, 0x00FF, 0x00FF, 0x0000 }, /* R4165  - Write Sequencer 69 */
+	{4166, 0x070F, 0x070F, 0x0000 }, /* R4166  - Write Sequencer 70 */
+	{4167, 0x010F, 0x010F, 0x0000 }, /* R4167  - Write Sequencer 71 */
+	{4168, 0x3FFF, 0x3FFF, 0x0000 }, /* R4168  - Write Sequencer 72 */
+	{4169, 0x00FF, 0x00FF, 0x0000 }, /* R4169  - Write Sequencer 73 */
+	{4170, 0x070F, 0x070F, 0x0000 }, /* R4170  - Write Sequencer 74 */
+	{4171, 0x010F, 0x010F, 0x0000 }, /* R4171  - Write Sequencer 75 */
+	{4172, 0x3FFF, 0x3FFF, 0x0000 }, /* R4172  - Write Sequencer 76 */
+	{4173, 0x00FF, 0x00FF, 0x0000 }, /* R4173  - Write Sequencer 77 */
+	{4174, 0x070F, 0x070F, 0x0000 }, /* R4174  - Write Sequencer 78 */
+	{4175, 0x010F, 0x010F, 0x0000 }, /* R4175  - Write Sequencer 79 */
+	{4176, 0x3FFF, 0x3FFF, 0x0000 }, /* R4176  - Write Sequencer 80 */
+	{4177, 0x00FF, 0x00FF, 0x0000 }, /* R4177  - Write Sequencer 81 */
+	{4178, 0x070F, 0x070F, 0x0000 }, /* R4178  - Write Sequencer 82 */
+	{4179, 0x010F, 0x010F, 0x0000 }, /* R4179  - Write Sequencer 83 */
+	{4180, 0x3FFF, 0x3FFF, 0x0000 }, /* R4180  - Write Sequencer 84 */
+	{4181, 0x00FF, 0x00FF, 0x0000 }, /* R4181  - Write Sequencer 85 */
+	{4182, 0x070F, 0x070F, 0x0000 }, /* R4182  - Write Sequencer 86 */
+	{4183, 0x010F, 0x010F, 0x0000 }, /* R4183  - Write Sequencer 87 */
+	{4184, 0x3FFF, 0x3FFF, 0x0000 }, /* R4184  - Write Sequencer 88 */
+	{4185, 0x00FF, 0x00FF, 0x0000 }, /* R4185  - Write Sequencer 89 */
+	{4186, 0x070F, 0x070F, 0x0000 }, /* R4186  - Write Sequencer 90 */
+	{4187, 0x010F, 0x010F, 0x0000 }, /* R4187  - Write Sequencer 91 */
+	{4188, 0x3FFF, 0x3FFF, 0x0000 }, /* R4188  - Write Sequencer 92 */
+	{4189, 0x00FF, 0x00FF, 0x0000 }, /* R4189  - Write Sequencer 93 */
+	{4190, 0x070F, 0x070F, 0x0000 }, /* R4190  - Write Sequencer 94 */
+	{4191, 0x010F, 0x010F, 0x0000 }, /* R4191  - Write Sequencer 95 */
+	{4192, 0x3FFF, 0x3FFF, 0x0000 }, /* R4192  - Write Sequencer 96 */
+	{4193, 0x00FF, 0x00FF, 0x0000 }, /* R4193  - Write Sequencer 97 */
+	{4194, 0x070F, 0x070F, 0x0000 }, /* R4194  - Write Sequencer 98 */
+	{4195, 0x010F, 0x010F, 0x0000 }, /* R4195  - Write Sequencer 99 */
+	{4196, 0x3FFF, 0x3FFF, 0x0000 }, /* R4196  - Write Sequencer 100 */
+	{4197, 0x00FF, 0x00FF, 0x0000 }, /* R4197  - Write Sequencer 101 */
+	{4198, 0x070F, 0x070F, 0x0000 }, /* R4198  - Write Sequencer 102 */
+	{4199, 0x010F, 0x010F, 0x0000 }, /* R4199  - Write Sequencer 103 */
+	{4200, 0x3FFF, 0x3FFF, 0x0000 }, /* R4200  - Write Sequencer 104 */
+	{4201, 0x00FF, 0x00FF, 0x0000 }, /* R4201  - Write Sequencer 105 */
+	{4202, 0x070F, 0x070F, 0x0000 }, /* R4202  - Write Sequencer 106 */
+	{4203, 0x010F, 0x010F, 0x0000 }, /* R4203  - Write Sequencer 107 */
+	{4204, 0x3FFF, 0x3FFF, 0x0000 }, /* R4204  - Write Sequencer 108 */
+	{4205, 0x00FF, 0x00FF, 0x0000 }, /* R4205  - Write Sequencer 109 */
+	{4206, 0x070F, 0x070F, 0x0000 }, /* R4206  - Write Sequencer 110 */
+	{4207, 0x010F, 0x010F, 0x0000 }, /* R4207  - Write Sequencer 111 */
+	{4208, 0x3FFF, 0x3FFF, 0x0000 }, /* R4208  - Write Sequencer 112 */
+	{4209, 0x00FF, 0x00FF, 0x0000 }, /* R4209  - Write Sequencer 113 */
+	{4210, 0x070F, 0x070F, 0x0000 }, /* R4210  - Write Sequencer 114 */
+	{4211, 0x010F, 0x010F, 0x0000 }, /* R4211  - Write Sequencer 115 */
+	{4212, 0x3FFF, 0x3FFF, 0x0000 }, /* R4212  - Write Sequencer 116 */
+	{4213, 0x00FF, 0x00FF, 0x0000 }, /* R4213  - Write Sequencer 117 */
+	{4214, 0x070F, 0x070F, 0x0000 }, /* R4214  - Write Sequencer 118 */
+	{4215, 0x010F, 0x010F, 0x0000 }, /* R4215  - Write Sequencer 119 */
+	{4216, 0x3FFF, 0x3FFF, 0x0000 }, /* R4216  - Write Sequencer 120 */
+	{4217, 0x00FF, 0x00FF, 0x0000 }, /* R4217  - Write Sequencer 121 */
+	{4218, 0x070F, 0x070F, 0x0000 }, /* R4218  - Write Sequencer 122 */
+	{4219, 0x010F, 0x010F, 0x0000 }, /* R4219  - Write Sequencer 123 */
+	{4220, 0x3FFF, 0x3FFF, 0x0000 }, /* R4220  - Write Sequencer 124 */
+	{4221, 0x00FF, 0x00FF, 0x0000 }, /* R4221  - Write Sequencer 125 */
+	{4222, 0x070F, 0x070F, 0x0000 }, /* R4222  - Write Sequencer 126 */
+	{4223, 0x010F, 0x010F, 0x0000 }, /* R4223  - Write Sequencer 127 */
+	{4224, 0x3FFF, 0x3FFF, 0x0000 }, /* R4224  - Write Sequencer 128 */
+	{4225, 0x00FF, 0x00FF, 0x0000 }, /* R4225  - Write Sequencer 129 */
+	{4226, 0x070F, 0x070F, 0x0000 }, /* R4226  - Write Sequencer 130 */
+	{4227, 0x010F, 0x010F, 0x0000 }, /* R4227  - Write Sequencer 131 */
+	{4228, 0x3FFF, 0x3FFF, 0x0000 }, /* R4228  - Write Sequencer 132 */
+	{4229, 0x00FF, 0x00FF, 0x0000 }, /* R4229  - Write Sequencer 133 */
+	{4230, 0x070F, 0x070F, 0x0000 }, /* R4230  - Write Sequencer 134 */
+	{4231, 0x010F, 0x010F, 0x0000 }, /* R4231  - Write Sequencer 135 */
+	{4232, 0x3FFF, 0x3FFF, 0x0000 }, /* R4232  - Write Sequencer 136 */
+	{4233, 0x00FF, 0x00FF, 0x0000 }, /* R4233  - Write Sequencer 137 */
+	{4234, 0x070F, 0x070F, 0x0000 }, /* R4234  - Write Sequencer 138 */
+	{4235, 0x010F, 0x010F, 0x0000 }, /* R4235  - Write Sequencer 139 */
+	{4236, 0x3FFF, 0x3FFF, 0x0000 }, /* R4236  - Write Sequencer 140 */
+	{4237, 0x00FF, 0x00FF, 0x0000 }, /* R4237  - Write Sequencer 141 */
+	{4238, 0x070F, 0x070F, 0x0000 }, /* R4238  - Write Sequencer 142 */
+	{4239, 0x010F, 0x010F, 0x0000 }, /* R4239  - Write Sequencer 143 */
+	{4240, 0x3FFF, 0x3FFF, 0x0000 }, /* R4240  - Write Sequencer 144 */
+	{4241, 0x00FF, 0x00FF, 0x0000 }, /* R4241  - Write Sequencer 145 */
+	{4242, 0x070F, 0x070F, 0x0000 }, /* R4242  - Write Sequencer 146 */
+	{4243, 0x010F, 0x010F, 0x0000 }, /* R4243  - Write Sequencer 147 */
+	{4244, 0x3FFF, 0x3FFF, 0x0000 }, /* R4244  - Write Sequencer 148 */
+	{4245, 0x00FF, 0x00FF, 0x0000 }, /* R4245  - Write Sequencer 149 */
+	{4246, 0x070F, 0x070F, 0x0000 }, /* R4246  - Write Sequencer 150 */
+	{4247, 0x010F, 0x010F, 0x0000 }, /* R4247  - Write Sequencer 151 */
+	{4248, 0x3FFF, 0x3FFF, 0x0000 }, /* R4248  - Write Sequencer 152 */
+	{4249, 0x00FF, 0x00FF, 0x0000 }, /* R4249  - Write Sequencer 153 */
+	{4250, 0x070F, 0x070F, 0x0000 }, /* R4250  - Write Sequencer 154 */
+	{4251, 0x010F, 0x010F, 0x0000 }, /* R4251  - Write Sequencer 155 */
+	{4252, 0x3FFF, 0x3FFF, 0x0000 }, /* R4252  - Write Sequencer 156 */
+	{4253, 0x00FF, 0x00FF, 0x0000 }, /* R4253  - Write Sequencer 157 */
+	{4254, 0x070F, 0x070F, 0x0000 }, /* R4254  - Write Sequencer 158 */
+	{4255, 0x010F, 0x010F, 0x0000 }, /* R4255  - Write Sequencer 159 */
+	{4256, 0x3FFF, 0x3FFF, 0x0000 }, /* R4256  - Write Sequencer 160 */
+	{4257, 0x00FF, 0x00FF, 0x0000 }, /* R4257  - Write Sequencer 161 */
+	{4258, 0x070F, 0x070F, 0x0000 }, /* R4258  - Write Sequencer 162 */
+	{4259, 0x010F, 0x010F, 0x0000 }, /* R4259  - Write Sequencer 163 */
+	{4260, 0x3FFF, 0x3FFF, 0x0000 }, /* R4260  - Write Sequencer 164 */
+	{4261, 0x00FF, 0x00FF, 0x0000 }, /* R4261  - Write Sequencer 165 */
+	{4262, 0x070F, 0x070F, 0x0000 }, /* R4262  - Write Sequencer 166 */
+	{4263, 0x010F, 0x010F, 0x0000 }, /* R4263  - Write Sequencer 167 */
+	{4264, 0x3FFF, 0x3FFF, 0x0000 }, /* R4264  - Write Sequencer 168 */
+	{4265, 0x00FF, 0x00FF, 0x0000 }, /* R4265  - Write Sequencer 169 */
+	{4266, 0x070F, 0x070F, 0x0000 }, /* R4266  - Write Sequencer 170 */
+	{4267, 0x010F, 0x010F, 0x0000 }, /* R4267  - Write Sequencer 171 */
+	{4268, 0x3FFF, 0x3FFF, 0x0000 }, /* R4268  - Write Sequencer 172 */
+	{4269, 0x00FF, 0x00FF, 0x0000 }, /* R4269  - Write Sequencer 173 */
+	{4270, 0x070F, 0x070F, 0x0000 }, /* R4270  - Write Sequencer 174 */
+	{4271, 0x010F, 0x010F, 0x0000 }, /* R4271  - Write Sequencer 175 */
+	{4272, 0x3FFF, 0x3FFF, 0x0000 }, /* R4272  - Write Sequencer 176 */
+	{4273, 0x00FF, 0x00FF, 0x0000 }, /* R4273  - Write Sequencer 177 */
+	{4274, 0x070F, 0x070F, 0x0000 }, /* R4274  - Write Sequencer 178 */
+	{4275, 0x010F, 0x010F, 0x0000 }, /* R4275  - Write Sequencer 179 */
+	{4276, 0x3FFF, 0x3FFF, 0x0000 }, /* R4276  - Write Sequencer 180 */
+	{4277, 0x00FF, 0x00FF, 0x0000 }, /* R4277  - Write Sequencer 181 */
+	{4278, 0x070F, 0x070F, 0x0000 }, /* R4278  - Write Sequencer 182 */
+	{4279, 0x010F, 0x010F, 0x0000 }, /* R4279  - Write Sequencer 183 */
+	{4280, 0x3FFF, 0x3FFF, 0x0000 }, /* R4280  - Write Sequencer 184 */
+	{4281, 0x00FF, 0x00FF, 0x0000 }, /* R4281  - Write Sequencer 185 */
+	{4282, 0x070F, 0x070F, 0x0000 }, /* R4282  - Write Sequencer 186 */
+	{4283, 0x010F, 0x010F, 0x0000 }, /* R4283  - Write Sequencer 187 */
+	{4284, 0x3FFF, 0x3FFF, 0x0000 }, /* R4284  - Write Sequencer 188 */
+	{4285, 0x00FF, 0x00FF, 0x0000 }, /* R4285  - Write Sequencer 189 */
+	{4286, 0x070F, 0x070F, 0x0000 }, /* R4286  - Write Sequencer 190 */
+	{4287, 0x010F, 0x010F, 0x0000 }, /* R4287  - Write Sequencer 191 */
+	{4288, 0x3FFF, 0x3FFF, 0x0000 }, /* R4288  - Write Sequencer 192 */
+	{4289, 0x00FF, 0x00FF, 0x0000 }, /* R4289  - Write Sequencer 193 */
+	{4290, 0x070F, 0x070F, 0x0000 }, /* R4290  - Write Sequencer 194 */
+	{4291, 0x010F, 0x010F, 0x0000 }, /* R4291  - Write Sequencer 195 */
+	{4292, 0x3FFF, 0x3FFF, 0x0000 }, /* R4292  - Write Sequencer 196 */
+	{4293, 0x00FF, 0x00FF, 0x0000 }, /* R4293  - Write Sequencer 197 */
+	{4294, 0x070F, 0x070F, 0x0000 }, /* R4294  - Write Sequencer 198 */
+	{4295, 0x010F, 0x010F, 0x0000 }, /* R4295  - Write Sequencer 199 */
+	{4296, 0x3FFF, 0x3FFF, 0x0000 }, /* R4296  - Write Sequencer 200 */
+	{4297, 0x00FF, 0x00FF, 0x0000 }, /* R4297  - Write Sequencer 201 */
+	{4298, 0x070F, 0x070F, 0x0000 }, /* R4298  - Write Sequencer 202 */
+	{4299, 0x010F, 0x010F, 0x0000 }, /* R4299  - Write Sequencer 203 */
+	{4300, 0x3FFF, 0x3FFF, 0x0000 }, /* R4300  - Write Sequencer 204 */
+	{4301, 0x00FF, 0x00FF, 0x0000 }, /* R4301  - Write Sequencer 205 */
+	{4302, 0x070F, 0x070F, 0x0000 }, /* R4302  - Write Sequencer 206 */
+	{4303, 0x010F, 0x010F, 0x0000 }, /* R4303  - Write Sequencer 207 */
+	{4304, 0x3FFF, 0x3FFF, 0x0000 }, /* R4304  - Write Sequencer 208 */
+	{4305, 0x00FF, 0x00FF, 0x0000 }, /* R4305  - Write Sequencer 209 */
+	{4306, 0x070F, 0x070F, 0x0000 }, /* R4306  - Write Sequencer 210 */
+	{4307, 0x010F, 0x010F, 0x0000 }, /* R4307  - Write Sequencer 211 */
+	{4308, 0x3FFF, 0x3FFF, 0x0000 }, /* R4308  - Write Sequencer 212 */
+	{4309, 0x00FF, 0x00FF, 0x0000 }, /* R4309  - Write Sequencer 213 */
+	{4310, 0x070F, 0x070F, 0x0000 }, /* R4310  - Write Sequencer 214 */
+	{4311, 0x010F, 0x010F, 0x0000 }, /* R4311  - Write Sequencer 215 */
+	{4312, 0x3FFF, 0x3FFF, 0x0000 }, /* R4312  - Write Sequencer 216 */
+	{4313, 0x00FF, 0x00FF, 0x0000 }, /* R4313  - Write Sequencer 217 */
+	{4314, 0x070F, 0x070F, 0x0000 }, /* R4314  - Write Sequencer 218 */
+	{4315, 0x010F, 0x010F, 0x0000 }, /* R4315  - Write Sequencer 219 */
+	{4316, 0x3FFF, 0x3FFF, 0x0000 }, /* R4316  - Write Sequencer 220 */
+	{4317, 0x00FF, 0x00FF, 0x0000 }, /* R4317  - Write Sequencer 221 */
+	{4318, 0x070F, 0x070F, 0x0000 }, /* R4318  - Write Sequencer 222 */
+	{4319, 0x010F, 0x010F, 0x0000 }, /* R4319  - Write Sequencer 223 */
+	{4320, 0x3FFF, 0x3FFF, 0x0000 }, /* R4320  - Write Sequencer 224 */
+	{4321, 0x00FF, 0x00FF, 0x0000 }, /* R4321  - Write Sequencer 225 */
+	{4322, 0x070F, 0x070F, 0x0000 }, /* R4322  - Write Sequencer 226 */
+	{4323, 0x010F, 0x010F, 0x0000 }, /* R4323  - Write Sequencer 227 */
+	{4324, 0x3FFF, 0x3FFF, 0x0000 }, /* R4324  - Write Sequencer 228 */
+	{4325, 0x00FF, 0x00FF, 0x0000 }, /* R4325  - Write Sequencer 229 */
+	{4326, 0x070F, 0x070F, 0x0000 }, /* R4326  - Write Sequencer 230 */
+	{4327, 0x010F, 0x010F, 0x0000 }, /* R4327  - Write Sequencer 231 */
+	{4328, 0x3FFF, 0x3FFF, 0x0000 }, /* R4328  - Write Sequencer 232 */
+	{4329, 0x00FF, 0x00FF, 0x0000 }, /* R4329  - Write Sequencer 233 */
+	{4330, 0x070F, 0x070F, 0x0000 }, /* R4330  - Write Sequencer 234 */
+	{4331, 0x010F, 0x010F, 0x0000 }, /* R4331  - Write Sequencer 235 */
+	{4332, 0x3FFF, 0x3FFF, 0x0000 }, /* R4332  - Write Sequencer 236 */
+	{4333, 0x00FF, 0x00FF, 0x0000 }, /* R4333  - Write Sequencer 237 */
+	{4334, 0x070F, 0x070F, 0x0000 }, /* R4334  - Write Sequencer 238 */
+	{4335, 0x010F, 0x010F, 0x0000 }, /* R4335  - Write Sequencer 239 */
+	{4336, 0x3FFF, 0x3FFF, 0x0000 }, /* R4336  - Write Sequencer 240 */
+	{4337, 0x00FF, 0x00FF, 0x0000 }, /* R4337  - Write Sequencer 241 */
+	{4338, 0x070F, 0x070F, 0x0000 }, /* R4338  - Write Sequencer 242 */
+	{4339, 0x010F, 0x010F, 0x0000 }, /* R4339  - Write Sequencer 243 */
+	{4340, 0x3FFF, 0x3FFF, 0x0000 }, /* R4340  - Write Sequencer 244 */
+	{4341, 0x00FF, 0x00FF, 0x0000 }, /* R4341  - Write Sequencer 245 */
+	{4342, 0x070F, 0x070F, 0x0000 }, /* R4342  - Write Sequencer 246 */
+	{4343, 0x010F, 0x010F, 0x0000 }, /* R4343  - Write Sequencer 247 */
+	{4344, 0x3FFF, 0x3FFF, 0x0000 }, /* R4344  - Write Sequencer 248 */
+	{4345, 0x00FF, 0x00FF, 0x0000 }, /* R4345  - Write Sequencer 249 */
+	{4346, 0x070F, 0x070F, 0x0000 }, /* R4346  - Write Sequencer 250 */
+	{4347, 0x010F, 0x010F, 0x0000 }, /* R4347  - Write Sequencer 251 */
+	{4348, 0x3FFF, 0x3FFF, 0x0000 }, /* R4348  - Write Sequencer 252 */
+	{4349, 0x00FF, 0x00FF, 0x0000 }, /* R4349  - Write Sequencer 253 */
+	{4350, 0x070F, 0x070F, 0x0000 }, /* R4350  - Write Sequencer 254 */
+	{4351, 0x010F, 0x010F, 0x0000 }, /* R4351  - Write Sequencer 255 */
+	{4352, 0x3FFF, 0x3FFF, 0x0000 }, /* R4352  - Write Sequencer 256 */
+	{4353, 0x00FF, 0x00FF, 0x0000 }, /* R4353  - Write Sequencer 257 */
+	{4354, 0x070F, 0x070F, 0x0000 }, /* R4354  - Write Sequencer 258 */
+	{4355, 0x010F, 0x010F, 0x0000 }, /* R4355  - Write Sequencer 259 */
+	{4356, 0x3FFF, 0x3FFF, 0x0000 }, /* R4356  - Write Sequencer 260 */
+	{4357, 0x00FF, 0x00FF, 0x0000 }, /* R4357  - Write Sequencer 261 */
+	{4358, 0x070F, 0x070F, 0x0000 }, /* R4358  - Write Sequencer 262 */
+	{4359, 0x010F, 0x010F, 0x0000 }, /* R4359  - Write Sequencer 263 */
+	{4360, 0x3FFF, 0x3FFF, 0x0000 }, /* R4360  - Write Sequencer 264 */
+	{4361, 0x00FF, 0x00FF, 0x0000 }, /* R4361  - Write Sequencer 265 */
+	{4362, 0x070F, 0x070F, 0x0000 }, /* R4362  - Write Sequencer 266 */
+	{4363, 0x010F, 0x010F, 0x0000 }, /* R4363  - Write Sequencer 267 */
+	{4364, 0x3FFF, 0x3FFF, 0x0000 }, /* R4364  - Write Sequencer 268 */
+	{4365, 0x00FF, 0x00FF, 0x0000 }, /* R4365  - Write Sequencer 269 */
+	{4366, 0x070F, 0x070F, 0x0000 }, /* R4366  - Write Sequencer 270 */
+	{4367, 0x010F, 0x010F, 0x0000 }, /* R4367  - Write Sequencer 271 */
+	{4368, 0x3FFF, 0x3FFF, 0x0000 }, /* R4368  - Write Sequencer 272 */
+	{4369, 0x00FF, 0x00FF, 0x0000 }, /* R4369  - Write Sequencer 273 */
+	{4370, 0x070F, 0x070F, 0x0000 }, /* R4370  - Write Sequencer 274 */
+	{4371, 0x010F, 0x010F, 0x0000 }, /* R4371  - Write Sequencer 275 */
+	{4372, 0x3FFF, 0x3FFF, 0x0000 }, /* R4372  - Write Sequencer 276 */
+	{4373, 0x00FF, 0x00FF, 0x0000 }, /* R4373  - Write Sequencer 277 */
+	{4374, 0x070F, 0x070F, 0x0000 }, /* R4374  - Write Sequencer 278 */
+	{4375, 0x010F, 0x010F, 0x0000 }, /* R4375  - Write Sequencer 279 */
+	{4376, 0x3FFF, 0x3FFF, 0x0000 }, /* R4376  - Write Sequencer 280 */
+	{4377, 0x00FF, 0x00FF, 0x0000 }, /* R4377  - Write Sequencer 281 */
+	{4378, 0x070F, 0x070F, 0x0000 }, /* R4378  - Write Sequencer 282 */
+	{4379, 0x010F, 0x010F, 0x0000 }, /* R4379  - Write Sequencer 283 */
+	{4380, 0x3FFF, 0x3FFF, 0x0000 }, /* R4380  - Write Sequencer 284 */
+	{4381, 0x00FF, 0x00FF, 0x0000 }, /* R4381  - Write Sequencer 285 */
+	{4382, 0x070F, 0x070F, 0x0000 }, /* R4382  - Write Sequencer 286 */
+	{4383, 0x010F, 0x010F, 0x0000 }, /* R4383  - Write Sequencer 287 */
+	{4384, 0x3FFF, 0x3FFF, 0x0000 }, /* R4384  - Write Sequencer 288 */
+	{4385, 0x00FF, 0x00FF, 0x0000 }, /* R4385  - Write Sequencer 289 */
+	{4386, 0x070F, 0x070F, 0x0000 }, /* R4386  - Write Sequencer 290 */
+	{4387, 0x010F, 0x010F, 0x0000 }, /* R4387  - Write Sequencer 291 */
+	{4388, 0x3FFF, 0x3FFF, 0x0000 }, /* R4388  - Write Sequencer 292 */
+	{4389, 0x00FF, 0x00FF, 0x0000 }, /* R4389  - Write Sequencer 293 */
+	{4390, 0x070F, 0x070F, 0x0000 }, /* R4390  - Write Sequencer 294 */
+	{4391, 0x010F, 0x010F, 0x0000 }, /* R4391  - Write Sequencer 295 */
+	{4392, 0x3FFF, 0x3FFF, 0x0000 }, /* R4392  - Write Sequencer 296 */
+	{4393, 0x00FF, 0x00FF, 0x0000 }, /* R4393  - Write Sequencer 297 */
+	{4394, 0x070F, 0x070F, 0x0000 }, /* R4394  - Write Sequencer 298 */
+	{4395, 0x010F, 0x010F, 0x0000 }, /* R4395  - Write Sequencer 299 */
+	{4396, 0x3FFF, 0x3FFF, 0x0000 }, /* R4396  - Write Sequencer 300 */
+	{4397, 0x00FF, 0x00FF, 0x0000 }, /* R4397  - Write Sequencer 301 */
+	{4398, 0x070F, 0x070F, 0x0000 }, /* R4398  - Write Sequencer 302 */
+	{4399, 0x010F, 0x010F, 0x0000 }, /* R4399  - Write Sequencer 303 */
+	{4400, 0x3FFF, 0x3FFF, 0x0000 }, /* R4400  - Write Sequencer 304 */
+	{4401, 0x00FF, 0x00FF, 0x0000 }, /* R4401  - Write Sequencer 305 */
+	{4402, 0x070F, 0x070F, 0x0000 }, /* R4402  - Write Sequencer 306 */
+	{4403, 0x010F, 0x010F, 0x0000 }, /* R4403  - Write Sequencer 307 */
+	{4404, 0x3FFF, 0x3FFF, 0x0000 }, /* R4404  - Write Sequencer 308 */
+	{4405, 0x00FF, 0x00FF, 0x0000 }, /* R4405  - Write Sequencer 309 */
+	{4406, 0x070F, 0x070F, 0x0000 }, /* R4406  - Write Sequencer 310 */
+	{4407, 0x010F, 0x010F, 0x0000 }, /* R4407  - Write Sequencer 311 */
+	{4408, 0x3FFF, 0x3FFF, 0x0000 }, /* R4408  - Write Sequencer 312 */
+	{4409, 0x00FF, 0x00FF, 0x0000 }, /* R4409  - Write Sequencer 313 */
+	{4410, 0x070F, 0x070F, 0x0000 }, /* R4410  - Write Sequencer 314 */
+	{4411, 0x010F, 0x010F, 0x0000 }, /* R4411  - Write Sequencer 315 */
+	{4412, 0x3FFF, 0x3FFF, 0x0000 }, /* R4412  - Write Sequencer 316 */
+	{4413, 0x00FF, 0x00FF, 0x0000 }, /* R4413  - Write Sequencer 317 */
+	{4414, 0x070F, 0x070F, 0x0000 }, /* R4414  - Write Sequencer 318 */
+	{4415, 0x010F, 0x010F, 0x0000 }, /* R4415  - Write Sequencer 319 */
+	{4416, 0x3FFF, 0x3FFF, 0x0000 }, /* R4416  - Write Sequencer 320 */
+	{4417, 0x00FF, 0x00FF, 0x0000 }, /* R4417  - Write Sequencer 321 */
+	{4418, 0x070F, 0x070F, 0x0000 }, /* R4418  - Write Sequencer 322 */
+	{4419, 0x010F, 0x010F, 0x0000 }, /* R4419  - Write Sequencer 323 */
+	{4420, 0x3FFF, 0x3FFF, 0x0000 }, /* R4420  - Write Sequencer 324 */
+	{4421, 0x00FF, 0x00FF, 0x0000 }, /* R4421  - Write Sequencer 325 */
+	{4422, 0x070F, 0x070F, 0x0000 }, /* R4422  - Write Sequencer 326 */
+	{4423, 0x010F, 0x010F, 0x0000 }, /* R4423  - Write Sequencer 327 */
+	{4424, 0x3FFF, 0x3FFF, 0x0000 }, /* R4424  - Write Sequencer 328 */
+	{4425, 0x00FF, 0x00FF, 0x0000 }, /* R4425  - Write Sequencer 329 */
+	{4426, 0x070F, 0x070F, 0x0000 }, /* R4426  - Write Sequencer 330 */
+	{4427, 0x010F, 0x010F, 0x0000 }, /* R4427  - Write Sequencer 331 */
+	{4428, 0x3FFF, 0x3FFF, 0x0000 }, /* R4428  - Write Sequencer 332 */
+	{4429, 0x00FF, 0x00FF, 0x0000 }, /* R4429  - Write Sequencer 333 */
+	{4430, 0x070F, 0x070F, 0x0000 }, /* R4430  - Write Sequencer 334 */
+	{4431, 0x010F, 0x010F, 0x0000 }, /* R4431  - Write Sequencer 335 */
+	{4432, 0x3FFF, 0x3FFF, 0x0000 }, /* R4432  - Write Sequencer 336 */
+	{4433, 0x00FF, 0x00FF, 0x0000 }, /* R4433  - Write Sequencer 337 */
+	{4434, 0x070F, 0x070F, 0x0000 }, /* R4434  - Write Sequencer 338 */
+	{4435, 0x010F, 0x010F, 0x0000 }, /* R4435  - Write Sequencer 339 */
+	{4436, 0x3FFF, 0x3FFF, 0x0000 }, /* R4436  - Write Sequencer 340 */
+	{4437, 0x00FF, 0x00FF, 0x0000 }, /* R4437  - Write Sequencer 341 */
+	{4438, 0x070F, 0x070F, 0x0000 }, /* R4438  - Write Sequencer 342 */
+	{4439, 0x010F, 0x010F, 0x0000 }, /* R4439  - Write Sequencer 343 */
+	{4440, 0x3FFF, 0x3FFF, 0x0000 }, /* R4440  - Write Sequencer 344 */
+	{4441, 0x00FF, 0x00FF, 0x0000 }, /* R4441  - Write Sequencer 345 */
+	{4442, 0x070F, 0x070F, 0x0000 }, /* R4442  - Write Sequencer 346 */
+	{4443, 0x010F, 0x010F, 0x0000 }, /* R4443  - Write Sequencer 347 */
+	{4444, 0x3FFF, 0x3FFF, 0x0000 }, /* R4444  - Write Sequencer 348 */
+	{4445, 0x00FF, 0x00FF, 0x0000 }, /* R4445  - Write Sequencer 349 */
+	{4446, 0x070F, 0x070F, 0x0000 }, /* R4446  - Write Sequencer 350 */
+	{4447, 0x010F, 0x010F, 0x0000 }, /* R4447  - Write Sequencer 351 */
+	{4448, 0x3FFF, 0x3FFF, 0x0000 }, /* R4448  - Write Sequencer 352 */
+	{4449, 0x00FF, 0x00FF, 0x0000 }, /* R4449  - Write Sequencer 353 */
+	{4450, 0x070F, 0x070F, 0x0000 }, /* R4450  - Write Sequencer 354 */
+	{4451, 0x010F, 0x010F, 0x0000 }, /* R4451  - Write Sequencer 355 */
+	{4452, 0x3FFF, 0x3FFF, 0x0000 }, /* R4452  - Write Sequencer 356 */
+	{4453, 0x00FF, 0x00FF, 0x0000 }, /* R4453  - Write Sequencer 357 */
+	{4454, 0x070F, 0x070F, 0x0000 }, /* R4454  - Write Sequencer 358 */
+	{4455, 0x010F, 0x010F, 0x0000 }, /* R4455  - Write Sequencer 359 */
+	{4456, 0x3FFF, 0x3FFF, 0x0000 }, /* R4456  - Write Sequencer 360 */
+	{4457, 0x00FF, 0x00FF, 0x0000 }, /* R4457  - Write Sequencer 361 */
+	{4458, 0x070F, 0x070F, 0x0000 }, /* R4458  - Write Sequencer 362 */
+	{4459, 0x010F, 0x010F, 0x0000 }, /* R4459  - Write Sequencer 363 */
+	{4460, 0x3FFF, 0x3FFF, 0x0000 }, /* R4460  - Write Sequencer 364 */
+	{4461, 0x00FF, 0x00FF, 0x0000 }, /* R4461  - Write Sequencer 365 */
+	{4462, 0x070F, 0x070F, 0x0000 }, /* R4462  - Write Sequencer 366 */
+	{4463, 0x010F, 0x010F, 0x0000 }, /* R4463  - Write Sequencer 367 */
+	{4464, 0x3FFF, 0x3FFF, 0x0000 }, /* R4464  - Write Sequencer 368 */
+	{4465, 0x00FF, 0x00FF, 0x0000 }, /* R4465  - Write Sequencer 369 */
+	{4466, 0x070F, 0x070F, 0x0000 }, /* R4466  - Write Sequencer 370 */
+	{4467, 0x010F, 0x010F, 0x0000 }, /* R4467  - Write Sequencer 371 */
+	{4468, 0x3FFF, 0x3FFF, 0x0000 }, /* R4468  - Write Sequencer 372 */
+	{4469, 0x00FF, 0x00FF, 0x0000 }, /* R4469  - Write Sequencer 373 */
+	{4470, 0x070F, 0x070F, 0x0000 }, /* R4470  - Write Sequencer 374 */
+	{4471, 0x010F, 0x010F, 0x0000 }, /* R4471  - Write Sequencer 375 */
+	{4472, 0x3FFF, 0x3FFF, 0x0000 }, /* R4472  - Write Sequencer 376 */
+	{4473, 0x00FF, 0x00FF, 0x0000 }, /* R4473  - Write Sequencer 377 */
+	{4474, 0x070F, 0x070F, 0x0000 }, /* R4474  - Write Sequencer 378 */
+	{4475, 0x010F, 0x010F, 0x0000 }, /* R4475  - Write Sequencer 379 */
+	{4476, 0x3FFF, 0x3FFF, 0x0000 }, /* R4476  - Write Sequencer 380 */
+	{4477, 0x00FF, 0x00FF, 0x0000 }, /* R4477  - Write Sequencer 381 */
+	{4478, 0x070F, 0x070F, 0x0000 }, /* R4478  - Write Sequencer 382 */
+	{4479, 0x010F, 0x010F, 0x0000 }, /* R4479  - Write Sequencer 383 */
+	{4480, 0x3FFF, 0x3FFF, 0x0000 }, /* R4480  - Write Sequencer 384 */
+	{4481, 0x00FF, 0x00FF, 0x0000 }, /* R4481  - Write Sequencer 385 */
+	{4482, 0x070F, 0x070F, 0x0000 }, /* R4482  - Write Sequencer 386 */
+	{4483, 0x010F, 0x010F, 0x0000 }, /* R4483  - Write Sequencer 387 */
+	{4484, 0x3FFF, 0x3FFF, 0x0000 }, /* R4484  - Write Sequencer 388 */
+	{4485, 0x00FF, 0x00FF, 0x0000 }, /* R4485  - Write Sequencer 389 */
+	{4486, 0x070F, 0x070F, 0x0000 }, /* R4486  - Write Sequencer 390 */
+	{4487, 0x010F, 0x010F, 0x0000 }, /* R4487  - Write Sequencer 391 */
+	{4488, 0x3FFF, 0x3FFF, 0x0000 }, /* R4488  - Write Sequencer 392 */
+	{4489, 0x00FF, 0x00FF, 0x0000 }, /* R4489  - Write Sequencer 393 */
+	{4490, 0x070F, 0x070F, 0x0000 }, /* R4490  - Write Sequencer 394 */
+	{4491, 0x010F, 0x010F, 0x0000 }, /* R4491  - Write Sequencer 395 */
+	{4492, 0x3FFF, 0x3FFF, 0x0000 }, /* R4492  - Write Sequencer 396 */
+	{4493, 0x00FF, 0x00FF, 0x0000 }, /* R4493  - Write Sequencer 397 */
+	{4494, 0x070F, 0x070F, 0x0000 }, /* R4494  - Write Sequencer 398 */
+	{4495, 0x010F, 0x010F, 0x0000 }, /* R4495  - Write Sequencer 399 */
+	{4496, 0x3FFF, 0x3FFF, 0x0000 }, /* R4496  - Write Sequencer 400 */
+	{4497, 0x00FF, 0x00FF, 0x0000 }, /* R4497  - Write Sequencer 401 */
+	{4498, 0x070F, 0x070F, 0x0000 }, /* R4498  - Write Sequencer 402 */
+	{4499, 0x010F, 0x010F, 0x0000 }, /* R4499  - Write Sequencer 403 */
+	{4500, 0x3FFF, 0x3FFF, 0x0000 }, /* R4500  - Write Sequencer 404 */
+	{4501, 0x00FF, 0x00FF, 0x0000 }, /* R4501  - Write Sequencer 405 */
+	{4502, 0x070F, 0x070F, 0x0000 }, /* R4502  - Write Sequencer 406 */
+	{4503, 0x010F, 0x010F, 0x0000 }, /* R4503  - Write Sequencer 407 */
+	{4504, 0x3FFF, 0x3FFF, 0x0000 }, /* R4504  - Write Sequencer 408 */
+	{4505, 0x00FF, 0x00FF, 0x0000 }, /* R4505  - Write Sequencer 409 */
+	{4506, 0x070F, 0x070F, 0x0000 }, /* R4506  - Write Sequencer 410 */
+	{4507, 0x010F, 0x010F, 0x0000 }, /* R4507  - Write Sequencer 411 */
+	{4508, 0x3FFF, 0x3FFF, 0x0000 }, /* R4508  - Write Sequencer 412 */
+	{4509, 0x00FF, 0x00FF, 0x0000 }, /* R4509  - Write Sequencer 413 */
+	{4510, 0x070F, 0x070F, 0x0000 }, /* R4510  - Write Sequencer 414 */
+	{4511, 0x010F, 0x010F, 0x0000 }, /* R4511  - Write Sequencer 415 */
+	{4512, 0x3FFF, 0x3FFF, 0x0000 }, /* R4512  - Write Sequencer 416 */
+	{4513, 0x00FF, 0x00FF, 0x0000 }, /* R4513  - Write Sequencer 417 */
+	{4514, 0x070F, 0x070F, 0x0000 }, /* R4514  - Write Sequencer 418 */
+	{4515, 0x010F, 0x010F, 0x0000 }, /* R4515  - Write Sequencer 419 */
+	{4516, 0x3FFF, 0x3FFF, 0x0000 }, /* R4516  - Write Sequencer 420 */
+	{4517, 0x00FF, 0x00FF, 0x0000 }, /* R4517  - Write Sequencer 421 */
+	{4518, 0x070F, 0x070F, 0x0000 }, /* R4518  - Write Sequencer 422 */
+	{4519, 0x010F, 0x010F, 0x0000 }, /* R4519  - Write Sequencer 423 */
+	{4520, 0x3FFF, 0x3FFF, 0x0000 }, /* R4520  - Write Sequencer 424 */
+	{4521, 0x00FF, 0x00FF, 0x0000 }, /* R4521  - Write Sequencer 425 */
+	{4522, 0x070F, 0x070F, 0x0000 }, /* R4522  - Write Sequencer 426 */
+	{4523, 0x010F, 0x010F, 0x0000 }, /* R4523  - Write Sequencer 427 */
+	{4524, 0x3FFF, 0x3FFF, 0x0000 }, /* R4524  - Write Sequencer 428 */
+	{4525, 0x00FF, 0x00FF, 0x0000 }, /* R4525  - Write Sequencer 429 */
+	{4526, 0x070F, 0x070F, 0x0000 }, /* R4526  - Write Sequencer 430 */
+	{4527, 0x010F, 0x010F, 0x0000 }, /* R4527  - Write Sequencer 431 */
+	{4528, 0x3FFF, 0x3FFF, 0x0000 }, /* R4528  - Write Sequencer 432 */
+	{4529, 0x00FF, 0x00FF, 0x0000 }, /* R4529  - Write Sequencer 433 */
+	{4530, 0x070F, 0x070F, 0x0000 }, /* R4530  - Write Sequencer 434 */
+	{4531, 0x010F, 0x010F, 0x0000 }, /* R4531  - Write Sequencer 435 */
+	{4532, 0x3FFF, 0x3FFF, 0x0000 }, /* R4532  - Write Sequencer 436 */
+	{4533, 0x00FF, 0x00FF, 0x0000 }, /* R4533  - Write Sequencer 437 */
+	{4534, 0x070F, 0x070F, 0x0000 }, /* R4534  - Write Sequencer 438 */
+	{4535, 0x010F, 0x010F, 0x0000 }, /* R4535  - Write Sequencer 439 */
+	{4536, 0x3FFF, 0x3FFF, 0x0000 }, /* R4536  - Write Sequencer 440 */
+	{4537, 0x00FF, 0x00FF, 0x0000 }, /* R4537  - Write Sequencer 441 */
+	{4538, 0x070F, 0x070F, 0x0000 }, /* R4538  - Write Sequencer 442 */
+	{4539, 0x010F, 0x010F, 0x0000 }, /* R4539  - Write Sequencer 443 */
+	{4540, 0x3FFF, 0x3FFF, 0x0000 }, /* R4540  - Write Sequencer 444 */
+	{4541, 0x00FF, 0x00FF, 0x0000 }, /* R4541  - Write Sequencer 445 */
+	{4542, 0x070F, 0x070F, 0x0000 }, /* R4542  - Write Sequencer 446 */
+	{4543, 0x010F, 0x010F, 0x0000 }, /* R4543  - Write Sequencer 447 */
+	{4544, 0x3FFF, 0x3FFF, 0x0000 }, /* R4544  - Write Sequencer 448 */
+	{4545, 0x00FF, 0x00FF, 0x0000 }, /* R4545  - Write Sequencer 449 */
+	{4546, 0x070F, 0x070F, 0x0000 }, /* R4546  - Write Sequencer 450 */
+	{4547, 0x010F, 0x010F, 0x0000 }, /* R4547  - Write Sequencer 451 */
+	{4548, 0x3FFF, 0x3FFF, 0x0000 }, /* R4548  - Write Sequencer 452 */
+	{4549, 0x00FF, 0x00FF, 0x0000 }, /* R4549  - Write Sequencer 453 */
+	{4550, 0x070F, 0x070F, 0x0000 }, /* R4550  - Write Sequencer 454 */
+	{4551, 0x010F, 0x010F, 0x0000 }, /* R4551  - Write Sequencer 455 */
+	{4552, 0x3FFF, 0x3FFF, 0x0000 }, /* R4552  - Write Sequencer 456 */
+	{4553, 0x00FF, 0x00FF, 0x0000 }, /* R4553  - Write Sequencer 457 */
+	{4554, 0x070F, 0x070F, 0x0000 }, /* R4554  - Write Sequencer 458 */
+	{4555, 0x010F, 0x010F, 0x0000 }, /* R4555  - Write Sequencer 459 */
+	{4556, 0x3FFF, 0x3FFF, 0x0000 }, /* R4556  - Write Sequencer 460 */
+	{4557, 0x00FF, 0x00FF, 0x0000 }, /* R4557  - Write Sequencer 461 */
+	{4558, 0x070F, 0x070F, 0x0000 }, /* R4558  - Write Sequencer 462 */
+	{4559, 0x010F, 0x010F, 0x0000 }, /* R4559  - Write Sequencer 463 */
+	{4560, 0x3FFF, 0x3FFF, 0x0000 }, /* R4560  - Write Sequencer 464 */
+	{4561, 0x00FF, 0x00FF, 0x0000 }, /* R4561  - Write Sequencer 465 */
+	{4562, 0x070F, 0x070F, 0x0000 }, /* R4562  - Write Sequencer 466 */
+	{4563, 0x010F, 0x010F, 0x0000 }, /* R4563  - Write Sequencer 467 */
+	{4564, 0x3FFF, 0x3FFF, 0x0000 }, /* R4564  - Write Sequencer 468 */
+	{4565, 0x00FF, 0x00FF, 0x0000 }, /* R4565  - Write Sequencer 469 */
+	{4566, 0x070F, 0x070F, 0x0000 }, /* R4566  - Write Sequencer 470 */
+	{4567, 0x010F, 0x010F, 0x0000 }, /* R4567  - Write Sequencer 471 */
+	{4568, 0x3FFF, 0x3FFF, 0x0000 }, /* R4568  - Write Sequencer 472 */
+	{4569, 0x00FF, 0x00FF, 0x0000 }, /* R4569  - Write Sequencer 473 */
+	{4570, 0x070F, 0x070F, 0x0000 }, /* R4570  - Write Sequencer 474 */
+	{4571, 0x010F, 0x010F, 0x0000 }, /* R4571  - Write Sequencer 475 */
+	{4572, 0x3FFF, 0x3FFF, 0x0000 }, /* R4572  - Write Sequencer 476 */
+	{4573, 0x00FF, 0x00FF, 0x0000 }, /* R4573  - Write Sequencer 477 */
+	{4574, 0x070F, 0x070F, 0x0000 }, /* R4574  - Write Sequencer 478 */
+	{4575, 0x010F, 0x010F, 0x0000 }, /* R4575  - Write Sequencer 479 */
+	{4576, 0x3FFF, 0x3FFF, 0x0000 }, /* R4576  - Write Sequencer 480 */
+	{4577, 0x00FF, 0x00FF, 0x0000 }, /* R4577  - Write Sequencer 481 */
+	{4578, 0x070F, 0x070F, 0x0000 }, /* R4578  - Write Sequencer 482 */
+	{4579, 0x010F, 0x010F, 0x0000 }, /* R4579  - Write Sequencer 483 */
+	{4580, 0x3FFF, 0x3FFF, 0x0000 }, /* R4580  - Write Sequencer 484 */
+	{4581, 0x00FF, 0x00FF, 0x0000 }, /* R4581  - Write Sequencer 485 */
+	{4582, 0x070F, 0x070F, 0x0000 }, /* R4582  - Write Sequencer 486 */
+	{4583, 0x010F, 0x010F, 0x0000 }, /* R4583  - Write Sequencer 487 */
+	{4584, 0x3FFF, 0x3FFF, 0x0000 }, /* R4584  - Write Sequencer 488 */
+	{4585, 0x00FF, 0x00FF, 0x0000 }, /* R4585  - Write Sequencer 489 */
+	{4586, 0x070F, 0x070F, 0x0000 }, /* R4586  - Write Sequencer 490 */
+	{4587, 0x010F, 0x010F, 0x0000 }, /* R4587  - Write Sequencer 491 */
+	{4588, 0x3FFF, 0x3FFF, 0x0000 }, /* R4588  - Write Sequencer 492 */
+	{4589, 0x00FF, 0x00FF, 0x0000 }, /* R4589  - Write Sequencer 493 */
+	{4590, 0x070F, 0x070F, 0x0000 }, /* R4590  - Write Sequencer 494 */
+	{4591, 0x010F, 0x010F, 0x0000 }, /* R4591  - Write Sequencer 495 */
+	{4592, 0x3FFF, 0x3FFF, 0x0000 }, /* R4592  - Write Sequencer 496 */
+	{4593, 0x00FF, 0x00FF, 0x0000 }, /* R4593  - Write Sequencer 497 */
+	{4594, 0x070F, 0x070F, 0x0000 }, /* R4594  - Write Sequencer 498 */
+	{4595, 0x010F, 0x010F, 0x0000 }, /* R4595  - Write Sequencer 499 */
+	{4596, 0x3FFF, 0x3FFF, 0x0000 }, /* R4596  - Write Sequencer 500 */
+	{4597, 0x00FF, 0x00FF, 0x0000 }, /* R4597  - Write Sequencer 501 */
+	{4598, 0x070F, 0x070F, 0x0000 }, /* R4598  - Write Sequencer 502 */
+	{4599, 0x010F, 0x010F, 0x0000 }, /* R4599  - Write Sequencer 503 */
+	{4600, 0x3FFF, 0x3FFF, 0x0000 }, /* R4600  - Write Sequencer 504 */
+	{4601, 0x00FF, 0x00FF, 0x0000 }, /* R4601  - Write Sequencer 505 */
+	{4602, 0x070F, 0x070F, 0x0000 }, /* R4602  - Write Sequencer 506 */
+	{4603, 0x010F, 0x010F, 0x0000 }, /* R4603  - Write Sequencer 507 */
+	{4604, 0x3FFF, 0x3FFF, 0x0000 }, /* R4604  - Write Sequencer 508 */
+	{4605, 0x00FF, 0x00FF, 0x0000 }, /* R4605  - Write Sequencer 509 */
+	{4606, 0x070F, 0x070F, 0x0000 }, /* R4606  - Write Sequencer 510 */
+	{4607, 0x010F, 0x010F, 0x0000 }, /* R4607  - Write Sequencer 511 */
+	{8192, 0x03FF, 0x03FF, 0x0000 }, /* R8192  - DSP2 Instruction RAM 0 */
+	{9216, 0x003F, 0x003F, 0x0000 }, /* R9216  - DSP2 Address RAM 2 */
+	{9217, 0xFFFF, 0xFFFF, 0x0000 }, /* R9217  - DSP2 Address RAM 1 */
+	{9218, 0xFFFF, 0xFFFF, 0x0000 }, /* R9218  - DSP2 Address RAM 0 */
+	{12288, 0x00FF, 0x00FF, 0x0000 }, /* R12288 - DSP2 Data1 RAM 1 */
+	{12289, 0xFFFF, 0xFFFF, 0x0000 }, /* R12289 - DSP2 Data1 RAM 0 */
+	{13312, 0x00FF, 0x00FF, 0x0000 }, /* R13312 - DSP2 Data2 RAM 1 */
+	{13313, 0xFFFF, 0xFFFF, 0x0000 }, /* R13313 - DSP2 Data2 RAM 0 */
+	{14336, 0x00FF, 0x00FF, 0x0000 }, /* R14336 - DSP2 Data3 RAM 1 */
+	{14337, 0xFFFF, 0xFFFF, 0x0000 }, /* R14337 - DSP2 Data3 RAM 0 */
+	{15360, 0x07FF, 0x07FF, 0x0000 }, /* R15360 - DSP2 Coeff RAM 0 */
+	{16384, 0x00FF, 0x00FF, 0x0000 }, /* R16384 - RETUNEADC_SHARED_COEFF_1*/
+	{16385, 0xFFFF, 0xFFFF, 0x0000 }, /* R16385 - RETUNEADC_SHARED_COEFF_0 */
+	{16386, 0x00FF, 0x00FF, 0x0000 }, /* R16386 - RETUNEDAC_SHARED_COEFF_1 */
+	{16387, 0xFFFF, 0xFFFF, 0x0000 }, /* R16387 - RETUNEDAC_SHARED_COEFF_0 */
+	{16388, 0x00FF, 0x00FF, 0x0000 }, /* R16388 - SOUNDSTAGE_ENABLES_1 */
+	{16389, 0xFFFF, 0xFFFF, 0x0000 }, /* R16389 - SOUNDSTAGE_ENABLES_0 */
+	{16896, 0x00FF, 0x00FF, 0x0000 }, /* R16896 - HDBASS_AI_1 */
+	{16897, 0xFFFF, 0xFFFF, 0x0000 }, /* R16897 - HDBASS_AI_0 */
+	{16898, 0x00FF, 0x00FF, 0x0000 }, /* R16898 - HDBASS_AR_1 */
+	{16899, 0xFFFF, 0xFFFF, 0x0000 }, /* R16899 - HDBASS_AR_0 */
+	{16900, 0x00FF, 0x00FF, 0x0000 }, /* R16900 - HDBASS_B_1 */
+	{16901, 0xFFFF, 0xFFFF, 0x0000 }, /* R16901 - HDBASS_B_0 */
+	{16902, 0x00FF, 0x00FF, 0x0000 }, /* R16902 - HDBASS_K_1 */
+	{16903, 0xFFFF, 0xFFFF, 0x0000 }, /* R16903 - HDBASS_K_0 */
+	{16904, 0x00FF, 0x00FF, 0x0000 }, /* R16904 - HDBASS_N1_1 */
+	{16905, 0xFFFF, 0xFFFF, 0x0000 }, /* R16905 - HDBASS_N1_0 */
+	{16906, 0x00FF, 0x00FF, 0x0000 }, /* R16906 - HDBASS_N2_1 */
+	{16907, 0xFFFF, 0xFFFF, 0x0000 }, /* R16907 - HDBASS_N2_0 */
+	{16908, 0x00FF, 0x00FF, 0x0000 }, /* R16908 - HDBASS_N3_1 */
+	{16909, 0xFFFF, 0xFFFF, 0x0000 }, /* R16909 - HDBASS_N3_0 */
+	{16910, 0x00FF, 0x00FF, 0x0000 }, /* R16910 - HDBASS_N4_1 */
+	{16911, 0xFFFF, 0xFFFF, 0x0000 }, /* R16911 - HDBASS_N4_0 */
+	{16912, 0x00FF, 0x00FF, 0x0000 }, /* R16912 - HDBASS_N5_1 */
+	{16913, 0xFFFF, 0xFFFF, 0x0000 }, /* R16913 - HDBASS_N5_0 */
+	{16914, 0x00FF, 0x00FF, 0x0000 }, /* R16914 - HDBASS_X1_1 */
+	{16915, 0xFFFF, 0xFFFF, 0x0000 }, /* R16915 - HDBASS_X1_0 */
+	{16916, 0x00FF, 0x00FF, 0x0000 }, /* R16916 - HDBASS_X2_1 */
+	{16917, 0xFFFF, 0xFFFF, 0x0000 }, /* R16917 - HDBASS_X2_0 */
+	{16918, 0x00FF, 0x00FF, 0x0000 }, /* R16918 - HDBASS_X3_1 */
+	{16919, 0xFFFF, 0xFFFF, 0x0000 }, /* R16919 - HDBASS_X3_0 */
+	{16920, 0x00FF, 0x00FF, 0x0000 }, /* R16920 - HDBASS_ATK_1 */
+	{16921, 0xFFFF, 0xFFFF, 0x0000 }, /* R16921 - HDBASS_ATK_0 */
+	{16922, 0x00FF, 0x00FF, 0x0000 }, /* R16922 - HDBASS_DCY_1 */
+	{16923, 0xFFFF, 0xFFFF, 0x0000 }, /* R16923 - HDBASS_DCY_0 */
+	{16924, 0x00FF, 0x00FF, 0x0000 }, /* R16924 - HDBASS_PG_1 */
+	{16925, 0xFFFF, 0xFFFF, 0x0000 }, /* R16925 - HDBASS_PG_0 */
+	{17408, 0x00FF, 0x00FF, 0x0000 }, /* R17408 - HPF_C_1 */
+	{17409, 0xFFFF, 0xFFFF, 0x0000 }, /* R17409 - HPF_C_0 */
+	{17920, 0x00FF, 0x00FF, 0x0000 }, /* R17920 - ADCL_RETUNE_C1_1 */
+	{17921, 0xFFFF, 0xFFFF, 0x0000 }, /* R17921 - ADCL_RETUNE_C1_0 */
+	{17922, 0x00FF, 0x00FF, 0x0000 }, /* R17922 - ADCL_RETUNE_C2_1 */
+	{17923, 0xFFFF, 0xFFFF, 0x0000 }, /* R17923 - ADCL_RETUNE_C2_0 */
+	{17924, 0x00FF, 0x00FF, 0x0000 }, /* R17924 - ADCL_RETUNE_C3_1 */
+	{17925, 0xFFFF, 0xFFFF, 0x0000 }, /* R17925 - ADCL_RETUNE_C3_0 */
+	{17926, 0x00FF, 0x00FF, 0x0000 }, /* R17926 - ADCL_RETUNE_C4_1 */
+	{17927, 0xFFFF, 0xFFFF, 0x0000 }, /* R17927 - ADCL_RETUNE_C4_0 */
+	{17928, 0x00FF, 0x00FF, 0x0000 }, /* R17928 - ADCL_RETUNE_C5_1 */
+	{17929, 0xFFFF, 0xFFFF, 0x0000 }, /* R17929 - ADCL_RETUNE_C5_0 */
+	{17930, 0x00FF, 0x00FF, 0x0000 }, /* R17930 - ADCL_RETUNE_C6_1 */
+	{17931, 0xFFFF, 0xFFFF, 0x0000 }, /* R17931 - ADCL_RETUNE_C6_0 */
+	{17932, 0x00FF, 0x00FF, 0x0000 }, /* R17932 - ADCL_RETUNE_C7_1 */
+	{17933, 0xFFFF, 0xFFFF, 0x0000 }, /* R17933 - ADCL_RETUNE_C7_0 */
+	{17934, 0x00FF, 0x00FF, 0x0000 }, /* R17934 - ADCL_RETUNE_C8_1 */
+	{17935, 0xFFFF, 0xFFFF, 0x0000 }, /* R17935 - ADCL_RETUNE_C8_0 */
+	{17936, 0x00FF, 0x00FF, 0x0000 }, /* R17936 - ADCL_RETUNE_C9_1 */
+	{17937, 0xFFFF, 0xFFFF, 0x0000 }, /* R17937 - ADCL_RETUNE_C9_0 */
+	{17938, 0x00FF, 0x00FF, 0x0000 }, /* R17938 - ADCL_RETUNE_C10_1 */
+	{17939, 0xFFFF, 0xFFFF, 0x0000 }, /* R17939 - ADCL_RETUNE_C10_0 */
+	{17940, 0x00FF, 0x00FF, 0x0000 }, /* R17940 - ADCL_RETUNE_C11_1 */
+	{17941, 0xFFFF, 0xFFFF, 0x0000 }, /* R17941 - ADCL_RETUNE_C11_0 */
+	{17942, 0x00FF, 0x00FF, 0x0000 }, /* R17942 - ADCL_RETUNE_C12_1 */
+	{17943, 0xFFFF, 0xFFFF, 0x0000 }, /* R17943 - ADCL_RETUNE_C12_0 */
+	{17944, 0x00FF, 0x00FF, 0x0000 }, /* R17944 - ADCL_RETUNE_C13_1 */
+	{17945, 0xFFFF, 0xFFFF, 0x0000 }, /* R17945 - ADCL_RETUNE_C13_0 */
+	{17946, 0x00FF, 0x00FF, 0x0000 }, /* R17946 - ADCL_RETUNE_C14_1 */
+	{17947, 0xFFFF, 0xFFFF, 0x0000 }, /* R17947 - ADCL_RETUNE_C14_0 */
+	{17948, 0x00FF, 0x00FF, 0x0000 }, /* R17948 - ADCL_RETUNE_C15_1 */
+	{17949, 0xFFFF, 0xFFFF, 0x0000 }, /* R17949 - ADCL_RETUNE_C15_0 */
+	{17950, 0x00FF, 0x00FF, 0x0000 }, /* R17950 - ADCL_RETUNE_C16_1 */
+	{17951, 0xFFFF, 0xFFFF, 0x0000 }, /* R17951 - ADCL_RETUNE_C16_0 */
+	{17952, 0x00FF, 0x00FF, 0x0000 }, /* R17952 - ADCL_RETUNE_C17_1 */
+	{17953, 0xFFFF, 0xFFFF, 0x0000 }, /* R17953 - ADCL_RETUNE_C17_0 */
+	{17954, 0x00FF, 0x00FF, 0x0000 }, /* R17954 - ADCL_RETUNE_C18_1 */
+	{17955, 0xFFFF, 0xFFFF, 0x0000 }, /* R17955 - ADCL_RETUNE_C18_0 */
+	{17956, 0x00FF, 0x00FF, 0x0000 }, /* R17956 - ADCL_RETUNE_C19_1 */
+	{17957, 0xFFFF, 0xFFFF, 0x0000 }, /* R17957 - ADCL_RETUNE_C19_0 */
+	{17958, 0x00FF, 0x00FF, 0x0000 }, /* R17958 - ADCL_RETUNE_C20_1 */
+	{17959, 0xFFFF, 0xFFFF, 0x0000 }, /* R17959 - ADCL_RETUNE_C20_0 */
+	{17960, 0x00FF, 0x00FF, 0x0000 }, /* R17960 - ADCL_RETUNE_C21_1 */
+	{17961, 0xFFFF, 0xFFFF, 0x0000 }, /* R17961 - ADCL_RETUNE_C21_0 */
+	{17962, 0x00FF, 0x00FF, 0x0000 }, /* R17962 - ADCL_RETUNE_C22_1 */
+	{17963, 0xFFFF, 0xFFFF, 0x0000 }, /* R17963 - ADCL_RETUNE_C22_0 */
+	{17964, 0x00FF, 0x00FF, 0x0000 }, /* R17964 - ADCL_RETUNE_C23_1 */
+	{17965, 0xFFFF, 0xFFFF, 0x0000 }, /* R17965 - ADCL_RETUNE_C23_0 */
+	{17966, 0x00FF, 0x00FF, 0x0000 }, /* R17966 - ADCL_RETUNE_C24_1 */
+	{17967, 0xFFFF, 0xFFFF, 0x0000 }, /* R17967 - ADCL_RETUNE_C24_0 */
+	{17968, 0x00FF, 0x00FF, 0x0000 }, /* R17968 - ADCL_RETUNE_C25_1 */
+	{17969, 0xFFFF, 0xFFFF, 0x0000 }, /* R17969 - ADCL_RETUNE_C25_0 */
+	{17970, 0x00FF, 0x00FF, 0x0000 }, /* R17970 - ADCL_RETUNE_C26_1 */
+	{17971, 0xFFFF, 0xFFFF, 0x0000 }, /* R17971 - ADCL_RETUNE_C26_0 */
+	{17972, 0x00FF, 0x00FF, 0x0000 }, /* R17972 - ADCL_RETUNE_C27_1 */
+	{17973, 0xFFFF, 0xFFFF, 0x0000 }, /* R17973 - ADCL_RETUNE_C27_0 */
+	{17974, 0x00FF, 0x00FF, 0x0000 }, /* R17974 - ADCL_RETUNE_C28_1 */
+	{17975, 0xFFFF, 0xFFFF, 0x0000 }, /* R17975 - ADCL_RETUNE_C28_0 */
+	{17976, 0x00FF, 0x00FF, 0x0000 }, /* R17976 - ADCL_RETUNE_C29_1 */
+	{17977, 0xFFFF, 0xFFFF, 0x0000 }, /* R17977 - ADCL_RETUNE_C29_0 */
+	{17978, 0x00FF, 0x00FF, 0x0000 }, /* R17978 - ADCL_RETUNE_C30_1 */
+	{17979, 0xFFFF, 0xFFFF, 0x0000 }, /* R17979 - ADCL_RETUNE_C30_0 */
+	{17980, 0x00FF, 0x00FF, 0x0000 }, /* R17980 - ADCL_RETUNE_C31_1 */
+	{17981, 0xFFFF, 0xFFFF, 0x0000 }, /* R17981 - ADCL_RETUNE_C31_0 */
+	{17982, 0x00FF, 0x00FF, 0x0000 }, /* R17982 - ADCL_RETUNE_C32_1 */
+	{17983, 0xFFFF, 0xFFFF, 0x0000 }, /* R17983 - ADCL_RETUNE_C32_0 */
+	{18432, 0x00FF, 0x00FF, 0x0000 }, /* R18432 - RETUNEADC_PG2_1 */
+	{18433, 0xFFFF, 0xFFFF, 0x0000 }, /* R18433 - RETUNEADC_PG2_0 */
+	{18434, 0x00FF, 0x00FF, 0x0000 }, /* R18434 - RETUNEADC_PG_1 */
+	{18435, 0xFFFF, 0xFFFF, 0x0000 }, /* R18435 - RETUNEADC_PG_0 */
+	{18944, 0x00FF, 0x00FF, 0x0000 }, /* R18944 - ADCR_RETUNE_C1_1 */
+	{18945, 0xFFFF, 0xFFFF, 0x0000 }, /* R18945 - ADCR_RETUNE_C1_0 */
+	{18946, 0x00FF, 0x00FF, 0x0000 }, /* R18946 - ADCR_RETUNE_C2_1 */
+	{18947, 0xFFFF, 0xFFFF, 0x0000 }, /* R18947 - ADCR_RETUNE_C2_0 */
+	{18948, 0x00FF, 0x00FF, 0x0000 }, /* R18948 - ADCR_RETUNE_C3_1 */
+	{18949, 0xFFFF, 0xFFFF, 0x0000 }, /* R18949 - ADCR_RETUNE_C3_0 */
+	{18950, 0x00FF, 0x00FF, 0x0000 }, /* R18950 - ADCR_RETUNE_C4_1 */
+	{18951, 0xFFFF, 0xFFFF, 0x0000 }, /* R18951 - ADCR_RETUNE_C4_0 */
+	{18952, 0x00FF, 0x00FF, 0x0000 }, /* R18952 - ADCR_RETUNE_C5_1 */
+	{18953, 0xFFFF, 0xFFFF, 0x0000 }, /* R18953 - ADCR_RETUNE_C5_0 */
+	{18954, 0x00FF, 0x00FF, 0x0000 }, /* R18954 - ADCR_RETUNE_C6_1 */
+	{18955, 0xFFFF, 0xFFFF, 0x0000 }, /* R18955 - ADCR_RETUNE_C6_0 */
+	{18956, 0x00FF, 0x00FF, 0x0000 }, /* R18956 - ADCR_RETUNE_C7_1 */
+	{18957, 0xFFFF, 0xFFFF, 0x0000 }, /* R18957 - ADCR_RETUNE_C7_0 */
+	{18958, 0x00FF, 0x00FF, 0x0000 }, /* R18958 - ADCR_RETUNE_C8_1 */
+	{18959, 0xFFFF, 0xFFFF, 0x0000 }, /* R18959 - ADCR_RETUNE_C8_0 */
+	{18960, 0x00FF, 0x00FF, 0x0000 }, /* R18960 - ADCR_RETUNE_C9_1 */
+	{18961, 0xFFFF, 0xFFFF, 0x0000 }, /* R18961 - ADCR_RETUNE_C9_0 */
+	{18962, 0x00FF, 0x00FF, 0x0000 }, /* R18962 - ADCR_RETUNE_C10_1 */
+	{18963, 0xFFFF, 0xFFFF, 0x0000 }, /* R18963 - ADCR_RETUNE_C10_0 */
+	{18964, 0x00FF, 0x00FF, 0x0000 }, /* R18964 - ADCR_RETUNE_C11_1 */
+	{18965, 0xFFFF, 0xFFFF, 0x0000 }, /* R18965 - ADCR_RETUNE_C11_0 */
+	{18966, 0x00FF, 0x00FF, 0x0000 }, /* R18966 - ADCR_RETUNE_C12_1 */
+	{18967, 0xFFFF, 0xFFFF, 0x0000 }, /* R18967 - ADCR_RETUNE_C12_0 */
+	{18968, 0x00FF, 0x00FF, 0x0000 }, /* R18968 - ADCR_RETUNE_C13_1 */
+	{18969, 0xFFFF, 0xFFFF, 0x0000 }, /* R18969 - ADCR_RETUNE_C13_0 */
+	{18970, 0x00FF, 0x00FF, 0x0000 }, /* R18970 - ADCR_RETUNE_C14_1 */
+	{18971, 0xFFFF, 0xFFFF, 0x0000 }, /* R18971 - ADCR_RETUNE_C14_0 */
+	{18972, 0x00FF, 0x00FF, 0x0000 }, /* R18972 - ADCR_RETUNE_C15_1 */
+	{18973, 0xFFFF, 0xFFFF, 0x0000 }, /* R18973 - ADCR_RETUNE_C15_0 */
+	{18974, 0x00FF, 0x00FF, 0x0000 }, /* R18974 - ADCR_RETUNE_C16_1 */
+	{18975, 0xFFFF, 0xFFFF, 0x0000 }, /* R18975 - ADCR_RETUNE_C16_0 */
+	{18976, 0x00FF, 0x00FF, 0x0000 }, /* R18976 - ADCR_RETUNE_C17_1 */
+	{18977, 0xFFFF, 0xFFFF, 0x0000 }, /* R18977 - ADCR_RETUNE_C17_0 */
+	{18978, 0x00FF, 0x00FF, 0x0000 }, /* R18978 - ADCR_RETUNE_C18_1 */
+	{18979, 0xFFFF, 0xFFFF, 0x0000 }, /* R18979 - ADCR_RETUNE_C18_0 */
+	{18980, 0x00FF, 0x00FF, 0x0000 }, /* R18980 - ADCR_RETUNE_C19_1 */
+	{18981, 0xFFFF, 0xFFFF, 0x0000 }, /* R18981 - ADCR_RETUNE_C19_0 */
+	{18982, 0x00FF, 0x00FF, 0x0000 }, /* R18982 - ADCR_RETUNE_C20_1 */
+	{18983, 0xFFFF, 0xFFFF, 0x0000 }, /* R18983 - ADCR_RETUNE_C20_0 */
+	{18984, 0x00FF, 0x00FF, 0x0000 }, /* R18984 - ADCR_RETUNE_C21_1 */
+	{18985, 0xFFFF, 0xFFFF, 0x0000 }, /* R18985 - ADCR_RETUNE_C21_0 */
+	{18986, 0x00FF, 0x00FF, 0x0000 }, /* R18986 - ADCR_RETUNE_C22_1 */
+	{18987, 0xFFFF, 0xFFFF, 0x0000 }, /* R18987 - ADCR_RETUNE_C22_0 */
+	{18988, 0x00FF, 0x00FF, 0x0000 }, /* R18988 - ADCR_RETUNE_C23_1 */
+	{18989, 0xFFFF, 0xFFFF, 0x0000 }, /* R18989 - ADCR_RETUNE_C23_0 */
+	{18990, 0x00FF, 0x00FF, 0x0000 }, /* R18990 - ADCR_RETUNE_C24_1 */
+	{18991, 0xFFFF, 0xFFFF, 0x0000 }, /* R18991 - ADCR_RETUNE_C24_0 */
+	{18992, 0x00FF, 0x00FF, 0x0000 }, /* R18992 - ADCR_RETUNE_C25_1 */
+	{18993, 0xFFFF, 0xFFFF, 0x0000 }, /* R18993 - ADCR_RETUNE_C25_0 */
+	{18994, 0x00FF, 0x00FF, 0x0000 }, /* R18994 - ADCR_RETUNE_C26_1 */
+	{18995, 0xFFFF, 0xFFFF, 0x0000 }, /* R18995 - ADCR_RETUNE_C26_0 */
+	{18996, 0x00FF, 0x00FF, 0x0000 }, /* R18996 - ADCR_RETUNE_C27_1 */
+	{18997, 0xFFFF, 0xFFFF, 0x0000 }, /* R18997 - ADCR_RETUNE_C27_0 */
+	{18998, 0x00FF, 0x00FF, 0x0000 }, /* R18998 - ADCR_RETUNE_C28_1 */
+	{18999, 0xFFFF, 0xFFFF, 0x0000 }, /* R18999 - ADCR_RETUNE_C28_0 */
+	{19000, 0x00FF, 0x00FF, 0x0000 }, /* R19000 - ADCR_RETUNE_C29_1 */
+	{19001, 0xFFFF, 0xFFFF, 0x0000 }, /* R19001 - ADCR_RETUNE_C29_0 */
+	{19002, 0x00FF, 0x00FF, 0x0000 }, /* R19002 - ADCR_RETUNE_C30_1 */
+	{19003, 0xFFFF, 0xFFFF, 0x0000 }, /* R19003 - ADCR_RETUNE_C30_0 */
+	{19004, 0x00FF, 0x00FF, 0x0000 }, /* R19004 - ADCR_RETUNE_C31_1 */
+	{19005, 0xFFFF, 0xFFFF, 0x0000 }, /* R19005 - ADCR_RETUNE_C31_0 */
+	{19006, 0x00FF, 0x00FF, 0x0000 }, /* R19006 - ADCR_RETUNE_C32_1 */
+	{19007, 0xFFFF, 0xFFFF, 0x0000 }, /* R19007 - ADCR_RETUNE_C32_0 */
+	{19456, 0x00FF, 0x00FF, 0x0000 }, /* R19456 - DACL_RETUNE_C1_1 */
+	{19457, 0xFFFF, 0xFFFF, 0x0000 }, /* R19457 - DACL_RETUNE_C1_0 */
+	{19458, 0x00FF, 0x00FF, 0x0000 }, /* R19458 - DACL_RETUNE_C2_1 */
+	{19459, 0xFFFF, 0xFFFF, 0x0000 }, /* R19459 - DACL_RETUNE_C2_0 */
+	{19460, 0x00FF, 0x00FF, 0x0000 }, /* R19460 - DACL_RETUNE_C3_1 */
+	{19461, 0xFFFF, 0xFFFF, 0x0000 }, /* R19461 - DACL_RETUNE_C3_0 */
+	{19462, 0x00FF, 0x00FF, 0x0000 }, /* R19462 - DACL_RETUNE_C4_1 */
+	{19463, 0xFFFF, 0xFFFF, 0x0000 }, /* R19463 - DACL_RETUNE_C4_0 */
+	{19464, 0x00FF, 0x00FF, 0x0000 }, /* R19464 - DACL_RETUNE_C5_1 */
+	{19465, 0xFFFF, 0xFFFF, 0x0000 }, /* R19465 - DACL_RETUNE_C5_0 */
+	{19466, 0x00FF, 0x00FF, 0x0000 }, /* R19466 - DACL_RETUNE_C6_1 */
+	{19467, 0xFFFF, 0xFFFF, 0x0000 }, /* R19467 - DACL_RETUNE_C6_0 */
+	{19468, 0x00FF, 0x00FF, 0x0000 }, /* R19468 - DACL_RETUNE_C7_1 */
+	{19469, 0xFFFF, 0xFFFF, 0x0000 }, /* R19469 - DACL_RETUNE_C7_0 */
+	{19470, 0x00FF, 0x00FF, 0x0000 }, /* R19470 - DACL_RETUNE_C8_1 */
+	{19471, 0xFFFF, 0xFFFF, 0x0000 }, /* R19471 - DACL_RETUNE_C8_0 */
+	{19472, 0x00FF, 0x00FF, 0x0000 }, /* R19472 - DACL_RETUNE_C9_1 */
+	{19473, 0xFFFF, 0xFFFF, 0x0000 }, /* R19473 - DACL_RETUNE_C9_0 */
+	{19474, 0x00FF, 0x00FF, 0x0000 }, /* R19474 - DACL_RETUNE_C10_1 */
+	{19475, 0xFFFF, 0xFFFF, 0x0000 }, /* R19475 - DACL_RETUNE_C10_0 */
+	{19476, 0x00FF, 0x00FF, 0x0000 }, /* R19476 - DACL_RETUNE_C11_1 */
+	{19477, 0xFFFF, 0xFFFF, 0x0000 }, /* R19477 - DACL_RETUNE_C11_0 */
+	{19478, 0x00FF, 0x00FF, 0x0000 }, /* R19478 - DACL_RETUNE_C12_1 */
+	{19479, 0xFFFF, 0xFFFF, 0x0000 }, /* R19479 - DACL_RETUNE_C12_0 */
+	{19480, 0x00FF, 0x00FF, 0x0000 }, /* R19480 - DACL_RETUNE_C13_1 */
+	{19481, 0xFFFF, 0xFFFF, 0x0000 }, /* R19481 - DACL_RETUNE_C13_0 */
+	{19482, 0x00FF, 0x00FF, 0x0000 }, /* R19482 - DACL_RETUNE_C14_1 */
+	{19483, 0xFFFF, 0xFFFF, 0x0000 }, /* R19483 - DACL_RETUNE_C14_0 */
+	{19484, 0x00FF, 0x00FF, 0x0000 }, /* R19484 - DACL_RETUNE_C15_1 */
+	{19485, 0xFFFF, 0xFFFF, 0x0000 }, /* R19485 - DACL_RETUNE_C15_0 */
+	{19486, 0x00FF, 0x00FF, 0x0000 }, /* R19486 - DACL_RETUNE_C16_1 */
+	{19487, 0xFFFF, 0xFFFF, 0x0000 }, /* R19487 - DACL_RETUNE_C16_0 */
+	{19488, 0x00FF, 0x00FF, 0x0000 }, /* R19488 - DACL_RETUNE_C17_1 */
+	{19489, 0xFFFF, 0xFFFF, 0x0000 }, /* R19489 - DACL_RETUNE_C17_0 */
+	{19490, 0x00FF, 0x00FF, 0x0000 }, /* R19490 - DACL_RETUNE_C18_1 */
+	{19491, 0xFFFF, 0xFFFF, 0x0000 }, /* R19491 - DACL_RETUNE_C18_0 */
+	{19492, 0x00FF, 0x00FF, 0x0000 }, /* R19492 - DACL_RETUNE_C19_1 */
+	{19493, 0xFFFF, 0xFFFF, 0x0000 }, /* R19493 - DACL_RETUNE_C19_0 */
+	{19494, 0x00FF, 0x00FF, 0x0000 }, /* R19494 - DACL_RETUNE_C20_1 */
+	{19495, 0xFFFF, 0xFFFF, 0x0000 }, /* R19495 - DACL_RETUNE_C20_0 */
+	{19496, 0x00FF, 0x00FF, 0x0000 }, /* R19496 - DACL_RETUNE_C21_1 */
+	{19497, 0xFFFF, 0xFFFF, 0x0000 }, /* R19497 - DACL_RETUNE_C21_0 */
+	{19498, 0x00FF, 0x00FF, 0x0000 }, /* R19498 - DACL_RETUNE_C22_1 */
+	{19499, 0xFFFF, 0xFFFF, 0x0000 }, /* R19499 - DACL_RETUNE_C22_0 */
+	{19500, 0x00FF, 0x00FF, 0x0000 }, /* R19500 - DACL_RETUNE_C23_1 */
+	{19501, 0xFFFF, 0xFFFF, 0x0000 }, /* R19501 - DACL_RETUNE_C23_0 */
+	{19502, 0x00FF, 0x00FF, 0x0000 }, /* R19502 - DACL_RETUNE_C24_1 */
+	{19503, 0xFFFF, 0xFFFF, 0x0000 }, /* R19503 - DACL_RETUNE_C24_0 */
+	{19504, 0x00FF, 0x00FF, 0x0000 }, /* R19504 - DACL_RETUNE_C25_1 */
+	{19505, 0xFFFF, 0xFFFF, 0x0000 }, /* R19505 - DACL_RETUNE_C25_0 */
+	{19506, 0x00FF, 0x00FF, 0x0000 }, /* R19506 - DACL_RETUNE_C26_1 */
+	{19507, 0xFFFF, 0xFFFF, 0x0000 }, /* R19507 - DACL_RETUNE_C26_0 */
+	{19508, 0x00FF, 0x00FF, 0x0000 }, /* R19508 - DACL_RETUNE_C27_1 */
+	{19509, 0xFFFF, 0xFFFF, 0x0000 }, /* R19509 - DACL_RETUNE_C27_0 */
+	{19510, 0x00FF, 0x00FF, 0x0000 }, /* R19510 - DACL_RETUNE_C28_1 */
+	{19511, 0xFFFF, 0xFFFF, 0x0000 }, /* R19511 - DACL_RETUNE_C28_0 */
+	{19512, 0x00FF, 0x00FF, 0x0000 }, /* R19512 - DACL_RETUNE_C29_1 */
+	{19513, 0xFFFF, 0xFFFF, 0x0000 }, /* R19513 - DACL_RETUNE_C29_0 */
+	{19514, 0x00FF, 0x00FF, 0x0000 }, /* R19514 - DACL_RETUNE_C30_1 */
+	{19515, 0xFFFF, 0xFFFF, 0x0000 }, /* R19515 - DACL_RETUNE_C30_0 */
+	{19516, 0x00FF, 0x00FF, 0x0000 }, /* R19516 - DACL_RETUNE_C31_1 */
+	{19517, 0xFFFF, 0xFFFF, 0x0000 }, /* R19517 - DACL_RETUNE_C31_0 */
+	{19518, 0x00FF, 0x00FF, 0x0000 }, /* R19518 - DACL_RETUNE_C32_1 */
+	{19519, 0xFFFF, 0xFFFF, 0x0000 }, /* R19519 - DACL_RETUNE_C32_0 */
+	{19968, 0x00FF, 0x00FF, 0x0000 }, /* R19968 - RETUNEDAC_PG2_1 */
+	{19969, 0xFFFF, 0xFFFF, 0x0000 }, /* R19969 - RETUNEDAC_PG2_0 */
+	{19970, 0x00FF, 0x00FF, 0x0000 }, /* R19970 - RETUNEDAC_PG_1 */
+	{19971, 0xFFFF, 0xFFFF, 0x0000 }, /* R19971 - RETUNEDAC_PG_0 */
+	{20480, 0x00FF, 0x00FF, 0x0000 }, /* R20480 - DACR_RETUNE_C1_1 */
+	{20481, 0xFFFF, 0xFFFF, 0x0000 }, /* R20481 - DACR_RETUNE_C1_0 */
+	{20482, 0x00FF, 0x00FF, 0x0000 }, /* R20482 - DACR_RETUNE_C2_1 */
+	{20483, 0xFFFF, 0xFFFF, 0x0000 }, /* R20483 - DACR_RETUNE_C2_0 */
+	{20484, 0x00FF, 0x00FF, 0x0000 }, /* R20484 - DACR_RETUNE_C3_1 */
+	{20485, 0xFFFF, 0xFFFF, 0x0000 }, /* R20485 - DACR_RETUNE_C3_0 */
+	{20486, 0x00FF, 0x00FF, 0x0000 }, /* R20486 - DACR_RETUNE_C4_1 */
+	{20487, 0xFFFF, 0xFFFF, 0x0000 }, /* R20487 - DACR_RETUNE_C4_0 */
+	{20488, 0x00FF, 0x00FF, 0x0000 }, /* R20488 - DACR_RETUNE_C5_1 */
+	{20489, 0xFFFF, 0xFFFF, 0x0000 }, /* R20489 - DACR_RETUNE_C5_0 */
+	{20490, 0x00FF, 0x00FF, 0x0000 }, /* R20490 - DACR_RETUNE_C6_1 */
+	{20491, 0xFFFF, 0xFFFF, 0x0000 }, /* R20491 - DACR_RETUNE_C6_0 */
+	{20492, 0x00FF, 0x00FF, 0x0000 }, /* R20492 - DACR_RETUNE_C7_1 */
+	{20493, 0xFFFF, 0xFFFF, 0x0000 }, /* R20493 - DACR_RETUNE_C7_0 */
+	{20494, 0x00FF, 0x00FF, 0x0000 }, /* R20494 - DACR_RETUNE_C8_1 */
+	{20495, 0xFFFF, 0xFFFF, 0x0000 }, /* R20495 - DACR_RETUNE_C8_0 */
+	{20496, 0x00FF, 0x00FF, 0x0000 }, /* R20496 - DACR_RETUNE_C9_1 */
+	{20497, 0xFFFF, 0xFFFF, 0x0000 }, /* R20497 - DACR_RETUNE_C9_0 */
+	{20498, 0x00FF, 0x00FF, 0x0000 }, /* R20498 - DACR_RETUNE_C10_1 */
+	{20499, 0xFFFF, 0xFFFF, 0x0000 }, /* R20499 - DACR_RETUNE_C10_0 */
+	{20500, 0x00FF, 0x00FF, 0x0000 }, /* R20500 - DACR_RETUNE_C11_1 */
+	{20501, 0xFFFF, 0xFFFF, 0x0000 }, /* R20501 - DACR_RETUNE_C11_0 */
+	{20502, 0x00FF, 0x00FF, 0x0000 }, /* R20502 - DACR_RETUNE_C12_1 */
+	{20503, 0xFFFF, 0xFFFF, 0x0000 }, /* R20503 - DACR_RETUNE_C12_0 */
+	{20504, 0x00FF, 0x00FF, 0x0000 }, /* R20504 - DACR_RETUNE_C13_1 */
+	{20505, 0xFFFF, 0xFFFF, 0x0000 }, /* R20505 - DACR_RETUNE_C13_0 */
+	{20506, 0x00FF, 0x00FF, 0x0000 }, /* R20506 - DACR_RETUNE_C14_1 */
+	{20507, 0xFFFF, 0xFFFF, 0x0000 }, /* R20507 - DACR_RETUNE_C14_0 */
+	{20508, 0x00FF, 0x00FF, 0x0000 }, /* R20508 - DACR_RETUNE_C15_1 */
+	{20509, 0xFFFF, 0xFFFF, 0x0000 }, /* R20509 - DACR_RETUNE_C15_0 */
+	{20510, 0x00FF, 0x00FF, 0x0000 }, /* R20510 - DACR_RETUNE_C16_1 */
+	{20511, 0xFFFF, 0xFFFF, 0x0000 }, /* R20511 - DACR_RETUNE_C16_0 */
+	{20512, 0x00FF, 0x00FF, 0x0000 }, /* R20512 - DACR_RETUNE_C17_1 */
+	{20513, 0xFFFF, 0xFFFF, 0x0000 }, /* R20513 - DACR_RETUNE_C17_0 */
+	{20514, 0x00FF, 0x00FF, 0x0000 }, /* R20514 - DACR_RETUNE_C18_1 */
+	{20515, 0xFFFF, 0xFFFF, 0x0000 }, /* R20515 - DACR_RETUNE_C18_0 */
+	{20516, 0x00FF, 0x00FF, 0x0000 }, /* R20516 - DACR_RETUNE_C19_1 */
+	{20517, 0xFFFF, 0xFFFF, 0x0000 }, /* R20517 - DACR_RETUNE_C19_0 */
+	{20518, 0x00FF, 0x00FF, 0x0000 }, /* R20518 - DACR_RETUNE_C20_1 */
+	{20519, 0xFFFF, 0xFFFF, 0x0000 }, /* R20519 - DACR_RETUNE_C20_0 */
+	{20520, 0x00FF, 0x00FF, 0x0000 }, /* R20520 - DACR_RETUNE_C21_1 */
+	{20521, 0xFFFF, 0xFFFF, 0x0000 }, /* R20521 - DACR_RETUNE_C21_0 */
+	{20522, 0x00FF, 0x00FF, 0x0000 }, /* R20522 - DACR_RETUNE_C22_1 */
+	{20523, 0xFFFF, 0xFFFF, 0x0000 }, /* R20523 - DACR_RETUNE_C22_0 */
+	{20524, 0x00FF, 0x00FF, 0x0000 }, /* R20524 - DACR_RETUNE_C23_1 */
+	{20525, 0xFFFF, 0xFFFF, 0x0000 }, /* R20525 - DACR_RETUNE_C23_0 */
+	{20526, 0x00FF, 0x00FF, 0x0000 }, /* R20526 - DACR_RETUNE_C24_1 */
+	{20527, 0xFFFF, 0xFFFF, 0x0000 }, /* R20527 - DACR_RETUNE_C24_0 */
+	{20528, 0x00FF, 0x00FF, 0x0000 }, /* R20528 - DACR_RETUNE_C25_1 */
+	{20529, 0xFFFF, 0xFFFF, 0x0000 }, /* R20529 - DACR_RETUNE_C25_0 */
+	{20530, 0x00FF, 0x00FF, 0x0000 }, /* R20530 - DACR_RETUNE_C26_1 */
+	{20531, 0xFFFF, 0xFFFF, 0x0000 }, /* R20531 - DACR_RETUNE_C26_0 */
+	{20532, 0x00FF, 0x00FF, 0x0000 }, /* R20532 - DACR_RETUNE_C27_1 */
+	{20533, 0xFFFF, 0xFFFF, 0x0000 }, /* R20533 - DACR_RETUNE_C27_0 */
+	{20534, 0x00FF, 0x00FF, 0x0000 }, /* R20534 - DACR_RETUNE_C28_1 */
+	{20535, 0xFFFF, 0xFFFF, 0x0000 }, /* R20535 - DACR_RETUNE_C28_0 */
+	{20536, 0x00FF, 0x00FF, 0x0000 }, /* R20536 - DACR_RETUNE_C29_1 */
+	{20537, 0xFFFF, 0xFFFF, 0x0000 }, /* R20537 - DACR_RETUNE_C29_0 */
+	{20538, 0x00FF, 0x00FF, 0x0000 }, /* R20538 - DACR_RETUNE_C30_1 */
+	{20539, 0xFFFF, 0xFFFF, 0x0000 }, /* R20539 - DACR_RETUNE_C30_0 */
+	{20540, 0x00FF, 0x00FF, 0x0000 }, /* R20540 - DACR_RETUNE_C31_1 */
+	{20541, 0xFFFF, 0xFFFF, 0x0000 }, /* R20541 - DACR_RETUNE_C31_0 */
+	{20542, 0x00FF, 0x00FF, 0x0000 }, /* R20542 - DACR_RETUNE_C32_1 */
+	{20543, 0xFFFF, 0xFFFF, 0x0000 }, /* R20543 - DACR_RETUNE_C32_0 */
+	{20992, 0x00FF, 0x00FF, 0x0000 }, /* R20992 - VSS_XHD2_1 */
+	{20993, 0xFFFF, 0xFFFF, 0x0000 }, /* R20993 - VSS_XHD2_0 */
+	{20994, 0x00FF, 0x00FF, 0x0000 }, /* R20994 - VSS_XHD3_1 */
+	{20995, 0xFFFF, 0xFFFF, 0x0000 }, /* R20995 - VSS_XHD3_0 */
+	{20996, 0x00FF, 0x00FF, 0x0000 }, /* R20996 - VSS_XHN1_1 */
+	{20997, 0xFFFF, 0xFFFF, 0x0000 }, /* R20997 - VSS_XHN1_0 */
+	{20998, 0x00FF, 0x00FF, 0x0000 }, /* R20998 - VSS_XHN2_1 */
+	{20999, 0xFFFF, 0xFFFF, 0x0000 }, /* R20999 - VSS_XHN2_0 */
+	{21000, 0x00FF, 0x00FF, 0x0000 }, /* R21000 - VSS_XHN3_1 */
+	{21001, 0xFFFF, 0xFFFF, 0x0000 }, /* R21001 - VSS_XHN3_0 */
+	{21002, 0x00FF, 0x00FF, 0x0000 }, /* R21002 - VSS_XLA_1 */
+	{21003, 0xFFFF, 0xFFFF, 0x0000 }, /* R21003 - VSS_XLA_0 */
+	{21004, 0x00FF, 0x00FF, 0x0000 }, /* R21004 - VSS_XLB_1 */
+	{21005, 0xFFFF, 0xFFFF, 0x0000 }, /* R21005 - VSS_XLB_0 */
+	{21006, 0x00FF, 0x00FF, 0x0000 }, /* R21006 - VSS_XLG_1 */
+	{21007, 0xFFFF, 0xFFFF, 0x0000 }, /* R21007 - VSS_XLG_0 */
+	{21008, 0x00FF, 0x00FF, 0x0000 }, /* R21008 - VSS_PG2_1 */
+	{21009, 0xFFFF, 0xFFFF, 0x0000 }, /* R21009 - VSS_PG2_0 */
+	{21010, 0x00FF, 0x00FF, 0x0000 }, /* R21010 - VSS_PG_1 */
+	{21011, 0xFFFF, 0xFFFF, 0x0000 }, /* R21011 - VSS_PG_0 */
+	{21012, 0x00FF, 0x00FF, 0x0000 }, /* R21012 - VSS_XTD1_1 */
+	{21013, 0xFFFF, 0xFFFF, 0x0000 }, /* R21013 - VSS_XTD1_0 */
+	{21014, 0x00FF, 0x00FF, 0x0000 }, /* R21014 - VSS_XTD2_1 */
+	{21015, 0xFFFF, 0xFFFF, 0x0000 }, /* R21015 - VSS_XTD2_0 */
+	{21016, 0x00FF, 0x00FF, 0x0000 }, /* R21016 - VSS_XTD3_1 */
+	{21017, 0xFFFF, 0xFFFF, 0x0000 }, /* R21017 - VSS_XTD3_0 */
+	{21018, 0x00FF, 0x00FF, 0x0000 }, /* R21018 - VSS_XTD4_1 */
+	{21019, 0xFFFF, 0xFFFF, 0x0000 }, /* R21019 - VSS_XTD4_0 */
+	{21020, 0x00FF, 0x00FF, 0x0000 }, /* R21020 - VSS_XTD5_1 */
+	{21021, 0xFFFF, 0xFFFF, 0x0000 }, /* R21021 - VSS_XTD5_0 */
+	{21022, 0x00FF, 0x00FF, 0x0000 }, /* R21022 - VSS_XTD6_1 */
+	{21023, 0xFFFF, 0xFFFF, 0x0000 }, /* R21023 - VSS_XTD6_0 */
+	{21024, 0x00FF, 0x00FF, 0x0000 }, /* R21024 - VSS_XTD7_1 */
+	{21025, 0xFFFF, 0xFFFF, 0x0000 }, /* R21025 - VSS_XTD7_0 */
+	{21026, 0x00FF, 0x00FF, 0x0000 }, /* R21026 - VSS_XTD8_1 */
+	{21027, 0xFFFF, 0xFFFF, 0x0000 }, /* R21027 - VSS_XTD8_0 */
+	{21028, 0x00FF, 0x00FF, 0x0000 }, /* R21028 - VSS_XTD9_1 */
+	{21029, 0xFFFF, 0xFFFF, 0x0000 }, /* R21029 - VSS_XTD9_0 */
+	{21030, 0x00FF, 0x00FF, 0x0000 }, /* R21030 - VSS_XTD10_1 */
+	{21031, 0xFFFF, 0xFFFF, 0x0000 }, /* R21031 - VSS_XTD10_0 */
+	{21032, 0x00FF, 0x00FF, 0x0000 }, /* R21032 - VSS_XTD11_1 */
+	{21033, 0xFFFF, 0xFFFF, 0x0000 }, /* R21033 - VSS_XTD11_0 */
+	{21034, 0x00FF, 0x00FF, 0x0000 }, /* R21034 - VSS_XTD12_1 */
+	{21035, 0xFFFF, 0xFFFF, 0x0000 }, /* R21035 - VSS_XTD12_0 */
+	{21036, 0x00FF, 0x00FF, 0x0000 }, /* R21036 - VSS_XTD13_1 */
+	{21037, 0xFFFF, 0xFFFF, 0x0000 }, /* R21037 - VSS_XTD13_0 */
+	{21038, 0x00FF, 0x00FF, 0x0000 }, /* R21038 - VSS_XTD14_1 */
+	{21039, 0xFFFF, 0xFFFF, 0x0000 }, /* R21039 - VSS_XTD14_0 */
+	{21040, 0x00FF, 0x00FF, 0x0000 }, /* R21040 - VSS_XTD15_1 */
+	{21041, 0xFFFF, 0xFFFF, 0x0000 }, /* R21041 - VSS_XTD15_0 */
+	{21042, 0x00FF, 0x00FF, 0x0000 }, /* R21042 - VSS_XTD16_1 */
+	{21043, 0xFFFF, 0xFFFF, 0x0000 }, /* R21043 - VSS_XTD16_0 */
+	{21044, 0x00FF, 0x00FF, 0x0000 }, /* R21044 - VSS_XTD17_1 */
+	{21045, 0xFFFF, 0xFFFF, 0x0000 }, /* R21045 - VSS_XTD17_0 */
+	{21046, 0x00FF, 0x00FF, 0x0000 }, /* R21046 - VSS_XTD18_1 */
+	{21047, 0xFFFF, 0xFFFF, 0x0000 }, /* R21047 - VSS_XTD18_0 */
+	{21048, 0x00FF, 0x00FF, 0x0000 }, /* R21048 - VSS_XTD19_1 */
+	{21049, 0xFFFF, 0xFFFF, 0x0000 }, /* R21049 - VSS_XTD19_0 */
+	{21050, 0x00FF, 0x00FF, 0x0000 }, /* R21050 - VSS_XTD20_1 */
+	{21051, 0xFFFF, 0xFFFF, 0x0000 }, /* R21051 - VSS_XTD20_0 */
+	{21052, 0x00FF, 0x00FF, 0x0000 }, /* R21052 - VSS_XTD21_1 */
+	{21053, 0xFFFF, 0xFFFF, 0x0000 }, /* R21053 - VSS_XTD21_0 */
+	{21054, 0x00FF, 0x00FF, 0x0000 }, /* R21054 - VSS_XTD22_1 */
+	{21055, 0xFFFF, 0xFFFF, 0x0000 }, /* R21055 - VSS_XTD22_0 */
+	{21056, 0x00FF, 0x00FF, 0x0000 }, /* R21056 - VSS_XTD23_1 */
+	{21057, 0xFFFF, 0xFFFF, 0x0000 }, /* R21057 - VSS_XTD23_0 */
+	{21058, 0x00FF, 0x00FF, 0x0000 }, /* R21058 - VSS_XTD24_1 */
+	{21059, 0xFFFF, 0xFFFF, 0x0000 }, /* R21059 - VSS_XTD24_0 */
+	{21060, 0x00FF, 0x00FF, 0x0000 }, /* R21060 - VSS_XTD25_1 */
+	{21061, 0xFFFF, 0xFFFF, 0x0000 }, /* R21061 - VSS_XTD25_0 */
+	{21062, 0x00FF, 0x00FF, 0x0000 }, /* R21062 - VSS_XTD26_1 */
+	{21063, 0xFFFF, 0xFFFF, 0x0000 }, /* R21063 - VSS_XTD26_0 */
+	{21064, 0x00FF, 0x00FF, 0x0000 }, /* R21064 - VSS_XTD27_1 */
+	{21065, 0xFFFF, 0xFFFF, 0x0000 }, /* R21065 - VSS_XTD27_0 */
+	{21066, 0x00FF, 0x00FF, 0x0000 }, /* R21066 - VSS_XTD28_1 */
+	{21067, 0xFFFF, 0xFFFF, 0x0000 }, /* R21067 - VSS_XTD28_0 */
+	{21068, 0x00FF, 0x00FF, 0x0000 }, /* R21068 - VSS_XTD29_1 */
+	{21069, 0xFFFF, 0xFFFF, 0x0000 }, /* R21069 - VSS_XTD29_0 */
+	{21070, 0x00FF, 0x00FF, 0x0000 }, /* R21070 - VSS_XTD30_1 */
+	{21071, 0xFFFF, 0xFFFF, 0x0000 }, /* R21071 - VSS_XTD30_0 */
+	{21072, 0x00FF, 0x00FF, 0x0000 }, /* R21072 - VSS_XTD31_1 */
+	{21073, 0xFFFF, 0xFFFF, 0x0000 }, /* R21073 - VSS_XTD31_0 */
+	{21074, 0x00FF, 0x00FF, 0x0000 }, /* R21074 - VSS_XTD32_1 */
+	{21075, 0xFFFF, 0xFFFF, 0x0000 }, /* R21075 - VSS_XTD32_0 */
+	{21076, 0x00FF, 0x00FF, 0x0000 }, /* R21076 - VSS_XTS1_1 */
+	{21077, 0xFFFF, 0xFFFF, 0x0000 }, /* R21077 - VSS_XTS1_0 */
+	{21078, 0x00FF, 0x00FF, 0x0000 }, /* R21078 - VSS_XTS2_1 */
+	{21079, 0xFFFF, 0xFFFF, 0x0000 }, /* R21079 - VSS_XTS2_0 */
+	{21080, 0x00FF, 0x00FF, 0x0000 }, /* R21080 - VSS_XTS3_1 */
+	{21081, 0xFFFF, 0xFFFF, 0x0000 }, /* R21081 - VSS_XTS3_0 */
+	{21082, 0x00FF, 0x00FF, 0x0000 }, /* R21082 - VSS_XTS4_1 */
+	{21083, 0xFFFF, 0xFFFF, 0x0000 }, /* R21083 - VSS_XTS4_0 */
+	{21084, 0x00FF, 0x00FF, 0x0000 }, /* R21084 - VSS_XTS5_1 */
+	{21085, 0xFFFF, 0xFFFF, 0x0000 }, /* R21085 - VSS_XTS5_0 */
+	{21086, 0x00FF, 0x00FF, 0x0000 }, /* R21086 - VSS_XTS6_1 */
+	{21087, 0xFFFF, 0xFFFF, 0x0000 }, /* R21087 - VSS_XTS6_0 */
+	{21088, 0x00FF, 0x00FF, 0x0000 }, /* R21088 - VSS_XTS7_1 */
+	{21089, 0xFFFF, 0xFFFF, 0x0000 }, /* R21089 - VSS_XTS7_0 */
+	{21090, 0x00FF, 0x00FF, 0x0000 }, /* R21090 - VSS_XTS8_1 */
+	{21091, 0xFFFF, 0xFFFF, 0x0000 }, /* R21091 - VSS_XTS8_0 */
+	{21092, 0x00FF, 0x00FF, 0x0000 }, /* R21092 - VSS_XTS9_1 */
+	{21093, 0xFFFF, 0xFFFF, 0x0000 }, /* R21093 - VSS_XTS9_0 */
+	{21094, 0x00FF, 0x00FF, 0x0000 }, /* R21094 - VSS_XTS10_1 */
+	{21095, 0xFFFF, 0xFFFF, 0x0000 }, /* R21095 - VSS_XTS10_0 */
+	{21096, 0x00FF, 0x00FF, 0x0000 }, /* R21096 - VSS_XTS11_1 */
+	{21097, 0xFFFF, 0xFFFF, 0x0000 }, /* R21097 - VSS_XTS11_0 */
+	{21098, 0x00FF, 0x00FF, 0x0000 }, /* R21098 - VSS_XTS12_1 */
+	{21099, 0xFFFF, 0xFFFF, 0x0000 }, /* R21099 - VSS_XTS12_0 */
+	{21100, 0x00FF, 0x00FF, 0x0000 }, /* R21100 - VSS_XTS13_1 */
+	{21101, 0xFFFF, 0xFFFF, 0x0000 }, /* R21101 - VSS_XTS13_0 */
+	{21102, 0x00FF, 0x00FF, 0x0000 }, /* R21102 - VSS_XTS14_1 */
+	{21103, 0xFFFF, 0xFFFF, 0x0000 }, /* R21103 - VSS_XTS14_0 */
+	{21104, 0x00FF, 0x00FF, 0x0000 }, /* R21104 - VSS_XTS15_1 */
+	{21105, 0xFFFF, 0xFFFF, 0x0000 }, /* R21105 - VSS_XTS15_0 */
+	{21106, 0x00FF, 0x00FF, 0x0000 }, /* R21106 - VSS_XTS16_1 */
+	{21107, 0xFFFF, 0xFFFF, 0x0000 }, /* R21107 - VSS_XTS16_0 */
+	{21108, 0x00FF, 0x00FF, 0x0000 }, /* R21108 - VSS_XTS17_1 */
+	{21109, 0xFFFF, 0xFFFF, 0x0000 }, /* R21109 - VSS_XTS17_0 */
+	{21110, 0x00FF, 0x00FF, 0x0000 }, /* R21110 - VSS_XTS18_1 */
+	{21111, 0xFFFF, 0xFFFF, 0x0000 }, /* R21111 - VSS_XTS18_0 */
+	{21112, 0x00FF, 0x00FF, 0x0000 }, /* R21112 - VSS_XTS19_1 */
+	{21113, 0xFFFF, 0xFFFF, 0x0000 }, /* R21113 - VSS_XTS19_0 */
+	{21114, 0x00FF, 0x00FF, 0x0000 }, /* R21114 - VSS_XTS20_1 */
+	{21115, 0xFFFF, 0xFFFF, 0x0000 }, /* R21115 - VSS_XTS20_0 */
+	{21116, 0x00FF, 0x00FF, 0x0000 }, /* R21116 - VSS_XTS21_1 */
+	{21117, 0xFFFF, 0xFFFF, 0x0000 }, /* R21117 - VSS_XTS21_0 */
+	{21118, 0x00FF, 0x00FF, 0x0000 }, /* R21118 - VSS_XTS22_1 */
+	{21119, 0xFFFF, 0xFFFF, 0x0000 }, /* R21119 - VSS_XTS22_0 */
+	{21120, 0x00FF, 0x00FF, 0x0000 }, /* R21120 - VSS_XTS23_1 */
+	{21121, 0xFFFF, 0xFFFF, 0x0000 }, /* R21121 - VSS_XTS23_0 */
+	{21122, 0x00FF, 0x00FF, 0x0000 }, /* R21122 - VSS_XTS24_1 */
+	{21123, 0xFFFF, 0xFFFF, 0x0000 }, /* R21123 - VSS_XTS24_0 */
+	{21124, 0x00FF, 0x00FF, 0x0000 }, /* R21124 - VSS_XTS25_1 */
+	{21125, 0xFFFF, 0xFFFF, 0x0000 }, /* R21125 - VSS_XTS25_0 */
+	{21126, 0x00FF, 0x00FF, 0x0000 }, /* R21126 - VSS_XTS26_1 */
+	{21127, 0xFFFF, 0xFFFF, 0x0000 }, /* R21127 - VSS_XTS26_0 */
+	{21128, 0x00FF, 0x00FF, 0x0000 }, /* R21128 - VSS_XTS27_1 */
+	{21129, 0xFFFF, 0xFFFF, 0x0000 }, /* R21129 - VSS_XTS27_0 */
+	{21130, 0x00FF, 0x00FF, 0x0000 }, /* R21130 - VSS_XTS28_1 */
+	{21131, 0xFFFF, 0xFFFF, 0x0000 }, /* R21131 - VSS_XTS28_0 */
+	{21132, 0x00FF, 0x00FF, 0x0000 }, /* R21132 - VSS_XTS29_1 */
+	{21133, 0xFFFF, 0xFFFF, 0x0000 }, /* R21133 - VSS_XTS29_0 */
+	{21134, 0x00FF, 0x00FF, 0x0000 }, /* R21134 - VSS_XTS30_1 */
+	{21135, 0xFFFF, 0xFFFF, 0x0000 }, /* R21135 - VSS_XTS30_0 */
+	{21136, 0x00FF, 0x00FF, 0x0000 }, /* R21136 - VSS_XTS31_1 */
+	{21137, 0xFFFF, 0xFFFF, 0x0000 }, /* R21137 - VSS_XTS31_0 */
+	{21138, 0x00FF, 0x00FF, 0x0000 }, /* R21138 - VSS_XTS32_1 */
+	{21139, 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */
 };
 
+static int wm8962_get_reg_access_index(unsigned int reg)
+{
+	unsigned int min = 0;
+	unsigned int max = ARRAY_SIZE(wm8962_reg_access) - 1;
+	do {
+		unsigned int index = (min + max) / 2;
+		if (wm8962_reg_access[index].reg == reg)
+			return index;
+		if (wm8962_reg_access[index].reg < reg)
+			min = index + 1;
+		else
+			max = index;
+	} while (min <= max);
+	return -1;
+}
+
 static int wm8962_volatile_register(unsigned int reg)
 {
-	if (wm8962_reg_access[reg].vol)
+	int index = wm8962_get_reg_access_index(reg);
+
+	if (index == -1)
+		return 0;
+	if (wm8962_reg_access[index].volatile_register)
 		return 1;
 	else
 		return 0;
@@ -1950,7 +1969,11 @@ static int wm8962_volatile_register(unsigned int reg)
 
 static int wm8962_readable_register(unsigned int reg)
 {
-	if (wm8962_reg_access[reg].read)
+	int index = wm8962_get_reg_access_index(reg);
+
+	if (index == -1)
+		return 0;
+	if (wm8962_reg_access[index].read)
 		return 1;
 	else
 		return 0;



^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text
  2010-10-13 19:10                   ` [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text Joe Perches
@ 2010-10-13 19:40                       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 19:40 UTC (permalink / raw)
  To: Joe Perches
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010 at 12:10:31PM -0700, Joe Perches wrote:

> I don't know if the binary chop is too cpu
> intensive or if too much data access would be
> a problem.

It's on my list to do something about this but if we're going to do
anything it should be in generic code - this is an issue which affects
a growing number of devices and there's enough code to mean that it
should be librified.  This should ideally also involve compressing the
cache itself since that has a similar effect again.

I'm not a fan of doing device specific stuff here since it will make it
harder to transition those devices to use generic code, causing hassle
trying to maintain the subsystem.  The size impact of doing something
simple is a bit annoying but not usually a pressing issue on devices
that would want a CODEC with a noticably large register map.

By the way, please try to provide prefixes for your patches which are
consistent with those normally used for the code you are modifying
rather than making up your own.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text
@ 2010-10-13 19:40                       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 19:40 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Peter Hsiang, Jesse Marroquin, Liam Girdwood

On Wed, Oct 13, 2010 at 12:10:31PM -0700, Joe Perches wrote:

> I don't know if the binary chop is too cpu
> intensive or if too much data access would be
> a problem.

It's on my list to do something about this but if we're going to do
anything it should be in generic code - this is an issue which affects
a growing number of devices and there's enough code to mean that it
should be librified.  This should ideally also involve compressing the
cache itself since that has a similar effect again.

I'm not a fan of doing device specific stuff here since it will make it
harder to transition those devices to use generic code, causing hassle
trying to maintain the subsystem.  The size impact of doing something
simple is a bit annoying but not usually a pressing issue on devices
that would want a CODEC with a noticably large register map.

By the way, please try to provide prefixes for your patches which are
consistent with those normally used for the code you are modifying
rather than making up your own.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text
  2010-10-13 19:40                       ` Mark Brown
  (?)
@ 2010-10-13 20:06                       ` Joe Perches
  2010-10-13 20:29                           ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Joe Perches @ 2010-10-13 20:06 UTC (permalink / raw)
  To: Mark Brown
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, 2010-10-13 at 20:40 +0100, Mark Brown wrote:
> The size impact of doing something
> simple is a bit annoying but not usually a pressing issue on devices
> that would want a CODEC with a noticably large register map.

I think wasting 100+ KB is excessive if I
were designing some cheap mp3 player.

Up to you how/when it should be resolved/fixed,
but I think it should be.


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text
  2010-10-13 20:06                       ` Joe Perches
@ 2010-10-13 20:29                           ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 20:29 UTC (permalink / raw)
  To: Joe Perches
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010 at 01:06:22PM -0700, Joe Perches wrote:
> On Wed, 2010-10-13 at 20:40 +0100, Mark Brown wrote:

> > The size impact of doing something
> > simple is a bit annoying but not usually a pressing issue on devices
> > that would want a CODEC with a noticably large register map.

> I think wasting 100+ KB is excessive if I
> were designing some cheap mp3 player.

Absolutely, but if you're desigining a cheap MP3 player you're very much
more likely to be using a much simpler device which doesn't have the
very large, sparse register maps that get a substantial benefit from
compressing the data like this.  As I said, devices that would want a
CODEC with a register map that is affected usually aren't under that
much size pressure for the kernel.

> Up to you how/when it should be resolved/fixed,
> but I think it should be.

As I said in the text which you cut I agree that this should be improved
but don't want to have to go through each and every device cut'n'pasting
code since that's not going to do anything for maintainbility.

In addition to my previous comments about your subject lines for patches
please also avoid burying new patches in the middle of tangentially
related threads.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text
@ 2010-10-13 20:29                           ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-13 20:29 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Peter Hsiang, Jesse Marroquin, Liam Girdwood

On Wed, Oct 13, 2010 at 01:06:22PM -0700, Joe Perches wrote:
> On Wed, 2010-10-13 at 20:40 +0100, Mark Brown wrote:

> > The size impact of doing something
> > simple is a bit annoying but not usually a pressing issue on devices
> > that would want a CODEC with a noticably large register map.

> I think wasting 100+ KB is excessive if I
> were designing some cheap mp3 player.

Absolutely, but if you're desigining a cheap MP3 player you're very much
more likely to be using a much simpler device which doesn't have the
very large, sparse register maps that get a substantial benefit from
compressing the data like this.  As I said, devices that would want a
CODEC with a register map that is affected usually aren't under that
much size pressure for the kernel.

> Up to you how/when it should be resolved/fixed,
> but I think it should be.

As I said in the text which you cut I agree that this should be improved
but don't want to have to go through each and every device cut'n'pasting
code since that's not going to do anything for maintainbility.

In addition to my previous comments about your subject lines for patches
please also avoid burying new patches in the middle of tangentially
related threads.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-13 10:32     ` Mark Brown
@ 2010-10-14  3:18       ` Peter Hsiang
  -1 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-10-14  3:18 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010, Mark Brown wrote:
> On Tue, Oct 12, 2010 at 06:20:10PM -0700, Peter Hsiang wrote:
> 
> This looks pretty good, waiting for Liam's review but I think we can do
> further stuff incrementally.  Minor comments:
> 
> > +
> > +       /* Setup an array of texts for the equalizer enum.
> > +        * This is based on Mark Brown's equalizer driver code.
> > +        * It has been extended to support multiple equalizers.
> > +        */
> 
> The code that you're basing this on supports multiple equalizers too -
> things like the WM8994 have several.  Given that all those supported by
> the original code are identical there didn't seem to be any point in
> restricting which EQ a given setup can be deployed on - it's just one
> more thing that can go wrong.

Thanks - that is a very good idea.  I have implemented it and will
submit it shortly.


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-10-14  3:18       ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-10-14  3:18 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Jesse, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Marroquin, Liam, Girdwood

On Wed, Oct 13, 2010, Mark Brown wrote:
> On Tue, Oct 12, 2010 at 06:20:10PM -0700, Peter Hsiang wrote:
> 
> This looks pretty good, waiting for Liam's review but I think we can do
> further stuff incrementally.  Minor comments:
> 
> > +
> > +       /* Setup an array of texts for the equalizer enum.
> > +        * This is based on Mark Brown's equalizer driver code.
> > +        * It has been extended to support multiple equalizers.
> > +        */
> 
> The code that you're basing this on supports multiple equalizers too -
> things like the WM8994 have several.  Given that all those supported by
> the original code are identical there didn't seem to be any point in
> restricting which EQ a given setup can be deployed on - it's just one
> more thing that can go wrong.

Thanks - that is a very good idea.  I have implemented it and will
submit it shortly.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-13  1:20 ` [PATCH] ASoC: Add max98088 CODEC driver Peter Hsiang
  2010-10-13  1:47   ` Joe Perches
  2010-10-13 10:32     ` Mark Brown
@ 2010-10-14  3:30   ` Peter Hsiang
  2010-10-15 10:04       ` Liam Girdwood
  2010-10-15 10:55       ` Mark Brown
  2 siblings, 2 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-10-14  3:30 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown, Peter Ujfalusi
  Cc: alsa-devel, linux-kernel, Jesse Marroquin

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
---
 include/sound/max98088.h    |   50 +
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2097 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  193 ++++
 5 files changed, 2346 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..59edf30
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,50 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq_cfg;
+       unsigned int eq_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode:1;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microphone input
+        */
+       unsigned int digmic_left_mode:1;
+       unsigned int digmic_right_mode:1;
+
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ff7b922..53af7d2 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS4270 if I2C
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
@@ -158,6 +159,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index af4d4c4..2d4941d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -89,6 +90,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..92bc2ec
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2097 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+       int eq_sel;
+};
+
+struct max98088_priv {
+       u8 reg_cache[M98088_REG_CNT];
+       void *control_data;
+       struct max98088_pdata *pdata;
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       int eq_textcnt;
+       const char **eq_texts;
+       struct soc_enum eq_enum;
+       u8 ina_state;
+       u8 inb_state;
+       unsigned int ex_mode;
+       unsigned int digmic;
+       unsigned int mic1pre;
+       unsigned int mic2pre;
+       unsigned int extmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x00, 0x00, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       BUG_ON(band > 4);
+       BUG_ON(dai > 1);
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_exmode_texts[] = {
+       "Off", "100Hz", "400Hz", "600Hz", "800Hz", "1000Hz", "200-400Hz",
+       "400-600Hz", "400-800Hz",
+};
+
+static const unsigned int max98088_exmode_values[] = {
+       0x00, 0x43, 0x10, 0x20, 0x30, 0x40, 0x11, 0x22, 0x32
+};
+
+static const struct soc_enum max98088_exmode_enum =
+       SOC_VALUE_ENUM_SINGLE(M98088_REG_41_SPKDHP, 0, 127,
+                             ARRAY_SIZE(max98088_exmode_texts),
+                             max98088_exmode_texts,
+                             max98088_exmode_values);
+static const struct snd_kcontrol_new max98088_exmode_controls =
+       SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static const struct soc_enum max98088_extmic_enum =
+       SOC_ENUM_SINGLE(M98088_REG_48_CFG_MIC, 0, 3, max98088_extmic_text);
+
+static const struct snd_kcontrol_new max98088_extmic_mux =
+       SOC_DAPM_ENUM("External MIC Mux", max98088_extmic_enum);
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int sel = ucontrol->value.integer.value[0];
+
+       max98088->mic1pre = sel;
+       snd_soc_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
+               (1+sel)<<M98088_MICPRE_SHIFT);
+
+       return 0;
+}
+
+static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = max98088->mic1pre;
+       return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int sel = ucontrol->value.integer.value[0];
+
+       max98088->mic2pre = sel;
+       snd_soc_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
+               (1+sel)<<M98088_MICPRE_SHIFT);
+
+       return 0;
+}
+
+static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = max98088->mic2pre;
+       return 0;
+}
+
+static const unsigned int max98088_micboost_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+       2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+       SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+       SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+       SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+                       M98088_REG_35_LVL_MIC1, 5, 2, 0,
+                       max98088_mic1pre_get, max98088_mic1pre_set,
+                       max98088_micboost_tlv),
+       SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+                       M98088_REG_36_LVL_MIC2, 5, 2, 0,
+                       max98088_mic2pre_get, max98088_mic2pre_set,
+                       max98088_micboost_tlv),
+
+       SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+       SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+       SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+               0, 1, 0),
+
+       SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (w->reg == M98088_REG_35_LVL_MIC1) {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+               } else {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * The line inputs are 2-channel stereo inputs with the left
+ * and right channels sharing a common PGA power control signal.
+ */
+static int max98088_line_pga(struct snd_soc_dapm_widget *w,
+                            int event, int line, u8 channel)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       u8 *state;
+
+       BUG_ON(!((channel == 1) || (channel == 2)));
+
+       switch (line) {
+       case LINE_INA:
+               state = &max98088->ina_state;
+               break;
+       case LINE_INB:
+               state = &max98088->inb_state;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               *state |= channel;
+               snd_soc_update_bits(codec, w->reg,
+                       (1 << w->shift), (1 << w->shift));
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               *state &= ~channel;
+               if (*state == 0) {
+                       snd_soc_update_bits(codec, w->reg,
+                               (1 << w->shift), 0);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int max98088_pga_ina1_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INA, 1);
+}
+
+static int max98088_pga_ina2_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INA, 2);
+}
+
+static int max98088_pga_inb1_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INB, 1);
+}
+
+static int max98088_pga_inb2_event(struct snd_soc_dapm_widget *w,
+                                  struct snd_kcontrol *k, int event)
+{
+       return max98088_line_pga(w, event, LINE_INB, 2);
+}
+
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+               &max98088_extmic_mux),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_ina1_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_ina2_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_inb1_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_inb2_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
+               &max98088_exmode_controls),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right HP Mixer", "Left DAC2 Switch", "DACL2"  },
+       {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+       {"MIC1 Input", NULL, "MIC1"},
+       {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+       u32 rate;
+       u8  sr;
+} rate_table[] = {
+       {8000,  0x10},
+       {11025, 0x20},
+       {16000, 0x30},
+       {22050, 0x40},
+       {24000, 0x50},
+       {32000, 0x60},
+       {44100, 0x70},
+       {48000, 0x80},
+       {88200, 0x90},
+       {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate) {
+                       *value = rate_table[i].sr;
+                       return 0;
+               }
+       }
+       *value = rate_table[0].sr;
+       return -EINVAL;
+}
+
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned long long ni;
+       unsigned int rate;
+       u8 regval;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate_value(rate, &regval))
+               return -EINVAL;
+
+       snd_soc_update_bits(codec, M98088_REG_11_DAI1_CLKMODE,
+               M98088_CLKMODE_MASK, regval);
+       cdata->rate = rate;
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0) {
+                       dev_err(codec->dev, "Invalid system clock frequency\n");
+                       return -EINVAL;
+               }
+               ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate;
+               do_div(ni, (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7F);
+               snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xFF);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+               M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       unsigned long long ni;
+       unsigned int rate;
+       u8 regval;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate_value(rate, &regval))
+               return -EINVAL;
+
+       snd_soc_update_bits(codec, M98088_REG_19_DAI2_CLKMODE,
+               M98088_CLKMODE_MASK, regval);
+       cdata->rate = rate;
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0) {
+                       dev_err(codec->dev, "Invalid system clock frequency\n");
+                       return -EINVAL;
+               }
+               ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate;
+               do_div(ni, (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7F);
+               snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xFF);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+               M98088_SHDNRUN);
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       /* Requested clock frequency is already setup */
+       if (freq == max98088->sysclk)
+               return 0;
+
+       max98088->sysclk = freq; /* remember current sysclk */
+
+       /* Setup clocks for slave mode, and using the PLL
+        * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+        *         0x02 (when master clk is 20MHz to 30MHz)..
+        */
+       if ((freq >= 10000000) && (freq < 20000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+       } else if ((freq >= 20000000) && (freq < 30000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+       } else {
+               dev_err(codec->dev, "Invalid master clock frequency\n");
+               return -EINVAL;
+       }
+
+       if (snd_soc_read(codec, M98088_REG_51_PWR_SYS)  & M98088_SHDNRUN) {
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, 0);
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, M98088_SHDNRUN);
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg15val;
+       u8 reg14val = 0;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* Slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* Set to master mode */
+                       reg14val |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg14val |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg14val |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg14val |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
+                       M98088_DAI_WCI, reg14val);
+
+               reg15val = M98088_DAI_BSEL64;
+               if (max98088->digmic)
+                       reg15val |= M98088_DAI_OSR64;
+               snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+       }
+
+       return 0;
+}
+
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       u8 reg1Cval = 0;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* Slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* Set to master mode */
+                       reg1Cval |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg1Cval |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg1Cval |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg1Cval |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
+                       M98088_DAI_WCI, reg1Cval);
+
+               snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       M98088_DAI_BSEL64);
+       }
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       if (!codec->cache_sync)
+               return;
+
+       codec->cache_only = 0;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+
+       codec->cache_sync = 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF)
+                       max98088_sync_cache(codec);
+
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, M98088_MBEN);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, 0);
+               codec->cache_sync = 1;
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+static struct snd_soc_dai_driver max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+
+static int max98088_get_channel(const char *name)
+{
+       if (strcmp(name, "EQ1 Mode") == 0)
+               return 0;
+       if (strcmp(name, "EQ2 Mode") == 0)
+               return 1;
+       return -EINVAL;
+}
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !max98088->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq_cfgcnt; i++) {
+               if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq_cfg[best].name,
+               pdata->eq_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* Restore the original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !max98088->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq_cfgcnt; i++) {
+               if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq_cfg[best].name,
+               pdata->eq_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* Restore the original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       int channel = max98088_get_channel(kcontrol->id.name);
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[channel];
+
+       if (sel >= pdata->eq_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+
+       switch (channel) {
+       case 0:
+               max98088_setup_eq1(codec);
+               break;
+       case 1:
+               max98088_setup_eq2(codec);
+               break;
+       }
+
+       return 0;
+}
+
+static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       int channel = max98088_get_channel(kcontrol->id.name);
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[channel];
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *cfg;
+       unsigned int cfgcnt;
+       int i, j;
+       const char **t;
+       int ret;
+
+       struct snd_kcontrol_new controls[] = {
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->eq_enum,
+                       max98088_get_eq_enum,
+                       max98088_put_eq_enum),
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->eq_enum,
+                       max98088_get_eq_enum,
+                       max98088_put_eq_enum),
+       };
+
+       cfg = pdata->eq_cfg;
+       cfgcnt = pdata->eq_cfgcnt;
+
+       /* Setup an array of texts for the equalizer enum.
+        * This is based on Mark Brown's equalizer driver code.
+        */
+       max98088->eq_textcnt = 0;
+       max98088->eq_texts = NULL;
+       for (i = 0; i < cfgcnt; i++) {
+               for (j = 0; j < max98088->eq_textcnt; j++) {
+                       if (strcmp(cfg[i].name, max98088->eq_texts[j]) == 0)
+                               break;
+               }
+
+               if (j != max98088->eq_textcnt)
+                       continue;
+
+               /* Expand the array */
+               t = krealloc(max98088->eq_texts,
+                            sizeof(char *) * (max98088->eq_textcnt + 1),
+                            GFP_KERNEL);
+               if (t == NULL)
+                       continue;
+
+               /* Store the new entry */
+               t[max98088->eq_textcnt] = cfg[i].name;
+               max98088->eq_textcnt++;
+               max98088->eq_texts = t;
+       }
+
+       /* Now point the soc_enum to .texts array items */
+       max98088->eq_enum.texts = max98088->eq_texts;
+       max98088->eq_enum.max = max98088->eq_textcnt;
+
+       ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval = 0;
+
+       if (!pdata) {
+               dev_dbg(codec->dev, "No platform data\n");
+               return;
+       }
+
+       /* Configure mic for analog/digital mic mode */
+       if (pdata->digmic_left_mode)
+               regval |= M98088_DIGMIC_L;
+
+       if (pdata->digmic_right_mode)
+               regval |= M98088_DIGMIC_R;
+
+       max98088->digmic = (regval ? 1 : 0);
+
+       snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* Configure receiver output */
+       regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               M98088_REC_LINEMODE_MASK, regval);
+
+       /* Configure equalizers */
+       if (pdata->eq_cfgcnt)
+               max98088_handle_eq_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct snd_soc_codec *codec)
+{
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define max98088_suspend NULL
+#define max98088_resume NULL
+#endif
+
+static int max98088_probe(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       struct max98088_cdata *cdata;
+       int ret = 0;
+
+       codec->cache_sync = 1;
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+       max98088->eq_textcnt = 0;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_sel = 0;
+
+       max98088->ina_state = 0;
+       max98088->inb_state = 0;
+       max98088->ex_mode = 0;
+       max98088->digmic = 0;
+       max98088->mic1pre = 0;
+       max98088->mic2pre = 0;
+
+       ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read device revision: %d\n",
+                       ret);
+               goto err_access;
+       }
+       dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+       snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+               M98088_S1NORMAL|M98088_SDATA);
+
+       snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+               M98088_S2NORMAL|M98088_SDATA);
+
+       max98088_handle_pdata(codec);
+
+       max98088_add_widgets(codec);
+
+err_access:
+       return ret;
+}
+
+static int max98088_remove(struct snd_soc_codec *codec)
+{
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+       .set_bias_level = max98088_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(max98088_reg),
+       .reg_word_size = sizeof(u8),
+       .reg_cache_default = max98088_reg,
+       .volatile_register = max98088_volatile_register,
+};
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct max98088_priv *max98088;
+       int ret;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max98088);
+       max98088->control_data = i2c;
+       max98088->pdata = i2c->dev.platform_data;
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_max98088, &max98088_dai[0], 2);
+       if (ret < 0)
+               kfree(max98088);
+       return ret;
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "max98088",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+module_init(max98088_init);
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..e46b258
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,193 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_11_DAI1_CLKMODE, M98088_REG_19_DAI2_CLKMODE */
+       #define M98088_CLKMODE_MASK             0xFF
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+       #define M98088_DAI_BSEL64               (1<<0)
+       #define M98088_DAI_OSR64                (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+       #define M98088_S1NORMAL                 (1<<6)
+       #define M98088_S2NORMAL                 (2<<6)
+       #define M98088_SDATA                    (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+       #define M98088_DAI_DHF                  (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+       #define M98088_REC_LINEMODE             (1<<7)
+       #define M98088_REC_LINEMODE_MASK        (1<<7)
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+       #define M98088_MICPRE_MASK              (3<<5)
+       #define M98088_MICPRE_SHIFT             5
+
+/* M98088_REG_3A_LVL_HP_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+       #define M98088_EXTMIC_MASK              (3<<0)
+       #define M98088_DIGMIC_L                 (1<<5)
+       #define M98088_DIGMIC_R                 (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+       #define M98088_INAEN                    (1<<7)
+       #define M98088_INBEN                    (1<<6)
+       #define M98088_MBEN                     (1<<3)
+       #define M98088_ADLEN                    (1<<1)
+       #define M98088_ADREN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+/* Line inputs */
+#define LINE_INA  0
+#define LINE_INB  1
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+#endif
--
1.6.3.3


^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-14  3:30   ` Peter Hsiang
@ 2010-10-15 10:04       ` Liam Girdwood
  2010-10-15 10:55       ` Mark Brown
  1 sibling, 0 replies; 81+ messages in thread
From: Liam Girdwood @ 2010-10-15 10:04 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Mark Brown, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, 2010-10-13 at 20:30 -0700, Peter Hsiang wrote:
> This patch adds the MAX98088 CODEC driver.
> 
> Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
> ---
>  include/sound/max98088.h    |   50 +
>  sound/soc/codecs/Kconfig    |    4 +
>  sound/soc/codecs/Makefile   |    2 +
>  sound/soc/codecs/max98088.c | 2097 +++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/codecs/max98088.h |  193 ++++
>  5 files changed, 2346 insertions(+), 0 deletions(-)
>  create mode 100644 include/sound/max98088.h
>  create mode 100644 sound/soc/codecs/max98088.c
>  create mode 100644 sound/soc/codecs/max98088.h
> 

Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
-- 
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-10-15 10:04       ` Liam Girdwood
  0 siblings, 0 replies; 81+ messages in thread
From: Liam Girdwood @ 2010-10-15 10:04 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Brown, Peter Ujfalusi, linux-kernel,
	Mark, Jesse Marroquin

On Wed, 2010-10-13 at 20:30 -0700, Peter Hsiang wrote:
> This patch adds the MAX98088 CODEC driver.
> 
> Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
> ---
>  include/sound/max98088.h    |   50 +
>  sound/soc/codecs/Kconfig    |    4 +
>  sound/soc/codecs/Makefile   |    2 +
>  sound/soc/codecs/max98088.c | 2097 +++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/codecs/max98088.h |  193 ++++
>  5 files changed, 2346 insertions(+), 0 deletions(-)
>  create mode 100644 include/sound/max98088.h
>  create mode 100644 sound/soc/codecs/max98088.c
>  create mode 100644 sound/soc/codecs/max98088.h
> 

Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
-- 
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol
  2010-10-13 15:19           ` [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol Joe Perches
@ 2010-10-15 10:08               ` Liam Girdwood
  2010-10-15 10:39               ` Mark Brown
  1 sibling, 0 replies; 81+ messages in thread
From: Liam Girdwood @ 2010-10-15 10:08 UTC (permalink / raw)
  To: Joe Perches
  Cc: Mark Brown, Peter Hsiang, Jaroslav Kysela, Takashi Iwai,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, 2010-10-13 at 08:19 -0700, Joe Perches wrote:
> Remove unused vol from struct access_mask
> 
> Reduces object size ~3kb.
> 
> $ size sound/soc/codecs/wm8994.o*
>    text	   data	    bss	    dec	    hex	filename
>   40727	   4384	   4480	  49591	   c1b7	sound/soc/codecs/wm8994.o.new
>   43879	   4384	   4480	  52743	   ce07	sound/soc/codecs/wm8994.o.old
> 
> Signed-off-by: Joe Perches <joe@perches.com>

Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
-- 
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol
@ 2010-10-15 10:08               ` Liam Girdwood
  0 siblings, 0 replies; 81+ messages in thread
From: Liam Girdwood @ 2010-10-15 10:08 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Mark Brown, Peter Ujfalusi,
	linux-kernel, Peter Hsiang, Marroquin, Jesse

On Wed, 2010-10-13 at 08:19 -0700, Joe Perches wrote:
> Remove unused vol from struct access_mask
> 
> Reduces object size ~3kb.
> 
> $ size sound/soc/codecs/wm8994.o*
>    text	   data	    bss	    dec	    hex	filename
>   40727	   4384	   4480	  49591	   c1b7	sound/soc/codecs/wm8994.o.new
>   43879	   4384	   4480	  52743	   ce07	sound/soc/codecs/wm8994.o.old
> 
> Signed-off-by: Joe Perches <joe@perches.com>

Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
-- 
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol
  2010-10-13 15:19           ` [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol Joe Perches
@ 2010-10-15 10:39               ` Mark Brown
  2010-10-15 10:39               ` Mark Brown
  1 sibling, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-15 10:39 UTC (permalink / raw)
  To: Joe Perches
  Cc: Peter Hsiang, Jaroslav Kysela, Takashi Iwai, Liam Girdwood,
	Peter Ujfalusi, alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010 at 08:19:51AM -0700, Joe Perches wrote:
> Remove unused vol from struct access_mask

Applied, thanks.

Please try to use subject lines for your patches consistent with the
rest of the subsystem.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol
@ 2010-10-15 10:39               ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-15 10:39 UTC (permalink / raw)
  To: Joe Perches
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Peter Hsiang, Jesse Marroquin, Liam Girdwood

On Wed, Oct 13, 2010 at 08:19:51AM -0700, Joe Perches wrote:
> Remove unused vol from struct access_mask

Applied, thanks.

Please try to use subject lines for your patches consistent with the
rest of the subsystem.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-14  3:30   ` Peter Hsiang
@ 2010-10-15 10:55       ` Mark Brown
  2010-10-15 10:55       ` Mark Brown
  1 sibling, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-15 10:55 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Oct 13, 2010 at 08:30:43PM -0700, Peter Hsiang wrote:
> This patch adds the MAX98088 CODEC driver.
> 
> Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>

Applied, thanks.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-10-15 10:55       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-10-15 10:55 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Jesse Marroquin, Liam Girdwood

On Wed, Oct 13, 2010 at 08:30:43PM -0700, Peter Hsiang wrote:
> This patch adds the MAX98088 CODEC driver.
> 
> Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>

Applied, thanks.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-10-15 10:55       ` Mark Brown
@ 2010-10-15 17:23         ` Peter Hsiang
  -1 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-10-15 17:23 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Friday, Oct 15, 2010, Mark Brown wrote:
> On Wed, Oct 13, 2010 at 08:30:43PM -0700, Peter Hsiang wrote:
> > This patch adds the MAX98088 CODEC driver.
> >
> > Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
> 
> Applied, thanks.

Thanks!

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-10-15 17:23         ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-10-15 17:23 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Jesse, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Marroquin, Liam, Girdwood

On Friday, Oct 15, 2010, Mark Brown wrote:
> On Wed, Oct 13, 2010 at 08:30:43PM -0700, Peter Hsiang wrote:
> > This patch adds the MAX98088 CODEC driver.
> >
> > Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
> 
> Applied, thanks.

Thanks!

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-23 17:56     ` Peter Hsiang
@ 2010-09-23 18:38       ` Mark Brown
  -1 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-23 18:38 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Thu, Sep 23, 2010 at 10:56:38AM -0700, Peter Hsiang wrote:
> On Thu, Sep 23, 2010, Mark Brown wrote:

> > |   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

> > Currently you should be looking at for-next or for-2.6.37, though the
> > branch names will change as kernel development moves forward.

> What is the git command line path/parameter for for-next or for-2.6.37?
> Something equivalent to...
> ....pub/scm/linux/kernel/git/next/linux-next.git 

That is the git path to the repository.  for-next and for-2.6.37 are
branches in that repository.

> Why do the files in git/broonie/sound-2.6.git look older than -next?
> For example, it's missing soc-cache.c, and the codec drivers there
> use the old I/O method.  Same drivers in -next are newer and use the soc-cache.

You're looking at the wrong branch, you need to look at one of the
branches mentioned above.  git branch -r to list remote branches.  I
can't remember what the default branch is but since it changes with
every release it's not always up to date.

Developing against -next is also OK, it contains everything in the above
tree (and then some).

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-23 18:38       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-23 18:38 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Jesse Marroquin, Liam Girdwood

On Thu, Sep 23, 2010 at 10:56:38AM -0700, Peter Hsiang wrote:
> On Thu, Sep 23, 2010, Mark Brown wrote:

> > |   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

> > Currently you should be looking at for-next or for-2.6.37, though the
> > branch names will change as kernel development moves forward.

> What is the git command line path/parameter for for-next or for-2.6.37?
> Something equivalent to...
> ....pub/scm/linux/kernel/git/next/linux-next.git 

That is the git path to the repository.  for-next and for-2.6.37 are
branches in that repository.

> Why do the files in git/broonie/sound-2.6.git look older than -next?
> For example, it's missing soc-cache.c, and the codec drivers there
> use the old I/O method.  Same drivers in -next are newer and use the soc-cache.

You're looking at the wrong branch, you need to look at one of the
branches mentioned above.  git branch -r to list remote branches.  I
can't remember what the default branch is but since it changes with
every release it's not always up to date.

Developing against -next is also OK, it contains everything in the above
tree (and then some).

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-23 12:04   ` Mark Brown
@ 2010-09-23 17:56     ` Peter Hsiang
  -1 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-23 17:56 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Thu, Sep 23, 2010, Mark Brown wrote:
> On Wed, Sep 22, 2010 at 07:58:48PM -0700, Peter Hsiang wrote:
> 
> > This patch adds the MAX98088 CODEC driver.
> 
> > Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
> 
> When you submitted the driver on the 31st of August my first comment in
> the review was:
> 
> | The major issue with this is that the driver needs to be updated to
> | current ASoC versions - you should always be working against the trees
> | that are in linux-next for new drivers.  For ASoC you should be looking
> | at:
> 
> |   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
> 
> Currently you should be looking at for-next or for-2.6.37, though the
> branch names will change as kernel development moves forward.

What is the git command line path/parameter for for-next or for-2.6.37?
Something equivalent to...
....pub/scm/linux/kernel/git/next/linux-next.git 

Why do the files in git/broonie/sound-2.6.git look older than -next?
For example, it's missing soc-cache.c, and the codec drivers there
use the old I/O method.  Same drivers in -next are newer and use the soc-cache.
 
> 
> This must be rectified before the driver can be considered for inclusion
> in the kernel.  As things stand the code will not compile.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-23 17:56     ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-23 17:56 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Jesse, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Marroquin, Liam, Girdwood

On Thu, Sep 23, 2010, Mark Brown wrote:
> On Wed, Sep 22, 2010 at 07:58:48PM -0700, Peter Hsiang wrote:
> 
> > This patch adds the MAX98088 CODEC driver.
> 
> > Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
> 
> When you submitted the driver on the 31st of August my first comment in
> the review was:
> 
> | The major issue with this is that the driver needs to be updated to
> | current ASoC versions - you should always be working against the trees
> | that are in linux-next for new drivers.  For ASoC you should be looking
> | at:
> 
> |   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
> 
> Currently you should be looking at for-next or for-2.6.37, though the
> branch names will change as kernel development moves forward.

What is the git command line path/parameter for for-next or for-2.6.37?
Something equivalent to...
....pub/scm/linux/kernel/git/next/linux-next.git 

Why do the files in git/broonie/sound-2.6.git look older than -next?
For example, it's missing soc-cache.c, and the codec drivers there
use the old I/O method.  Same drivers in -next are newer and use the soc-cache.
 
> 
> This must be rectified before the driver can be considered for inclusion
> in the kernel.  As things stand the code will not compile.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-23  2:58 ` Peter Hsiang
@ 2010-09-23 12:04   ` Mark Brown
  -1 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-23 12:04 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Peter Ujfalusi,
	alsa-devel, linux-kernel, Jesse Marroquin

On Wed, Sep 22, 2010 at 07:58:48PM -0700, Peter Hsiang wrote:

> This patch adds the MAX98088 CODEC driver.

> Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>

When you submitted the driver on the 31st of August my first comment in
the review was:

| The major issue with this is that the driver needs to be updated to
| current ASoC versions - you should always be working against the trees
| that are in linux-next for new drivers.  For ASoC you should be looking
| at:

|   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

Currently you should be looking at for-next or for-2.6.37, though the
branch names will change as kernel development moves forward.

This must be rectified before the driver can be considered for inclusion
in the kernel.  As things stand the code will not compile.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-23 12:04   ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-23 12:04 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Takashi Iwai, Peter Ujfalusi, linux-kernel,
	Jesse Marroquin, Liam Girdwood

On Wed, Sep 22, 2010 at 07:58:48PM -0700, Peter Hsiang wrote:

> This patch adds the MAX98088 CODEC driver.

> Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>

When you submitted the driver on the 31st of August my first comment in
the review was:

| The major issue with this is that the driver needs to be updated to
| current ASoC versions - you should always be working against the trees
| that are in linux-next for new drivers.  For ASoC you should be looking
| at:

|   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

Currently you should be looking at for-next or for-2.6.37, though the
branch names will change as kernel development moves forward.

This must be rectified before the driver can be considered for inclusion
in the kernel.  As things stand the code will not compile.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-23  2:58 ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-23  2:58 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Peter Hsiang, Liam Girdwood,
	Mark Brown, Peter Ujfalusi
  Cc: alsa-devel, linux-kernel, Jesse Marroquin

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
---
 include/sound/max98088.h    |   54 +
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2477 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  193 ++++
 5 files changed, 2730 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..30652be
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,54 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+#define EQ_CFG_MAX 32
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq1_cfg;
+       struct max98088_eq_cfg *eq2_cfg;
+       unsigned int eq1_cfgcnt;
+       unsigned int eq2_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode:1;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microphone input
+        */
+       unsigned int digmic_left_mode:1;
+       unsigned int digmic_right_mode:1;
+
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4ccc2b7..4e6713c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS4270 if I2C
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
@@ -157,6 +158,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 23e7e2c..7184611 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..00319d6
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2477 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+static struct snd_soc_codec *max98088_codec;
+struct snd_soc_codec_device soc_codec_dev_max98088;
+
+/* Configurations associated with each channel of DAI stream */
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+       int eq_textcnt;
+       const char *eq_texts[EQ_CFG_MAX];
+       int eq_sel;
+       struct soc_enum eq_enum;
+};
+
+/* Codec private data */
+struct max98088_priv {
+       struct snd_soc_codec codec;
+       struct max98088_pdata *pdata;
+       u8 reg_cache[M98088_REG_CNT];
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       u8 power_state;
+       unsigned int ex_mode;
+       unsigned int digmic;
+       unsigned int mic1pre;
+       unsigned int mic2pre;
+       unsigned int extmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x00, 0x00, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 data[2];
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+/*
+ * For kernels compiled without unsigned long long int division
+ */
+unsigned long long int ulldiv(unsigned long long int dividend,
+                             unsigned long long int divisor)
+{
+       unsigned long long int quotient = 0;
+       int shift = 1;
+
+       BUG_ON(divisor == 0);
+
+       /* Result is 1.0 if divisor and dividend are equal */
+       if (divisor == dividend)
+               return 1;
+
+       /* Normalize divisor */
+       while (!(divisor & 0x8000000000000000ULL)) {
+               divisor <<= 1;
+               ++shift;
+       }
+
+       /* Shift and subtract */
+       while (shift--) {
+               quotient <<= 1;
+
+               if (divisor <= dividend) {
+                       dividend -= divisor;
+                       ++quotient;
+               }
+               divisor >>= 1;
+       }
+
+       /* Round up */
+       if (dividend > divisor)
+               ++quotient;
+
+       return quotient;
+}
+
+#define INA1_PGA_BIT 0x01
+#define INA2_PGA_BIT 0x02
+#define INB1_PGA_BIT 0x04
+#define INB2_PGA_BIT 0x08
+/*
+ * The INx1 and INx2 PGAs share a power control signal.
+ * This function OR's the two power events to keep an unpowered INx
+ * from turning off it's counterpart.
+ * The control names are used to identify the PGA.
+ */
+static int max98088_pga_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       u8 *state = &max98088->power_state;
+       unsigned int val;
+       unsigned int pga;
+       unsigned int mask;
+
+       BUG_ON(w->reg != M98088_REG_4C_PWR_EN_IN);
+
+       if (strncmp(w->name, "INA1", 4) == 0) {
+               pga = INA1_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INA2", 4) == 0) {
+               pga = INA2_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INB1", 4) == 0) {
+               pga = INB1_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else if (strncmp(w->name, "INB2", 4) == 0) {
+               pga = INB2_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else {
+               return -EINVAL;
+       }
+
+       if (event == SND_SOC_DAPM_POST_PMU) {
+               /* ON */
+               *state |= pga;
+
+               /* Turn on, avoiding unnecessary writes */
+               val = snd_soc_read(codec, w->reg);
+               if (!(val & (1 << w->shift))) {
+                       val |= (1 << w->shift);
+                       snd_soc_write(codec, w->reg, val);
+               }
+       } else if (event == SND_SOC_DAPM_POST_PMD) {
+               /* OFF */
+               *state &= ~pga;
+
+               /* Turn off if both are off, avoiding unnecessary writes */
+               if (!(*state & mask)) {
+                       val = snd_soc_read(codec, w->reg);
+                       if (val & (1 << w->shift)) {
+                               val &= ~(1 << w->shift);
+                               snd_soc_write(codec, w->reg, val);
+                       }
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       BUG_ON(band > 4);
+       BUG_ON(dai > 1);
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+
+       return;
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_ex_mode[] = {
+       "Off",
+       "100Hz",
+       "400Hz",
+       "600Hz",
+       "800Hz",
+       "1000Hz",
+       "200-400Hz",
+       "400-600Hz",
+       "400-800Hz",
+};
+
+static const unsigned int ex_mode_table[] = {
+       0x00,           /* disabled */
+       (0<<4)|3,       /* 100Hz */
+       (1<<4)|0,       /* 400Hz */
+       (2<<4)|0,       /* 600Hz */
+       (3<<4)|0,       /* 800Hz */
+       (4<<4)|0,       /* 1000Hz */
+       (1<<4)|1,       /* 200-400Hz */
+       (2<<4)|2,       /* 400-600Hz */
+       (3<<4)|2,       /* 400-800Hz */
+};
+
+static const struct soc_enum max98088_ex_mode_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_ex_mode), max98088_ex_mode),
+};
+
+static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(ex_mode_table))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       snd_soc_write(codec, M98088_REG_41_SPKDHP,
+               ex_mode_table[*mode]);
+
+       return 0;
+}
+
+static int max98088_ex_mode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static const char *max98088_micpre[] = {
+       "0dB",
+       "20dB",
+       "30dB",
+};
+
+static const struct soc_enum max98088_micpre_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_micpre), max98088_micpre),
+};
+
+static const char *max98088_extmic[] = {
+       "Off",
+       "MIC1",
+       "MIC2",
+};
+
+static const struct soc_enum max98088_extmic_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
+};
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic1pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic1pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic2pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic2pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_extmic_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->extmic_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_extmic))
+               return -EINVAL;
+
+       *mode = sel;
+       snd_soc_update_bits(codec, M98088_REG_48_CFG_MIC,
+               M98088_EXTMIC_MASK, sel);
+
+       return 0;
+}
+
+static int max98088_extmic_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->extmic_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       /* Analog outputs */
+
+       SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+       SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+       SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+       /* Analog inputs */
+
+       SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_ENUM_EXT("MIC1 Boost Volume", max98088_micpre_enum,
+               max98088_mic1pre_get, max98088_mic1pre_set),
+
+       SOC_ENUM_EXT("MIC2 Boost Volume", max98088_micpre_enum,
+               max98088_mic2pre_get, max98088_mic2pre_set),
+
+       SOC_ENUM_EXT("Ext MIC Switch", max98088_extmic_enum,
+               max98088_extmic_get, max98088_extmic_set),
+
+       SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       /* ADC input digital gains and volume controls */
+
+       SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       /* Equalizer */
+
+       SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       /* Excursion limiter */
+
+       SOC_ENUM_EXT("EX Limiter Mode", max98088_ex_mode_enum,
+               max98088_ex_mode_get, max98088_ex_mode_set),
+       SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+       /* Voice/music filters */
+
+       SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+       SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+               0, 1, 0),
+
+       /* Automatic level control (for both DAI1/DAI2) */
+
+       SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       /* Power limiter */
+
+       SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       /* THD distortion limiter */
+
+       SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_hp_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 status;
+
+       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+       /* powering down headphone gracefully */
+       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
+       if ((status & M98088_HPEN) == M98088_HPEN) {
+               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
+                       (status & ~M98088_HPEN));
+       }
+       schedule_timeout(msecs_to_jiffies(20));
+
+       return 0;
+}
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (w->reg == M98088_REG_35_LVL_MIC1) {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+               } else {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* DAPM widgets top level */
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA_E("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+/* DAPM AUDIO_MAP: */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right HP Mixer", "Left DAC2 Switch", "DACL2"  },
+       {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+       {"MIC1 Input", NULL, "MIC1"},
+       {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       /* set up audio path interconnects */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       u8 reg15val;
+       u8 reg14msk = 0;
+       u8 reg14val = 0;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg14msk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg14val |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg14val |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg14msk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg14msk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg14msk |= M98088_DAI_BCI;
+                       reg14val |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg14msk |= M98088_DAI_WCI;
+                       reg14val |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       reg14msk, reg14val);
+
+               reg15val = M98088_DAI_BSEL64;
+               if (max98088->digmic)
+                       reg15val |= M98088_DAI_OSR64;
+               snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+       }
+
+       return 0;
+}
+
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       u8 reg1Cmsk = 0;
+       u8 reg1Cval = 0;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg1Cmsk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg1Cval |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg1Cval |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg1Cmsk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg1Cmsk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg1Cmsk |= M98088_DAI_BCI;
+                       reg1Cval |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg1Cmsk |= M98088_DAI_WCI;
+                       reg1Cval |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       reg1Cmsk, reg1Cval);
+
+               snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       M98088_DAI_BSEL64);
+       }
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+
+       /* requested clock frequency is already setup */
+       if (freq == max98088->sysclk)
+               return 0;
+
+       max98088->sysclk = freq; /* remember current sysclk */
+
+       /* setup clocks for slave mode, and using the PLL
+        * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+        *         0x02 (when master clk is 20MHz to 30MHz)..
+        */
+       if ((freq >= 10000000) && (freq < 20000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+       } else if ((freq >= 20000000) && (freq < 30000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+       } else {
+               dev_err(codec->dev, "Invalid master clock frequency\n");
+               return -EINVAL;
+       }
+
+       if (snd_soc_read(codec, M98088_REG_51_PWR_SYS)  & M98088_SHDNRUN) {
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, 0);
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       0, M98088_SHDNRUN);
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+       u32 rate;
+       u8  sr;
+} rate_table[] = {
+       {8000,  0x10},
+       {11025, 0x20},
+       {16000, 0x30},
+       {22050, 0x40},
+       {24000, 0x50},
+       {32000, 0x60},
+       {44100, 0x70},
+       {48000, 0x80},
+       {88200, 0x90},
+       {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate) {
+                       *value = rate_table[i].sr;
+                       return 0;
+               }
+       }
+       *value = rate_table[0].sr;
+       return -EINVAL;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI2 SR2 value for the DSP */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_19_DAI2_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       int i;
+
+       if (!codec->cache_sync)
+               return;
+
+       codec->cache_only = 0;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+
+       codec->cache_sync = 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               max98088_sync_cache(codec);
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, M98088_MBEN);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, 0);
+#ifdef CONFIG_REGULATOR
+               codec->cache_sync = 1;
+#endif
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+struct snd_soc_dai max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+EXPORT_SYMBOL_GPL(max98088_dai);
+
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq1_cfg[best].name,
+               pdata->eq1_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq1_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq2_cfg[best].name,
+               pdata->eq2_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq2_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->eq1_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq1(codec);
+       return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->eq2_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq2(codec);
+       return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_handle_eq1_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq1control =
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->dai[0].eq_enum,
+                       max98088_get_eq1_enum,
+                       max98088_put_eq1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq1_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->eq_texts[i] = pdata->eq1_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       BUG_ON(&max98088->codec == 0);
+       BUG_ON((max98088->codec).card == 0);
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq2control =
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->dai[1].eq_enum,
+                       max98088_get_eq2_enum,
+                       max98088_put_eq2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq2_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               cdata->eq_texts[i] = pdata->eq2_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq2control, 1);
+       if (ret != 0)
+               printk(KERN_ERR "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval = 0;
+
+       if (!pdata)
+               return;
+
+       /* configure mic for analog/digital mic mode */
+       if (pdata->digmic_left_mode)
+               regval |= M98088_DIGMIC_L;
+
+       if (pdata->digmic_right_mode)
+               regval |= M98088_DIGMIC_R;
+
+       max98088->digmic = (regval ? 1 : 0);
+
+       snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* configure receiver output */
+       regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               M98088_REC_LINEMODE_MASK, regval);
+
+       /* configure equalizers */
+       if (pdata->eq1_cfgcnt)
+               max98088_handle_eq1_pdata(max98088);
+
+       if (pdata->eq2_cfgcnt)
+               max98088_handle_eq2_pdata(max98088);
+}
+
+static int max98088_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 *cache = codec->reg_cache;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < M98088_REG_CNT; i++) {
+               if (i == M98088_REG_51_PWR_SYS)
+                       continue;
+
+               if (!max98088_access[i].writable)
+                       continue;
+
+               max98088_hw_write(codec, i, cache[i]);
+       }
+
+       /* now enter into the resume mode bias level */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+
+/*
+ * Make sure that a max98088 is attached to the I2C bus.
+ */
+static int max98088_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+       int ret = 0;
+
+       dev_printk(KERN_INFO, &pdev->dev, "MAX98088 Audio CODEC\n");
+
+       if (!max98088_codec) {
+               dev_err(&pdev->dev, "Codec device is not registered\n");
+               return -EINVAL;
+       }
+
+       socdev->card->codec = max98088_codec;
+       codec = max98088_codec;
+       max98088 = codec->private_data;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       /* install additional amixer controls: EQ and excursion limiter */
+       if (max98088->pdata)
+               max98088_handle_pdata(max98088);
+       else
+               dev_err(&pdev->dev, "No platform data\n");
+
+       max98088_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register card\n");
+               goto card_err;
+       }
+
+       return 0;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+pcm_err:
+       return ret;
+}
+
+static int max98088_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       if (codec->control_data)
+               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+/* expose to machine driver */
+struct snd_soc_codec_device soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98088);
+
+
+static int max98088_register(struct max98088_priv *max98088,
+                            enum snd_soc_control_type control)
+
+{
+       int ret, i, version;
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_cdata *cdata;
+
+       if (max98088_codec) {
+               dev_err(codec->dev, "Another MAX98088 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->name = "MAX98088";
+       codec->owner = THIS_MODULE;
+
+       /* setup DAPM event bias level and handler function */
+       codec->bias_level = SND_SOC_BIAS_STANDBY;
+       codec->set_bias_level = max98088_set_bias_level;
+
+       codec->dai = max98088_dai;
+       codec->num_dai = ARRAY_SIZE(max98088_dai);
+       codec->private_data = max98088;
+
+       codec->reg_cache_size = M98088_REG_CNT;
+       codec->reg_cache = &max98088->reg_cache;
+       codec->volatile_register = max98088_volatile_register;
+       codec->cache_sync = 1;
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       version = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+       dev_dbg(codec->dev, "MAX98088 revision 0x%02X\n", version);
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       max98088->power_state = 0; /* INA INB power enable state */
+       max98088->ex_mode = 0; /* excursion limiter mode */
+       max98088->digmic = 0; /* 0=analog, 1=digital */
+       max98088->mic1pre = 0;
+       max98088->mic2pre = 0;
+
+       snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+               M98088_S1NORMAL|M98088_SDATA);
+
+       snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+               M98088_S2NORMAL|M98088_SDATA);
+
+       /* power on device */
+       max98088_codec = codec;
+       for (i = 0; i < codec->num_dai; i++)
+               max98088_dai[i].dev = codec->dev;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dais(&max98088_dai[0], codec->num_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+               goto err_codec;
+       }
+
+       return ret;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(max98088);
+
+       return ret;
+}
+
+static void max98088_unregister(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       snd_soc_unregister_dais(max98088_dai, codec->num_dai);
+       snd_soc_unregister_codec(codec);
+       kfree(max98088);
+       max98088_codec = NULL;
+}
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       /* codec structure is inside the private data */
+       codec = &(max98088->codec);
+       codec->hw_write = (hw_write_t) i2c_master_send;
+
+       codec->private_data = max98088;
+       i2c_set_clientdata(i2c, max98088);
+       codec->control_data = i2c;
+
+       max98088->pdata = i2c->dev.platform_data;
+
+       codec->dev = &i2c->dev;
+
+       return max98088_register(max98088, SND_SOC_I2C);
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       struct max98088_priv *max98088 = i2c_get_clientdata(client);
+       max98088_unregister(max98088);
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "MAX98088 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+
+module_init(max98088_init);
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..bf6fae9
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,193 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+       #define M98088_DAI_BSEL64               (1<<0)
+       #define M98088_DAI_OSR64                (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+       #define M98088_S1NORMAL                 (1<<6)
+       #define M98088_S2NORMAL                 (2<<6)
+       #define M98088_SDATA                    (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+       #define M98088_DAI_DHF                  (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+       #define M98088_REC_LINEMODE             (1<<7)
+       #define M98088_REC_LINEMODE_MASK        (1<<7)
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+       #define M98088_MICPRE_MASK              (3<<5)
+       #define M98088_MICPRE_SHIFT             5
+
+/* M98088_REG_3A_LVL_HP_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+       #define M98088_EXTMIC_MASK              (3<<0)
+       #define M98088_DIGMIC_L                 (1<<5)
+       #define M98088_DIGMIC_R                 (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+       #define M98088_INAEN                    (1<<7)
+       #define M98088_INBEN                    (1<<6)
+       #define M98088_MBEN                     (1<<3)
+       #define M98088_ADLEN                    (1<<1)
+       #define M98088_ADREN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+struct max98088_setup_data {
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai max98088_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_max98088;
+
+#endif
--
1.6.3.3


^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-23  2:58 ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-23  2:58 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Peter Hsiang, Liam Girdwood, Mark Brown
  Cc: alsa-devel, linux-kernel, Jesse Marroquin

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang@maxim-ic.com>
---
 include/sound/max98088.h    |   54 +
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2477 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  193 ++++
 5 files changed, 2730 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..30652be
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,54 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+#define EQ_CFG_MAX 32
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq1_cfg;
+       struct max98088_eq_cfg *eq2_cfg;
+       unsigned int eq1_cfgcnt;
+       unsigned int eq2_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode:1;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microphone input
+        */
+       unsigned int digmic_left_mode:1;
+       unsigned int digmic_right_mode:1;
+
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4ccc2b7..4e6713c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS4270 if I2C
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
@@ -157,6 +158,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 23e7e2c..7184611 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..00319d6
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2477 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+static struct snd_soc_codec *max98088_codec;
+struct snd_soc_codec_device soc_codec_dev_max98088;
+
+/* Configurations associated with each channel of DAI stream */
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+       int eq_textcnt;
+       const char *eq_texts[EQ_CFG_MAX];
+       int eq_sel;
+       struct soc_enum eq_enum;
+};
+
+/* Codec private data */
+struct max98088_priv {
+       struct snd_soc_codec codec;
+       struct max98088_pdata *pdata;
+       u8 reg_cache[M98088_REG_CNT];
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       u8 power_state;
+       unsigned int ex_mode;
+       unsigned int digmic;
+       unsigned int mic1pre;
+       unsigned int mic2pre;
+       unsigned int extmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x00, 0x00, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 data[2];
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+/*
+ * For kernels compiled without unsigned long long int division
+ */
+unsigned long long int ulldiv(unsigned long long int dividend,
+                             unsigned long long int divisor)
+{
+       unsigned long long int quotient = 0;
+       int shift = 1;
+
+       BUG_ON(divisor == 0);
+
+       /* Result is 1.0 if divisor and dividend are equal */
+       if (divisor == dividend)
+               return 1;
+
+       /* Normalize divisor */
+       while (!(divisor & 0x8000000000000000ULL)) {
+               divisor <<= 1;
+               ++shift;
+       }
+
+       /* Shift and subtract */
+       while (shift--) {
+               quotient <<= 1;
+
+               if (divisor <= dividend) {
+                       dividend -= divisor;
+                       ++quotient;
+               }
+               divisor >>= 1;
+       }
+
+       /* Round up */
+       if (dividend > divisor)
+               ++quotient;
+
+       return quotient;
+}
+
+#define INA1_PGA_BIT 0x01
+#define INA2_PGA_BIT 0x02
+#define INB1_PGA_BIT 0x04
+#define INB2_PGA_BIT 0x08
+/*
+ * The INx1 and INx2 PGAs share a power control signal.
+ * This function OR's the two power events to keep an unpowered INx
+ * from turning off it's counterpart.
+ * The control names are used to identify the PGA.
+ */
+static int max98088_pga_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       u8 *state = &max98088->power_state;
+       unsigned int val;
+       unsigned int pga;
+       unsigned int mask;
+
+       BUG_ON(w->reg != M98088_REG_4C_PWR_EN_IN);
+
+       if (strncmp(w->name, "INA1", 4) == 0) {
+               pga = INA1_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INA2", 4) == 0) {
+               pga = INA2_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INB1", 4) == 0) {
+               pga = INB1_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else if (strncmp(w->name, "INB2", 4) == 0) {
+               pga = INB2_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else {
+               return -EINVAL;
+       }
+
+       if (event == SND_SOC_DAPM_POST_PMU) {
+               /* ON */
+               *state |= pga;
+
+               /* Turn on, avoiding unnecessary writes */
+               val = snd_soc_read(codec, w->reg);
+               if (!(val & (1 << w->shift))) {
+                       val |= (1 << w->shift);
+                       snd_soc_write(codec, w->reg, val);
+               }
+       } else if (event == SND_SOC_DAPM_POST_PMD) {
+               /* OFF */
+               *state &= ~pga;
+
+               /* Turn off if both are off, avoiding unnecessary writes */
+               if (!(*state & mask)) {
+                       val = snd_soc_read(codec, w->reg);
+                       if (val & (1 << w->shift)) {
+                               val &= ~(1 << w->shift);
+                               snd_soc_write(codec, w->reg, val);
+                       }
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       BUG_ON(band > 4);
+       BUG_ON(dai > 1);
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+
+       return;
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_ex_mode[] = {
+       "Off",
+       "100Hz",
+       "400Hz",
+       "600Hz",
+       "800Hz",
+       "1000Hz",
+       "200-400Hz",
+       "400-600Hz",
+       "400-800Hz",
+};
+
+static const unsigned int ex_mode_table[] = {
+       0x00,           /* disabled */
+       (0<<4)|3,       /* 100Hz */
+       (1<<4)|0,       /* 400Hz */
+       (2<<4)|0,       /* 600Hz */
+       (3<<4)|0,       /* 800Hz */
+       (4<<4)|0,       /* 1000Hz */
+       (1<<4)|1,       /* 200-400Hz */
+       (2<<4)|2,       /* 400-600Hz */
+       (3<<4)|2,       /* 400-800Hz */
+};
+
+static const struct soc_enum max98088_ex_mode_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_ex_mode), max98088_ex_mode),
+};
+
+static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(ex_mode_table))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       snd_soc_write(codec, M98088_REG_41_SPKDHP,
+               ex_mode_table[*mode]);
+
+       return 0;
+}
+
+static int max98088_ex_mode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static const char *max98088_micpre[] = {
+       "0dB",
+       "20dB",
+       "30dB",
+};
+
+static const struct soc_enum max98088_micpre_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_micpre), max98088_micpre),
+};
+
+static const char *max98088_extmic[] = {
+       "Off",
+       "MIC1",
+       "MIC2",
+};
+
+static const struct soc_enum max98088_extmic_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98088_extmic), max98088_extmic),
+};
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic1pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic1pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic2pre;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_micpre))
+               return -EINVAL;
+
+       *mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->mic2pre;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static int max98088_extmic_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->extmic_mode;
+       int sel = ucontrol->value.integer.value[0];
+
+       if (sel >= ARRAY_SIZE(max98088_extmic))
+               return -EINVAL;
+
+       *mode = sel;
+       snd_soc_update_bits(codec, M98088_REG_48_CFG_MIC,
+               M98088_EXTMIC_MASK, sel);
+
+       return 0;
+}
+
+static int max98088_extmic_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->extmic_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       /* Analog outputs */
+
+       SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+       SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+       SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+       /* Analog inputs */
+
+       SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_ENUM_EXT("MIC1 Boost Volume", max98088_micpre_enum,
+               max98088_mic1pre_get, max98088_mic1pre_set),
+
+       SOC_ENUM_EXT("MIC2 Boost Volume", max98088_micpre_enum,
+               max98088_mic2pre_get, max98088_mic2pre_set),
+
+       SOC_ENUM_EXT("Ext MIC Switch", max98088_extmic_enum,
+               max98088_extmic_get, max98088_extmic_set),
+
+       SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       /* ADC input digital gains and volume controls */
+
+       SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       /* Equalizer */
+
+       SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       /* Excursion limiter */
+
+       SOC_ENUM_EXT("EX Limiter Mode", max98088_ex_mode_enum,
+               max98088_ex_mode_get, max98088_ex_mode_set),
+       SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+       /* Voice/music filters */
+
+       SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+       SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+               0, 1, 0),
+
+       /* Automatic level control (for both DAI1/DAI2) */
+
+       SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       /* Power limiter */
+
+       SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       /* THD distortion limiter */
+
+       SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_hp_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 status;
+
+       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+       /* powering down headphone gracefully */
+       status = snd_soc_read(codec, M98088_REG_4D_PWR_EN_OUT);
+       if ((status & M98088_HPEN) == M98088_HPEN) {
+               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
+                       (status & ~M98088_HPEN));
+       }
+       schedule_timeout(msecs_to_jiffies(20));
+
+       return 0;
+}
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (w->reg == M98088_REG_35_LVL_MIC1) {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+               } else {
+                       snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+                               (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* DAPM widgets top level */
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA_E("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+               5, 0, NULL, 0, max98088_mic_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+/* DAPM AUDIO_MAP: */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right HP Mixer", "Left DAC2 Switch", "DACL2"  },
+       {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+       {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+       {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+       {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+       {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+       {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+       {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+       {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+       /* inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+       {"MIC1 Input", NULL, "MIC1"},
+       {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       /* set up audio path interconnects */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       u8 reg15val;
+       u8 reg14msk = 0;
+       u8 reg14val = 0;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg14msk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg14val |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg14val |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg14msk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg14msk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg14msk |= M98088_DAI_BCI;
+                       reg14val |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg14msk |= M98088_DAI_WCI;
+                       reg14val |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       reg14msk, reg14val);
+
+               reg15val = M98088_DAI_BSEL64;
+               if (max98088->digmic)
+                       reg15val |= M98088_DAI_OSR64;
+               snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+       }
+
+       return 0;
+}
+
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       u8 reg1Cmsk = 0;
+       u8 reg1Cval = 0;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* mask MAS to select slave mode */
+                       reg1Cmsk |= M98088_DAI_MAS;
+                       /* slave mode PLL */
+                       snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* set to master mode */
+                       reg1Cval |= M98088_DAI_MAS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       reg1Cval |= M98088_DAI_DLY;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       reg1Cmsk |= M98088_DAI_DLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       reg1Cmsk |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       reg1Cmsk |= M98088_DAI_BCI;
+                       reg1Cval |= M98088_DAI_WCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       reg1Cmsk |= M98088_DAI_WCI;
+                       reg1Cval |= M98088_DAI_BCI;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       reg1Cmsk, reg1Cval);
+
+               snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       M98088_DAI_BSEL64);
+       }
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+
+       /* requested clock frequency is already setup */
+       if (freq == max98088->sysclk)
+               return 0;
+
+       max98088->sysclk = freq; /* remember current sysclk */
+
+       /* setup clocks for slave mode, and using the PLL
+        * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+        *         0x02 (when master clk is 20MHz to 30MHz)..
+        */
+       if ((freq >= 10000000) && (freq < 20000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+       } else if ((freq >= 20000000) && (freq < 30000000)) {
+               snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+       } else {
+               dev_err(codec->dev, "Invalid master clock frequency\n");
+               return -EINVAL;
+       }
+
+       if (snd_soc_read(codec, M98088_REG_51_PWR_SYS)  & M98088_SHDNRUN) {
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN, 0);
+               snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                       0, M98088_SHDNRUN);
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+       u32 rate;
+       u8  sr;
+} rate_table[] = {
+       {8000,  0x10},
+       {11025, 0x20},
+       {16000, 0x30},
+       {22050, 0x40},
+       {24000, 0x50},
+       {32000, 0x60},
+       {44100, 0x70},
+       {48000, 0x80},
+       {88200, 0x90},
+       {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate) {
+                       *value = rate_table[i].sr;
+                       return 0;
+               }
+       }
+       *value = rate_table[0].sr;
+       return -EINVAL;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_11_DAI1_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 regval;
+       u16 ni;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, M98088_DAI_WS);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI2 SR2 value for the DSP */
+               if (rate_value(rate, &regval))
+                       return -EINVAL;
+
+               snd_soc_write(codec, M98088_REG_19_DAI2_CLKMODE, regval);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               if (max98088->sysclk == 0)
+                       return -EINVAL;
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       M98088_DAI_DHF, M98088_DAI_DHF);
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       int i;
+
+       if (!codec->cache_sync)
+               return;
+
+       codec->cache_only = 0;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+
+       codec->cache_sync = 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               max98088_sync_cache(codec);
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, M98088_MBEN);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+                               M98088_MBEN, 0);
+#ifdef CONFIG_REGULATOR
+               codec->cache_sync = 1;
+#endif
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+struct snd_soc_dai max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+EXPORT_SYMBOL_GPL(max98088_dai);
+
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq1_cfg[best].name,
+               pdata->eq1_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq1_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq2_cfg[best].name,
+               pdata->eq2_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq2_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->eq1_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq1(codec);
+       return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->eq2_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq2(codec);
+       return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_handle_eq1_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq1control =
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->dai[0].eq_enum,
+                       max98088_get_eq1_enum,
+                       max98088_put_eq1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq1_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->eq_texts[i] = pdata->eq1_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       BUG_ON(&max98088->codec == 0);
+       BUG_ON((max98088->codec).card == 0);
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq2control =
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->dai[1].eq_enum,
+                       max98088_get_eq2_enum,
+                       max98088_put_eq2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq2_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               cdata->eq_texts[i] = pdata->eq2_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq2control, 1);
+       if (ret != 0)
+               printk(KERN_ERR "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval = 0;
+
+       if (!pdata)
+               return;
+
+       /* configure mic for analog/digital mic mode */
+       if (pdata->digmic_left_mode)
+               regval |= M98088_DIGMIC_L;
+
+       if (pdata->digmic_right_mode)
+               regval |= M98088_DIGMIC_R;
+
+       max98088->digmic = (regval ? 1 : 0);
+
+       snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* configure receiver output */
+       regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               M98088_REC_LINEMODE_MASK, regval);
+
+       /* configure equalizers */
+       if (pdata->eq1_cfgcnt)
+               max98088_handle_eq1_pdata(max98088);
+
+       if (pdata->eq2_cfgcnt)
+               max98088_handle_eq2_pdata(max98088);
+}
+
+static int max98088_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 *cache = codec->reg_cache;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < M98088_REG_CNT; i++) {
+               if (i == M98088_REG_51_PWR_SYS)
+                       continue;
+
+               if (!max98088_access[i].writable)
+                       continue;
+
+               max98088_hw_write(codec, i, cache[i]);
+       }
+
+       /* now enter into the resume mode bias level */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+
+/*
+ * Make sure that a max98088 is attached to the I2C bus.
+ */
+static int max98088_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+       int ret = 0;
+
+       dev_printk(KERN_INFO, &pdev->dev, "MAX98088 Audio CODEC\n");
+
+       if (!max98088_codec) {
+               dev_err(&pdev->dev, "Codec device is not registered\n");
+               return -EINVAL;
+       }
+
+       socdev->card->codec = max98088_codec;
+       codec = max98088_codec;
+       max98088 = codec->private_data;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       /* install additional amixer controls: EQ and excursion limiter */
+       if (max98088->pdata)
+               max98088_handle_pdata(max98088);
+       else
+               dev_err(&pdev->dev, "No platform data\n");
+
+       max98088_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register card\n");
+               goto card_err;
+       }
+
+       return 0;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+pcm_err:
+       return ret;
+}
+
+static int max98088_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       if (codec->control_data)
+               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+/* expose to machine driver */
+struct snd_soc_codec_device soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98088);
+
+
+static int max98088_register(struct max98088_priv *max98088,
+                            enum snd_soc_control_type control)
+
+{
+       int ret, i, version;
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_cdata *cdata;
+
+       if (max98088_codec) {
+               dev_err(codec->dev, "Another MAX98088 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->name = "MAX98088";
+       codec->owner = THIS_MODULE;
+
+       /* setup DAPM event bias level and handler function */
+       codec->bias_level = SND_SOC_BIAS_STANDBY;
+       codec->set_bias_level = max98088_set_bias_level;
+
+       codec->dai = max98088_dai;
+       codec->num_dai = ARRAY_SIZE(max98088_dai);
+       codec->private_data = max98088;
+
+       codec->reg_cache_size = M98088_REG_CNT;
+       codec->reg_cache = &max98088->reg_cache;
+       codec->volatile_register = max98088_volatile_register;
+       codec->cache_sync = 1;
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       version = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+       dev_dbg(codec->dev, "MAX98088 revision 0x%02X\n", version);
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+
+       max98088->power_state = 0; /* INA INB power enable state */
+       max98088->ex_mode = 0; /* excursion limiter mode */
+       max98088->digmic = 0; /* 0=analog, 1=digital */
+       max98088->mic1pre = 0;
+       max98088->mic2pre = 0;
+
+       snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+               M98088_S1NORMAL|M98088_SDATA);
+
+       snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+               M98088_S2NORMAL|M98088_SDATA);
+
+       /* power on device */
+       max98088_codec = codec;
+       for (i = 0; i < codec->num_dai; i++)
+               max98088_dai[i].dev = codec->dev;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dais(&max98088_dai[0], codec->num_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+               goto err_codec;
+       }
+
+       return ret;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(max98088);
+
+       return ret;
+}
+
+static void max98088_unregister(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       snd_soc_unregister_dais(max98088_dai, codec->num_dai);
+       snd_soc_unregister_codec(codec);
+       kfree(max98088);
+       max98088_codec = NULL;
+}
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       /* codec structure is inside the private data */
+       codec = &(max98088->codec);
+       codec->hw_write = (hw_write_t) i2c_master_send;
+
+       codec->private_data = max98088;
+       i2c_set_clientdata(i2c, max98088);
+       codec->control_data = i2c;
+
+       max98088->pdata = i2c->dev.platform_data;
+
+       codec->dev = &i2c->dev;
+
+       return max98088_register(max98088, SND_SOC_I2C);
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       struct max98088_priv *max98088 = i2c_get_clientdata(client);
+       max98088_unregister(max98088);
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "MAX98088 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+
+module_init(max98088_init);
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..bf6fae9
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,193 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+       #define M98088_DAI_BSEL64               (1<<0)
+       #define M98088_DAI_OSR64                (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+       #define M98088_S1NORMAL                 (1<<6)
+       #define M98088_S2NORMAL                 (2<<6)
+       #define M98088_SDATA                    (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+       #define M98088_DAI_DHF                  (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+       #define M98088_REC_LINEMODE             (1<<7)
+       #define M98088_REC_LINEMODE_MASK        (1<<7)
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+       #define M98088_MICPRE_MASK              (3<<5)
+       #define M98088_MICPRE_SHIFT             5
+
+/* M98088_REG_3A_LVL_HP_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+       #define M98088_EXTMIC_MASK              (3<<0)
+       #define M98088_DIGMIC_L                 (1<<5)
+       #define M98088_DIGMIC_R                 (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+       #define M98088_INAEN                    (1<<7)
+       #define M98088_INBEN                    (1<<6)
+       #define M98088_MBEN                     (1<<3)
+       #define M98088_ADLEN                    (1<<1)
+       #define M98088_ADREN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+struct max98088_setup_data {
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai max98088_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_max98088;
+
+#endif
--
1.6.3.3

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-22  2:49         ` Peter Hsiang
@ 2010-09-22 10:38           ` Mark Brown
  -1 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-22 10:38 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Jesse Marroquin, Liam Girdwood,
	Peter Ujfalusi, Joonyoung Shim, alsa-devel, linux-kernel

On Tue, Sep 21, 2010 at 07:49:30PM -0700, Peter Hsiang wrote:
> On Fri, Sep 03, 2010, Mark Brown wrote:

> > No, you should use the features of the current kernel.  For your
> > backport you can do things like supply soc-cache.c as well.

> The soc-cache.c in the latest kernel-next version supports codecs
> with volatile registers in 16 bit mode, but not for the 8 bit mode.
> See snd_soc_8_8_write.

> The source code comments confirmed this:
> "Note at present this code can not be used by CODECs with 
> volatile registers."  

> For this codec (with volatile registers), should we use the read
> and write functions in the codec driver until the 8-bit mode is
> supported by Linux soc-cache.c?

You should modify soc-cache.c to support volatile registers if you need
this; it's not been implemented because nobody needed it yet (someone
may get to it before you get round to resubmitting, but if they haven't
then just extend the core functionality).

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-22 10:38           ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-22 10:38 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Joonyoung Shim, Takashi Iwai, Peter Ujfalusi,
	linux-kernel, Jesse Marroquin, Liam Girdwood

On Tue, Sep 21, 2010 at 07:49:30PM -0700, Peter Hsiang wrote:
> On Fri, Sep 03, 2010, Mark Brown wrote:

> > No, you should use the features of the current kernel.  For your
> > backport you can do things like supply soc-cache.c as well.

> The soc-cache.c in the latest kernel-next version supports codecs
> with volatile registers in 16 bit mode, but not for the 8 bit mode.
> See snd_soc_8_8_write.

> The source code comments confirmed this:
> "Note at present this code can not be used by CODECs with 
> volatile registers."  

> For this codec (with volatile registers), should we use the read
> and write functions in the codec driver until the 8-bit mode is
> supported by Linux soc-cache.c?

You should modify soc-cache.c to support volatile registers if you need
this; it's not been implemented because nobody needed it yet (someone
may get to it before you get round to resubmitting, but if they haven't
then just extend the core functionality).

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-03 10:17       ` Mark Brown
@ 2010-09-22  2:49         ` Peter Hsiang
  -1 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-22  2:49 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Jesse Marroquin, Liam Girdwood,
	Peter Ujfalusi, Joonyoung Shim, alsa-devel, linux-kernel

On Fri, Sep 03, 2010, Mark Brown wrote:
> On Thu, Sep 02, 2010 at 04:30:14PM -0700, Peter Hsiang wrote:
> > On Wed, Sep 01, 2010, Mark Brown wrote:
> 
> > > > +/*
> > > > + * Read the MAX98088 I2C register space
> > > > + * Note: this driver source code is backward compatible to kernel
> > > > + * version 2.6.32.
> > > > + */
> > > > +static unsigned int max98088_read(struct snd_soc_codec *codec,
> > > > +                                 unsigned int reg)
> 
> > > Just use soc-cache.  Compatibility code for older kernels isn't really
> > > acceptable for mainline due to the increased maintainance burden and it
> > > will at best result in a driver that doesn't work as well as possible.
> 
> > This integrated registers feature is not available in 2.6.32
> > Is it ok with you to keep it this way for this release?  Thanks.
> 
> No, you should use the features of the current kernel.  For your
> backport you can do things like supply soc-cache.c as well.

The soc-cache.c in the latest kernel-next version supports codecs
with volatile registers in 16 bit mode, but not for the 8 bit mode.
See snd_soc_8_8_write.

The source code comments confirmed this:
"Note at present this code can not be used by CODECs with 
volatile registers."  

For this codec (with volatile registers), should we use the read
and write functions in the codec driver until the 8-bit mode is
supported by Linux soc-cache.c?


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-22  2:49         ` Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-09-22  2:49 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Joonyoung, Shim, Takashi Iwai, Peter Ujfalusi,
	linux-kernel, Marroquin, Liam Girdwood, Jesse

On Fri, Sep 03, 2010, Mark Brown wrote:
> On Thu, Sep 02, 2010 at 04:30:14PM -0700, Peter Hsiang wrote:
> > On Wed, Sep 01, 2010, Mark Brown wrote:
> 
> > > > +/*
> > > > + * Read the MAX98088 I2C register space
> > > > + * Note: this driver source code is backward compatible to kernel
> > > > + * version 2.6.32.
> > > > + */
> > > > +static unsigned int max98088_read(struct snd_soc_codec *codec,
> > > > +                                 unsigned int reg)
> 
> > > Just use soc-cache.  Compatibility code for older kernels isn't really
> > > acceptable for mainline due to the increased maintainance burden and it
> > > will at best result in a driver that doesn't work as well as possible.
> 
> > This integrated registers feature is not available in 2.6.32
> > Is it ok with you to keep it this way for this release?  Thanks.
> 
> No, you should use the features of the current kernel.  For your
> backport you can do things like supply soc-cache.c as well.

The soc-cache.c in the latest kernel-next version supports codecs
with volatile registers in 16 bit mode, but not for the 8 bit mode.
See snd_soc_8_8_write.

The source code comments confirmed this:
"Note at present this code can not be used by CODECs with 
volatile registers."  

For this codec (with volatile registers), should we use the read
and write functions in the codec driver until the 8-bit mode is
supported by Linux soc-cache.c?

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-02 23:30   ` Peter Hsiang
@ 2010-09-03 10:17       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-03 10:17 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Jesse Marroquin, Liam Girdwood,
	Peter Ujfalusi, Joonyoung Shim, alsa-devel, linux-kernel

On Thu, Sep 02, 2010 at 04:30:14PM -0700, Peter Hsiang wrote:
> On Wed, Sep 01, 2010, Mark Brown wrote:

> >   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

> Thanks for the git location you provided.  What version of Linux
> kernel does this git/broonie/sound-2.6.git version correspond to?
> Is it newer than even the latest kernel-next?

This is what's going into -next, it will often be trivially ahead of
-next with the patches addded since -next was last built but not
meaningfully so.

> Some of the ASoC API functions and features in the latest version of
> the kernel you require us to use are not in the popular stable kernel
> version 2.6.32 that people are using today to build products.
> Our intent is to release driver code that will work in both the current
> and latest kernel versions.

This is not how Linux development works, and in many cases will be
impossible as internal APIs are frequently removed from the
Linux kernel or changed incompatibly.

> Would it not be a good idea to use the API functions that exist in
> both the very latest and the currently popular kernel version?

Like I say this is just not possible in many cases.

> Does kernel.org provide a channel for releasing codec drivers that
> can be backward compatible to a popular earlier kernel version?

No, and it's not clear that a single approach can be adopted since newer
devices often drive enhancements to the core APIs so the approaches that
work best to integrate with older kernels vary depending on the particular 
devices and kernel versions involved.

> > > +       /* Bypass option for INA to MIC1 connection
> > > +        * 0 = normal setting
> > > +        * 1 = bypass enabled
> > > +        */
> > > +       unsigned int ina_to_mic1_bypass;
> > > +
> > > +       /* Bypass option for MIC1 to MIC2 connection

> > What are these - they sound like something I'd expect to be configurable
> > at runtime?

Please engage with questions like this.

> > > +/*
> > > + * Read the MAX98088 I2C register space
> > > + * Note: this driver source code is backward compatible to kernel
> > > + * version 2.6.32.
> > > + */
> > > +static unsigned int max98088_read(struct snd_soc_codec *codec,
> > > +                                 unsigned int reg)

> > Just use soc-cache.  Compatibility code for older kernels isn't really
> > acceptable for mainline due to the increased maintainance burden and it
> > will at best result in a driver that doesn't work as well as possible.

> This integrated registers feature is not available in 2.6.32
> Is it ok with you to keep it this way for this release?  Thanks.

No, you should use the features of the current kernel.  For your
backport you can do things like supply soc-cache.c as well.

> > > +       client = (struct i2c_client *)codec->control_data;

> > No need to cast away from void.

> Ok will do - but no harm in being informative :)

There is actually - if you have a cast then if you have done something
broken by mistake the compiler is much less likely to generate a warning
since the cast says you know what you're doing.

> > > +       "user-400Hz",
> > > +       "user-600Hz",
> > > +       "user-800Hz",
> > > +       "user-1000Hz"

> > These user options look very suspicous.

> The first 8 options are settings pre-defined in hardware that the
> user can choose from.
> The last 4 are user programmable options, and thus the name 'user'.

What happens if the user didn't supply any programmable options?  Would
it not be better to allow the user to specify meaningful text here?

> > > +       SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
> > > +       SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),

> > Capitalise switch.

> I noticed that by capitalizing the first letter of the 2nd word,
> the 2nd word is dropped out by the system when amixer lists them.
> "EQ1 Switch" becomes just "EQ1", while "EQ1 switch" is "EQ1 switch".
> Have you seen this behavior before?
> Is this a known issue with some version of amixer or ASoC code base?

This is not a problem, this is intended behaviour.  ALSA applications
interpret the control names and use this naming information to present
an appropriate UI to users.

> > > +static const struct snd_soc_dapm_route audio_map[] = {
> > > +       /* Left headphone output mixer */
> > > +       {"Left HP Mixer", "Left DAC1", "DACL1"},
> > > +       {"Left HP Mixer", "Left DAC2", "DACL2"},
> > > +       {"Left HP Mixer", "Right DAC1", "DACR1"},
> > > +       {"Left HP Mixer", "Right DAC2", "DACR2"},
> > > +       {"Left HP Mixer", "MIC1", "Mic Bias"},
> > > +       {"Left HP Mixer", "MIC2", "Mic Bias"},

> > This looks wrong - I'd expect this to be switching the microphone input
> > to the mixer, not the bias.  Mixing the bias in would just present a DC
> > level which isn't going to be great.  Similarly for all your other
> > microphone input switching.

> I'll look at switching the microphone input like you suggested. Thanks.
> No worries, when turning on the mic bias, the hardware will not
> add DC into the digital samples.  It's handled well.

As I said I rather suspect that this is because the microphone input is
being switched into the path rather than the microphone bias.

> > > +                       /* BCI: normal bclk (rise) */
> > > +                       /* WCI: invert frame */
> > > +                       snd_soc_update_bits(codec,
> > M98088_REG_14_DAI1_FORMAT,
> > > +                               M98088_DAI_BCI, M98088_DAI_WCI);
> > > +                       break;
> > > +               case SND_SOC_DAIFMT_IB_NF:
> > > +                       /* BCI: invert bclk (fall) */
> > > +                       /* WCI: normal frame */
> > > +                       snd_soc_update_bits(codec,
> > M98088_REG_14_DAI1_FORMAT,
> > > +                               M98088_DAI_WCI, M98088_DAI_BCI);

> > This code (and most of the other update_bits() usages round here) looks
> > wrong - I'd expect to see a constant bitmask being supplied as the mask
> > argument then the value changing to set different values.

> The M98088_DAI_BCI and M98088_DAI_WCI are bit field masks.
> Is this what you expect?  If not, could you clarify?  Thanks!

As I said above I'd expect to see a constant mask being supplied and
varying values.  Could you please explain what you think the code above
does?

> > > +                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive)
> > */
> > > +                       (1<<1) |  /* SDOEN : serial data out ENABLE */
> > > +                       (1<<0));  /* SDIEN : serial data in ENABLE */

> > These look like they ought to be DAPM widgets for the AIF.

> Good idea. Which widget(s) will drive SDOEN, or SDIEN?

The AIF widgets.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-03 10:17       ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-03 10:17 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Joonyoung Shim, Takashi Iwai, Peter Ujfalusi,
	linux-kernel, Jesse Marroquin, Liam Girdwood

On Thu, Sep 02, 2010 at 04:30:14PM -0700, Peter Hsiang wrote:
> On Wed, Sep 01, 2010, Mark Brown wrote:

> >   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

> Thanks for the git location you provided.  What version of Linux
> kernel does this git/broonie/sound-2.6.git version correspond to?
> Is it newer than even the latest kernel-next?

This is what's going into -next, it will often be trivially ahead of
-next with the patches addded since -next was last built but not
meaningfully so.

> Some of the ASoC API functions and features in the latest version of
> the kernel you require us to use are not in the popular stable kernel
> version 2.6.32 that people are using today to build products.
> Our intent is to release driver code that will work in both the current
> and latest kernel versions.

This is not how Linux development works, and in many cases will be
impossible as internal APIs are frequently removed from the
Linux kernel or changed incompatibly.

> Would it not be a good idea to use the API functions that exist in
> both the very latest and the currently popular kernel version?

Like I say this is just not possible in many cases.

> Does kernel.org provide a channel for releasing codec drivers that
> can be backward compatible to a popular earlier kernel version?

No, and it's not clear that a single approach can be adopted since newer
devices often drive enhancements to the core APIs so the approaches that
work best to integrate with older kernels vary depending on the particular 
devices and kernel versions involved.

> > > +       /* Bypass option for INA to MIC1 connection
> > > +        * 0 = normal setting
> > > +        * 1 = bypass enabled
> > > +        */
> > > +       unsigned int ina_to_mic1_bypass;
> > > +
> > > +       /* Bypass option for MIC1 to MIC2 connection

> > What are these - they sound like something I'd expect to be configurable
> > at runtime?

Please engage with questions like this.

> > > +/*
> > > + * Read the MAX98088 I2C register space
> > > + * Note: this driver source code is backward compatible to kernel
> > > + * version 2.6.32.
> > > + */
> > > +static unsigned int max98088_read(struct snd_soc_codec *codec,
> > > +                                 unsigned int reg)

> > Just use soc-cache.  Compatibility code for older kernels isn't really
> > acceptable for mainline due to the increased maintainance burden and it
> > will at best result in a driver that doesn't work as well as possible.

> This integrated registers feature is not available in 2.6.32
> Is it ok with you to keep it this way for this release?  Thanks.

No, you should use the features of the current kernel.  For your
backport you can do things like supply soc-cache.c as well.

> > > +       client = (struct i2c_client *)codec->control_data;

> > No need to cast away from void.

> Ok will do - but no harm in being informative :)

There is actually - if you have a cast then if you have done something
broken by mistake the compiler is much less likely to generate a warning
since the cast says you know what you're doing.

> > > +       "user-400Hz",
> > > +       "user-600Hz",
> > > +       "user-800Hz",
> > > +       "user-1000Hz"

> > These user options look very suspicous.

> The first 8 options are settings pre-defined in hardware that the
> user can choose from.
> The last 4 are user programmable options, and thus the name 'user'.

What happens if the user didn't supply any programmable options?  Would
it not be better to allow the user to specify meaningful text here?

> > > +       SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
> > > +       SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),

> > Capitalise switch.

> I noticed that by capitalizing the first letter of the 2nd word,
> the 2nd word is dropped out by the system when amixer lists them.
> "EQ1 Switch" becomes just "EQ1", while "EQ1 switch" is "EQ1 switch".
> Have you seen this behavior before?
> Is this a known issue with some version of amixer or ASoC code base?

This is not a problem, this is intended behaviour.  ALSA applications
interpret the control names and use this naming information to present
an appropriate UI to users.

> > > +static const struct snd_soc_dapm_route audio_map[] = {
> > > +       /* Left headphone output mixer */
> > > +       {"Left HP Mixer", "Left DAC1", "DACL1"},
> > > +       {"Left HP Mixer", "Left DAC2", "DACL2"},
> > > +       {"Left HP Mixer", "Right DAC1", "DACR1"},
> > > +       {"Left HP Mixer", "Right DAC2", "DACR2"},
> > > +       {"Left HP Mixer", "MIC1", "Mic Bias"},
> > > +       {"Left HP Mixer", "MIC2", "Mic Bias"},

> > This looks wrong - I'd expect this to be switching the microphone input
> > to the mixer, not the bias.  Mixing the bias in would just present a DC
> > level which isn't going to be great.  Similarly for all your other
> > microphone input switching.

> I'll look at switching the microphone input like you suggested. Thanks.
> No worries, when turning on the mic bias, the hardware will not
> add DC into the digital samples.  It's handled well.

As I said I rather suspect that this is because the microphone input is
being switched into the path rather than the microphone bias.

> > > +                       /* BCI: normal bclk (rise) */
> > > +                       /* WCI: invert frame */
> > > +                       snd_soc_update_bits(codec,
> > M98088_REG_14_DAI1_FORMAT,
> > > +                               M98088_DAI_BCI, M98088_DAI_WCI);
> > > +                       break;
> > > +               case SND_SOC_DAIFMT_IB_NF:
> > > +                       /* BCI: invert bclk (fall) */
> > > +                       /* WCI: normal frame */
> > > +                       snd_soc_update_bits(codec,
> > M98088_REG_14_DAI1_FORMAT,
> > > +                               M98088_DAI_WCI, M98088_DAI_BCI);

> > This code (and most of the other update_bits() usages round here) looks
> > wrong - I'd expect to see a constant bitmask being supplied as the mask
> > argument then the value changing to set different values.

> The M98088_DAI_BCI and M98088_DAI_WCI are bit field masks.
> Is this what you expect?  If not, could you clarify?  Thanks!

As I said above I'd expect to see a constant mask being supplied and
varying values.  Could you please explain what you think the code above
does?

> > > +                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive)
> > */
> > > +                       (1<<1) |  /* SDOEN : serial data out ENABLE */
> > > +                       (1<<0));  /* SDIEN : serial data in ENABLE */

> > These look like they ought to be DAPM widgets for the AIF.

> Good idea. Which widget(s) will drive SDOEN, or SDIEN?

The AIF widgets.

^ permalink raw reply	[flat|nested] 81+ messages in thread

* RE: [PATCH] ASoC: Add max98088 CODEC driver
  2010-09-01 11:14   ` Mark Brown
  (?)
@ 2010-09-02 23:30   ` Peter Hsiang
  2010-09-03 10:17       ` Mark Brown
  -1 siblings, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-09-02 23:30 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Jesse Marroquin, Liam Girdwood,
	Peter Ujfalusi, Joonyoung Shim, alsa-devel, linux-kernel

On Wed, Sep 01, 2010, Mark Brown wrote:
> On Tue, Aug 31, 2010 at 02:08:30PM -0700, Peter Hsiang wrote:
> > This patch adds the MAX98088 CODEC driver.
>
> The major issue with this is that the driver needs to be updated to
> current ASoC versions - you should always be working against the trees
> that are in linux-next for new drivers.  For ASoC you should be looking
> at:
>
>   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

Thanks for the git location you provided.  What version of Linux
kernel does this git/broonie/sound-2.6.git version correspond to?
Is it newer than even the latest kernel-next?

Some of the ASoC API functions and features in the latest version of
the kernel you require us to use are not in the popular stable kernel
version 2.6.32 that people are using today to build products.
Our intent is to release driver code that will work in both the current
and latest kernel versions.

Would it not be a good idea to use the API functions that exist in
both the very latest and the currently popular kernel version?

Does kernel.org provide a channel for releasing codec drivers that
can be backward compatible to a popular earlier kernel version?

>
> > Signed-off-by: Peter Hsiang <peter.hsiang at maxim-ic.com>
>
> Please write your e-mail address properly - it'll end up in the git logs
> anyway.  It'd also be nice to credit your sources when you've taken code
> from other drivers :)
>
> > +#define EQ_CFG_MAX 32
>
> > +/* Speaker excursion limiter filter response configurations */
> > +#define EX_CFG_MAX 32
>
> Multiple definitions of this.

These are not multiple definitions.
They are 2 different ones - the equalizer, and excursion limiter.

>
> > +       /* Set receiver_mode to:
> > +        * 0 = amplifier output, or
> > +        * 1 = LINE level output
> > +        */
> > +       unsigned int receiver_mode;
>
> Better to use bools or bitfields.
>
> > +       /* Bypass option for INA to MIC1 connection
> > +        * 0 = normal setting
> > +        * 1 = bypass enabled
> > +        */
> > +       unsigned int ina_to_mic1_bypass;
> > +
> > +       /* Bypass option for MIC1 to MIC2 connection
>
> What are these - they sound like something I'd expect to be configurable
> at runtime?
>
> > @@ -28,6 +28,7 @@ config SND_SOC_ALL_CODECS
> >         select SND_SOC_DA7210 if I2C
> >         select SND_SOC_JZ4740 if SOC_JZ4740
> >         select SND_SOC_MAX9877 if I2C
> > +       select SND_SOC_MAX98088 if I2C
>
> The sorting should be lexical.

Ok, thanks

>
> > +/*
> > + * Read the MAX98088 I2C register space
> > + * Note: this driver source code is backward compatible to kernel
> > + * version 2.6.32.
> > + */
> > +static unsigned int max98088_read(struct snd_soc_codec *codec,
> > +                                 unsigned int reg)
>
> Just use soc-cache.  Compatibility code for older kernels isn't really
> acceptable for mainline due to the increased maintainance burden and it
> will at best result in a driver that doesn't work as well as possible.

This integrated registers feature is not available in 2.6.32
Is it ok with you to keep it this way for this release?  Thanks.

>
> > +       client = (struct i2c_client *)codec->control_data;
>
> No need to cast away from void.

Ok will do - but no harm in being informative :)

>
> > +/*
> > + * For kernels compiled without unsigned long long int division
> > + */
> > +unsigned long long int ulldiv(unsigned long long int dividend,
> > +                             unsigned long long int divisor)
> > +{
> > +       unsigned long long int quotient = 0;
> > +       int shift = 1;
> > +
> > +       if (divisor == 0)
> > +               return 0;
> > +
> > +       /* result is 1.0 if divisor and dividend are equal */
> > +       if (divisor == dividend)
> > +               return 1;
> > +
> > +       /* Normalize divisor */
> > +       while (!(divisor & 0x8000000000000000ULL)) {
> > +               divisor <<= 1;
> > +               ++shift;
> > +       }
> > +
> > +       /* Shift and subtract */
> > +       while (shift--) {
> > +               quotient <<= 1;
> > +
> > +               if (divisor <= dividend) {
> > +                       dividend -= divisor;
> > +                       ++quotient;
> > +               }
> > +               divisor >>= 1;
> > +       }
> > +
> > +       /* Round up */
> > +       if (dividend > divisor)
> > +               ++quotient;
> > +
> > +       return quotient;
> > +}
>
> > +/*
> > + * Load equalizer DSP coefficient configurations registers
> > + */
> > +void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
> > +                   unsigned int band, u16 *coefs)
> > +{
> > +       unsigned int eq_reg;
> > +       unsigned int i;
> > +
> > +       if (band > 4)
> > +               return;
> > +
> > +       if (dai > 1)
> > +               return;
>
> These look like they should be BUG_ON().

Yes, good idea.

>
> > +/*
> > + * Excursion limiter modes
> > + */
> > +static const char *max98088_ex_mode[] = {
> > +       "Off",
> > +       "100Hz",
> > +       "400Hz",
> > +       "600Hz",
> > +       "800Hz",
> > +       "1000Hz",
> > +       "200-400Hz",
> > +       "400-600Hz",
> > +       "400-800Hz",
> > +       "user-400Hz",
> > +       "user-600Hz",
> > +       "user-800Hz",
> > +       "user-1000Hz"
>
> These user options look very suspicous.

The first 8 options are settings pre-defined in hardware that the
user can choose from.
The last 4 are user programmable options, and thus the name 'user'.

>
> > +static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
> > +                               struct snd_ctl_elem_value *ucontrol)
> > +{
> > +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> > +       struct max98088_priv *max98088 = codec->private_data;
> > +       unsigned int *mode = &max98088->ex_mode;
> > +
> > +       *mode = ucontrol->value.integer.value[0];
> > +
> > +       if (*mode <= ARRAY_SIZE(ex_mode_table))
> > +               max98088_write(codec, M98088_REG_41_SPKDHP,
> > +                       ex_mode_table[*mode]);
>
> Report the error if there's an invalid input.

Ok

>
> > +static const char *max98088_hp_spk_mute[] = {"disable", "enable"};
> > +static const struct soc_enum max98088_hp_spk_mute_enum[] = {
> > +       SOC_ENUM_SINGLE_EXT(2, max98088_hp_spk_mute),
>
> Why are you doing this rather than just using a standard switch control?
> Even if there's a reason to not use one of the standard register
> controls this should be presented to the application layer as a switch
> rather than as an enumeration.

We will use a switch control.  Thanks

>
> The same issue applies to the other outputs.
>
> > +static const char *max98088_dcblk[] =  {"Off", "On"};
> > +static const struct soc_enum max98088_dcblk_enum[] = {
> > +       SOC_ENUM_SINGLE(M98088_REG_20_DAI2_FILTERS, 0, 2,
> max98088_dcblk),
> > +};
>
> Again, present a switch to userspace.

Yes

>
> > +       SOC_DOUBLE_R("Headphone volume", M98088_REG_39_LVL_HP_L,
> > +               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
>
> Volume, not volume.
>
> > +       SOC_SINGLE("MIC1 gain", M98088_REG_35_LVL_MIC1, 0, 31, 1),
> > +       SOC_SINGLE("MIC2 gain", M98088_REG_36_LVL_MIC2, 0, 31, 1),
>
> > +       SOC_SINGLE("MIC1 pre", M98088_REG_35_LVL_MIC1, 5, 3, 0),
> > +       SOC_SINGLE("MIC2 pre", M98088_REG_36_LVL_MIC2, 5, 3, 0),
> > +
> > +       SOC_SINGLE("INA gain", M98088_REG_37_LVL_INA, 0, 7, 1),
> > +       SOC_SINGLE("INB gain", M98088_REG_38_LVL_INB, 0, 7, 1),
>
> These should all be named " Volume", as should all your other " gain" or
> " pre" controls.

Ok
>
> > +       SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
> > +       SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
>
> Capitalise switch.

I noticed that by capitalizing the first letter of the 2nd word,
the 2nd word is dropped out by the system when amixer lists them.
"EQ1 Switch" becomes just "EQ1", while "EQ1 switch" is "EQ1 switch".
Have you seen this behavior before?
Is this a known issue with some version of amixer or ASoC code base?

>
> > +/* Left speaker mixer switch */
> > +static const struct snd_kcontrol_new
> max98088_left_speaker_mixer_controls[] = {
> > +       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2B_MIX_SPK_LEFT, 7, 1,
> 0),
>
> All of these should be " Switch".

Ok

>
> > +static int max98088_hp_event(struct snd_soc_dapm_widget *w,
> > +                            struct snd_kcontrol *kcontrol, int event)
> > +{
> > +       struct snd_soc_codec *codec = w->codec;
> > +       u16 status;
> > +
> > +       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
> > +
> > +       /* powering down headphone gracefully */
> > +       status = max98088_read(codec, M98088_REG_4D_PWR_EN_OUT);
> > +       if ((status & M98088_HPEN) == M98088_HPEN) {
> > +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> > +                       (status & ~M98088_HPEN));
> > +       }
> > +       schedule_timeout_interruptible(msecs_to_jiffies(20));
>
> This should be non-interruptible since there's no handling of any actual
> interruptions.

Ok

>
> > +/* DAPM AUDIO_MAP: */
> > +static const struct snd_soc_dapm_route audio_map[] = {
> > +       /* Left headphone output mixer */
> > +       {"Left HP Mixer", "Left DAC1", "DACL1"},
> > +       {"Left HP Mixer", "Left DAC2", "DACL2"},
> > +       {"Left HP Mixer", "Right DAC1", "DACR1"},
> > +       {"Left HP Mixer", "Right DAC2", "DACR2"},
> > +       {"Left HP Mixer", "MIC1", "Mic Bias"},
> > +       {"Left HP Mixer", "MIC2", "Mic Bias"},
>
> This looks wrong - I'd expect this to be switching the microphone input
> to the mixer, not the bias.  Mixing the bias in would just present a DC
> level which isn't going to be great.  Similarly for all your other
> microphone input switching.

I'll look at switching the microphone input like you suggested. Thanks.
No worries, when turning on the mic bias, the hardware will not
add DC into the digital samples.  It's handled well.

>
> > +               /* DAI clock master/slave wrt the codec */
> > +               switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {
>
> You should be using the masks for your switch statements.  Using
> specific values is at best harder to read and at worst will lead to
> false positives.  This applies to all your comparisons in this function.

This method was inherited from another released driver.
We will adopt the new method.

>
> > +                       /* BCI: normal bclk (rise) */
> > +                       /* WCI: invert frame */
> > +                       snd_soc_update_bits(codec,
> M98088_REG_14_DAI1_FORMAT,
> > +                               M98088_DAI_BCI, M98088_DAI_WCI);
> > +                       break;
> > +               case SND_SOC_DAIFMT_IB_NF:
> > +                       /* BCI: invert bclk (fall) */
> > +                       /* WCI: normal frame */
> > +                       snd_soc_update_bits(codec,
> M98088_REG_14_DAI1_FORMAT,
> > +                               M98088_DAI_WCI, M98088_DAI_BCI);
>
> This code (and most of the other update_bits() usages round here) looks
> wrong - I'd expect to see a constant bitmask being supplied as the mask
> argument then the value changing to set different values.

The M98088_DAI_BCI and M98088_DAI_WCI are bit field masks.
Is this what you expect?  If not, could you clarify?  Thanks!

>
> > +               max98088_write(codec, M98088_REG_16_DAI1_IOCFG,
> > +                       (1<<6) |  /* SEL : map DAI1 to S1 */
> > +                       (0<<5) |  /* LTEN : ADC->DAC loop-through enable
> */
> > +                       (0<<4) |  /* LBEN : loopback (0=disable,
> 1=enable) */
> > +                       (0<<3) |  /* DMONO : DAC SDIN (0=stereo, 1=mono)
> */
>
> These look like things that should be configured by the user, especially
> LTEN.
>
> > +                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive)
> */
> > +                       (1<<1) |  /* SDOEN : serial data out ENABLE */
> > +                       (1<<0));  /* SDIEN : serial data in ENABLE */
>
> These look like they ought to be DAPM widgets for the AIF.

Good idea. Which widget(s) will drive SDOEN, or SDIEN?

>
> > +static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
> > +                                  int clk_id, unsigned int freq, int
> dir)
> > +{
> > +       struct snd_soc_codec *codec = dai->codec;
> > +       struct max98088_priv *max98088 = codec->private_data;
> > +
> > +       if (freq != max98088->sysclk) {
>
> It would be clearer to just return in this case so you don't have so
> much indentation.

Good point.  Thanks for this suggestion on the source indentation.

>
> > +               /* If codec is currently running, toggle reset */
> > +               if (max98088_read(codec, M98088_REG_51_PWR_SYS)
> > +                       & M98088_SHDNRUN) {
> > +                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
> > +                               M98088_SHDNRUN, 0);
> > +                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
> > +                               0, M98088_SHDNRUN);
>
> Will this reset the whole chip?

It's not a chip reset.
I will correct the comments to make it clearer.  Thanks.

>
> > +static inline int rate_index(int rate)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
> > +               if (rate_table[i].rate >= rate)
> > +                       return i;
> > +       }
> > +       return 0;
>
> This won't indicate if there's a failure to find a match.

You are right.
This will default to the hardware default setting of 0 if the sample
rate specified by the user were invalid.
I'll take a look this.

>
> > +       /* data 16/24 bit width */
> > +       switch (params_format(params)) {
> > +       case SNDRV_PCM_FORMAT_S16_LE:
> > +               /* WS: 16bit */
> > +               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> > +                       M98088_DAI_WS, 0);
> > +               break;
> > +       case SNDRV_PCM_FORMAT_S24_LE:
> > +               /* WS: 24bit */
> > +               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> > +                       0, M98088_DAI_WS);
> > +               break;
> > +       }
>
> Again, your snd_soc_update_bits() usage seems confused.  You're also not
> checking for invalid inputs.  There are other instances of this in the
> driver, I'll not comment them all.

It uses the bit field mask to control the desired bits.

>
> > +       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
> 0);
> > +
> > +       if (rate != cdata->rate) {
> > +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> > +               reg11val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
> > +               max98088_write(codec, M98088_REG_11_DAI1_CLKMODE,
> reg11val);
> > +               cdata->rate = rate;
>
> Just directly use snd_soc_update_bits() - it will suppress null changes.

Good idea.

>
> > +       /* Configure NI when operating as master */
> > +       if (max98088_read(codec, M98088_REG_14_DAI1_FORMAT)
> > +               & M98088_DAI_MAS) {
> > +               BUG_ON(max98088->sysclk == 0);
>
> BUG_ON() is unfriendly here - this is a machine driver configuration
> error and normally BUG_ON() would be an internal error in the driver.
> Better error reporting please.

Ok

>
> > +static int max98088_set_bias_level(struct snd_soc_codec *codec,
> > +                                 enum snd_soc_bias_level level)
> > +{
> > +       switch (level) {
> > +       case SND_SOC_BIAS_ON:
> > +               max98088_write(codec, M98088_REG_51_PWR_SYS,
> M98088_SHDNRUN);
> > +               break;
> > +
> > +       case SND_SOC_BIAS_PREPARE:
> > +               max98088_write(codec, M98088_REG_51_PWR_SYS,
> M98088_SHDNRUN);
> > +               break;
>
> Drop the _ON cacse if it's the same.  Also, how will this interact with
> the management of SHDNRUN in hw_params()?

Ok

>
> > +       case SND_SOC_BIAS_STANDBY:
> > +               max98088_sync_cache(codec);
> > +               max98088_write(codec, M98088_REG_51_PWR_SYS,
> > +                       M98088_SHDNRUN|M98088_PWRSV);
> > +               break;
>
> Do you really want to sync the cache every time we drop into standby?

Good point.

>
> > +       /* Configure digital mic / external mic */
> > +       /* Digital mic needs REG_15 OSR1=1 */
> > +       regval = ((0<<6) | /* digital mic clock freq = PCLK/8 */
> > +                 (0<<5) | /* DIGMICL enable */
> > +                 (0<<4) | /* DIGMICR enable */
> > +                 (0<<0)); /* external mic enable */
>
> These look like they ought to be managed via DAPM?


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
  2010-08-31 21:08 Peter Hsiang
@ 2010-09-01 11:14   ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-01 11:14 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: Jaroslav Kysela, Takashi Iwai, Jesse Marroquin, Liam Girdwood,
	Peter Ujfalusi, Joonyoung Shim, alsa-devel, linux-kernel

On Tue, Aug 31, 2010 at 02:08:30PM -0700, Peter Hsiang wrote:
> This patch adds the MAX98088 CODEC driver.

The major issue with this is that the driver needs to be updated to
current ASoC versions - you should always be working against the trees
that are in linux-next for new drivers.  For ASoC you should be looking
at:

  git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

> Signed-off-by: Peter Hsiang <peter.hsiang at maxim-ic.com>

Please write your e-mail address properly - it'll end up in the git logs
anyway.  It'd also be nice to credit your sources when you've taken code
from other drivers :)

> +#define EQ_CFG_MAX 32

> +/* Speaker excursion limiter filter response configurations */
> +#define EX_CFG_MAX 32

Multiple definitions of this.

> +       /* Set receiver_mode to:
> +        * 0 = amplifier output, or
> +        * 1 = LINE level output
> +        */
> +       unsigned int receiver_mode;

Better to use bools or bitfields.

> +       /* Bypass option for INA to MIC1 connection
> +        * 0 = normal setting
> +        * 1 = bypass enabled
> +        */
> +       unsigned int ina_to_mic1_bypass;
> +
> +       /* Bypass option for MIC1 to MIC2 connection

What are these - they sound like something I'd expect to be configurable
at runtime?

> @@ -28,6 +28,7 @@ config SND_SOC_ALL_CODECS
>         select SND_SOC_DA7210 if I2C
>         select SND_SOC_JZ4740 if SOC_JZ4740
>         select SND_SOC_MAX9877 if I2C
> +       select SND_SOC_MAX98088 if I2C

The sorting should be lexical.

> +/*
> + * Read the MAX98088 I2C register space
> + * Note: this driver source code is backward compatible to kernel
> + * version 2.6.32.
> + */
> +static unsigned int max98088_read(struct snd_soc_codec *codec,
> +                                 unsigned int reg)

Just use soc-cache.  Compatibility code for older kernels isn't really
acceptable for mainline due to the increased maintainance burden and it
will at best result in a driver that doesn't work as well as possible.

> +       client = (struct i2c_client *)codec->control_data;

No need to cast away from void.

> +/*
> + * For kernels compiled without unsigned long long int division
> + */
> +unsigned long long int ulldiv(unsigned long long int dividend,
> +                             unsigned long long int divisor)
> +{
> +       unsigned long long int quotient = 0;
> +       int shift = 1;
> +
> +       if (divisor == 0)
> +               return 0;
> +
> +       /* result is 1.0 if divisor and dividend are equal */
> +       if (divisor == dividend)
> +               return 1;
> +
> +       /* Normalize divisor */
> +       while (!(divisor & 0x8000000000000000ULL)) {
> +               divisor <<= 1;
> +               ++shift;
> +       }
> +
> +       /* Shift and subtract */
> +       while (shift--) {
> +               quotient <<= 1;
> +
> +               if (divisor <= dividend) {
> +                       dividend -= divisor;
> +                       ++quotient;
> +               }
> +               divisor >>= 1;
> +       }
> +
> +       /* Round up */
> +       if (dividend > divisor)
> +               ++quotient;
> +
> +       return quotient;
> +}

> +/*
> + * Load equalizer DSP coefficient configurations registers
> + */
> +void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
> +                   unsigned int band, u16 *coefs)
> +{
> +       unsigned int eq_reg;
> +       unsigned int i;
> +
> +       if (band > 4)
> +               return;
> +
> +       if (dai > 1)
> +               return;

These look like they should be BUG_ON().

> +/*
> + * Excursion limiter modes
> + */
> +static const char *max98088_ex_mode[] = {
> +       "Off",
> +       "100Hz",
> +       "400Hz",
> +       "600Hz",
> +       "800Hz",
> +       "1000Hz",
> +       "200-400Hz",
> +       "400-600Hz",
> +       "400-800Hz",
> +       "user-400Hz",
> +       "user-600Hz",
> +       "user-800Hz",
> +       "user-1000Hz"

These user options look very suspicous.

> +static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
> +                               struct snd_ctl_elem_value *ucontrol)
> +{
> +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +       struct max98088_priv *max98088 = codec->private_data;
> +       unsigned int *mode = &max98088->ex_mode;
> +
> +       *mode = ucontrol->value.integer.value[0];
> +
> +       if (*mode <= ARRAY_SIZE(ex_mode_table))
> +               max98088_write(codec, M98088_REG_41_SPKDHP,
> +                       ex_mode_table[*mode]);

Report the error if there's an invalid input.

> +static const char *max98088_hp_spk_mute[] = {"disable", "enable"};
> +static const struct soc_enum max98088_hp_spk_mute_enum[] = {
> +       SOC_ENUM_SINGLE_EXT(2, max98088_hp_spk_mute),

Why are you doing this rather than just using a standard switch control?
Even if there's a reason to not use one of the standard register
controls this should be presented to the application layer as a switch
rather than as an enumeration.

The same issue applies to the other outputs.

> +static const char *max98088_dcblk[] =  {"Off", "On"};
> +static const struct soc_enum max98088_dcblk_enum[] = {
> +       SOC_ENUM_SINGLE(M98088_REG_20_DAI2_FILTERS, 0, 2, max98088_dcblk),
> +};

Again, present a switch to userspace.

> +       SOC_DOUBLE_R("Headphone volume", M98088_REG_39_LVL_HP_L,
> +               M98088_REG_3A_LVL_HP_R, 0, 31, 0),

Volume, not volume.

> +       SOC_SINGLE("MIC1 gain", M98088_REG_35_LVL_MIC1, 0, 31, 1),
> +       SOC_SINGLE("MIC2 gain", M98088_REG_36_LVL_MIC2, 0, 31, 1),

> +       SOC_SINGLE("MIC1 pre", M98088_REG_35_LVL_MIC1, 5, 3, 0),
> +       SOC_SINGLE("MIC2 pre", M98088_REG_36_LVL_MIC2, 5, 3, 0),
> +
> +       SOC_SINGLE("INA gain", M98088_REG_37_LVL_INA, 0, 7, 1),
> +       SOC_SINGLE("INB gain", M98088_REG_38_LVL_INB, 0, 7, 1),

These should all be named " Volume", as should all your other " gain" or
" pre" controls.

> +       SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
> +       SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),

Capitalise switch.

> +/* Left speaker mixer switch */
> +static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
> +       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),

All of these should be " Switch".

> +static int max98088_hp_event(struct snd_soc_dapm_widget *w,
> +                            struct snd_kcontrol *kcontrol, int event)
> +{
> +       struct snd_soc_codec *codec = w->codec;
> +       u16 status;
> +
> +       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
> +
> +       /* powering down headphone gracefully */
> +       status = max98088_read(codec, M98088_REG_4D_PWR_EN_OUT);
> +       if ((status & M98088_HPEN) == M98088_HPEN) {
> +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> +                       (status & ~M98088_HPEN));
> +       }
> +       schedule_timeout_interruptible(msecs_to_jiffies(20));

This should be non-interruptible since there's no handling of any actual
interruptions.

> +/* DAPM AUDIO_MAP: */
> +static const struct snd_soc_dapm_route audio_map[] = {
> +       /* Left headphone output mixer */
> +       {"Left HP Mixer", "Left DAC1", "DACL1"},
> +       {"Left HP Mixer", "Left DAC2", "DACL2"},
> +       {"Left HP Mixer", "Right DAC1", "DACR1"},
> +       {"Left HP Mixer", "Right DAC2", "DACR2"},
> +       {"Left HP Mixer", "MIC1", "Mic Bias"},
> +       {"Left HP Mixer", "MIC2", "Mic Bias"},

This looks wrong - I'd expect this to be switching the microphone input
to the mixer, not the bias.  Mixing the bias in would just present a DC
level which isn't going to be great.  Similarly for all your other
microphone input switching.

> +               /* DAI clock master/slave wrt the codec */
> +               switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {

You should be using the masks for your switch statements.  Using
specific values is at best harder to read and at worst will lead to
false positives.  This applies to all your comparisons in this function.

> +                       /* BCI: normal bclk (rise) */
> +                       /* WCI: invert frame */
> +                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                               M98088_DAI_BCI, M98088_DAI_WCI);
> +                       break;
> +               case SND_SOC_DAIFMT_IB_NF:
> +                       /* BCI: invert bclk (fall) */
> +                       /* WCI: normal frame */
> +                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                               M98088_DAI_WCI, M98088_DAI_BCI);

This code (and most of the other update_bits() usages round here) looks
wrong - I'd expect to see a constant bitmask being supplied as the mask
argument then the value changing to set different values.

> +               max98088_write(codec, M98088_REG_16_DAI1_IOCFG,
> +                       (1<<6) |  /* SEL : map DAI1 to S1 */
> +                       (0<<5) |  /* LTEN : ADC->DAC loop-through enable */
> +                       (0<<4) |  /* LBEN : loopback (0=disable, 1=enable) */
> +                       (0<<3) |  /* DMONO : DAC SDIN (0=stereo, 1=mono) */

These look like things that should be configured by the user, especially
LTEN.

> +                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
> +                       (1<<1) |  /* SDOEN : serial data out ENABLE */
> +                       (1<<0));  /* SDIEN : serial data in ENABLE */

These look like they ought to be DAPM widgets for the AIF.

> +static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
> +                                  int clk_id, unsigned int freq, int dir)
> +{
> +       struct snd_soc_codec *codec = dai->codec;
> +       struct max98088_priv *max98088 = codec->private_data;
> +
> +       if (freq != max98088->sysclk) {

It would be clearer to just return in this case so you don't have so
much indentation.

> +               /* If codec is currently running, toggle reset */
> +               if (max98088_read(codec, M98088_REG_51_PWR_SYS)
> +                       & M98088_SHDNRUN) {
> +                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
> +                               M98088_SHDNRUN, 0);
> +                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
> +                               0, M98088_SHDNRUN);

Will this reset the whole chip?

> +static inline int rate_index(int rate)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
> +               if (rate_table[i].rate >= rate)
> +                       return i;
> +       }
> +       return 0;

This won't indicate if there's a failure to find a match.

> +       /* data 16/24 bit width */
> +       switch (params_format(params)) {
> +       case SNDRV_PCM_FORMAT_S16_LE:
> +               /* WS: 16bit */
> +               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                       M98088_DAI_WS, 0);
> +               break;
> +       case SNDRV_PCM_FORMAT_S24_LE:
> +               /* WS: 24bit */
> +               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                       0, M98088_DAI_WS);
> +               break;
> +       }

Again, your snd_soc_update_bits() usage seems confused.  You're also not
checking for invalid inputs.  There are other instances of this in the
driver, I'll not comment them all.

> +       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
> +
> +       if (rate != cdata->rate) {
> +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> +               reg11val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
> +               max98088_write(codec, M98088_REG_11_DAI1_CLKMODE, reg11val);
> +               cdata->rate = rate;

Just directly use snd_soc_update_bits() - it will suppress null changes.

> +       /* Configure NI when operating as master */
> +       if (max98088_read(codec, M98088_REG_14_DAI1_FORMAT)
> +               & M98088_DAI_MAS) {
> +               BUG_ON(max98088->sysclk == 0);

BUG_ON() is unfriendly here - this is a machine driver configuration
error and normally BUG_ON() would be an internal error in the driver.
Better error reporting please.

> +static int max98088_set_bias_level(struct snd_soc_codec *codec,
> +                                 enum snd_soc_bias_level level)
> +{
> +       switch (level) {
> +       case SND_SOC_BIAS_ON:
> +               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
> +               break;
> +
> +       case SND_SOC_BIAS_PREPARE:
> +               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
> +               break;

Drop the _ON cacse if it's the same.  Also, how will this interact with
the management of SHDNRUN in hw_params()?

> +       case SND_SOC_BIAS_STANDBY:
> +               max98088_sync_cache(codec);
> +               max98088_write(codec, M98088_REG_51_PWR_SYS,
> +                       M98088_SHDNRUN|M98088_PWRSV);
> +               break;

Do you really want to sync the cache every time we drop into standby?

> +       /* Configure digital mic / external mic */
> +       /* Digital mic needs REG_15 OSR1=1 */
> +       regval = ((0<<6) | /* digital mic clock freq = PCLK/8 */
> +                 (0<<5) | /* DIGMICL enable */
> +                 (0<<4) | /* DIGMICR enable */
> +                 (0<<0)); /* external mic enable */

These look like they ought to be managed via DAPM?

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-09-01 11:14   ` Mark Brown
  0 siblings, 0 replies; 81+ messages in thread
From: Mark Brown @ 2010-09-01 11:14 UTC (permalink / raw)
  To: Peter Hsiang
  Cc: alsa-devel, Joonyoung Shim, Takashi Iwai, Peter Ujfalusi,
	linux-kernel, Jesse Marroquin, Liam Girdwood

On Tue, Aug 31, 2010 at 02:08:30PM -0700, Peter Hsiang wrote:
> This patch adds the MAX98088 CODEC driver.

The major issue with this is that the driver needs to be updated to
current ASoC versions - you should always be working against the trees
that are in linux-next for new drivers.  For ASoC you should be looking
at:

  git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git

> Signed-off-by: Peter Hsiang <peter.hsiang at maxim-ic.com>

Please write your e-mail address properly - it'll end up in the git logs
anyway.  It'd also be nice to credit your sources when you've taken code
from other drivers :)

> +#define EQ_CFG_MAX 32

> +/* Speaker excursion limiter filter response configurations */
> +#define EX_CFG_MAX 32

Multiple definitions of this.

> +       /* Set receiver_mode to:
> +        * 0 = amplifier output, or
> +        * 1 = LINE level output
> +        */
> +       unsigned int receiver_mode;

Better to use bools or bitfields.

> +       /* Bypass option for INA to MIC1 connection
> +        * 0 = normal setting
> +        * 1 = bypass enabled
> +        */
> +       unsigned int ina_to_mic1_bypass;
> +
> +       /* Bypass option for MIC1 to MIC2 connection

What are these - they sound like something I'd expect to be configurable
at runtime?

> @@ -28,6 +28,7 @@ config SND_SOC_ALL_CODECS
>         select SND_SOC_DA7210 if I2C
>         select SND_SOC_JZ4740 if SOC_JZ4740
>         select SND_SOC_MAX9877 if I2C
> +       select SND_SOC_MAX98088 if I2C

The sorting should be lexical.

> +/*
> + * Read the MAX98088 I2C register space
> + * Note: this driver source code is backward compatible to kernel
> + * version 2.6.32.
> + */
> +static unsigned int max98088_read(struct snd_soc_codec *codec,
> +                                 unsigned int reg)

Just use soc-cache.  Compatibility code for older kernels isn't really
acceptable for mainline due to the increased maintainance burden and it
will at best result in a driver that doesn't work as well as possible.

> +       client = (struct i2c_client *)codec->control_data;

No need to cast away from void.

> +/*
> + * For kernels compiled without unsigned long long int division
> + */
> +unsigned long long int ulldiv(unsigned long long int dividend,
> +                             unsigned long long int divisor)
> +{
> +       unsigned long long int quotient = 0;
> +       int shift = 1;
> +
> +       if (divisor == 0)
> +               return 0;
> +
> +       /* result is 1.0 if divisor and dividend are equal */
> +       if (divisor == dividend)
> +               return 1;
> +
> +       /* Normalize divisor */
> +       while (!(divisor & 0x8000000000000000ULL)) {
> +               divisor <<= 1;
> +               ++shift;
> +       }
> +
> +       /* Shift and subtract */
> +       while (shift--) {
> +               quotient <<= 1;
> +
> +               if (divisor <= dividend) {
> +                       dividend -= divisor;
> +                       ++quotient;
> +               }
> +               divisor >>= 1;
> +       }
> +
> +       /* Round up */
> +       if (dividend > divisor)
> +               ++quotient;
> +
> +       return quotient;
> +}

> +/*
> + * Load equalizer DSP coefficient configurations registers
> + */
> +void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
> +                   unsigned int band, u16 *coefs)
> +{
> +       unsigned int eq_reg;
> +       unsigned int i;
> +
> +       if (band > 4)
> +               return;
> +
> +       if (dai > 1)
> +               return;

These look like they should be BUG_ON().

> +/*
> + * Excursion limiter modes
> + */
> +static const char *max98088_ex_mode[] = {
> +       "Off",
> +       "100Hz",
> +       "400Hz",
> +       "600Hz",
> +       "800Hz",
> +       "1000Hz",
> +       "200-400Hz",
> +       "400-600Hz",
> +       "400-800Hz",
> +       "user-400Hz",
> +       "user-600Hz",
> +       "user-800Hz",
> +       "user-1000Hz"

These user options look very suspicous.

> +static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
> +                               struct snd_ctl_elem_value *ucontrol)
> +{
> +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +       struct max98088_priv *max98088 = codec->private_data;
> +       unsigned int *mode = &max98088->ex_mode;
> +
> +       *mode = ucontrol->value.integer.value[0];
> +
> +       if (*mode <= ARRAY_SIZE(ex_mode_table))
> +               max98088_write(codec, M98088_REG_41_SPKDHP,
> +                       ex_mode_table[*mode]);

Report the error if there's an invalid input.

> +static const char *max98088_hp_spk_mute[] = {"disable", "enable"};
> +static const struct soc_enum max98088_hp_spk_mute_enum[] = {
> +       SOC_ENUM_SINGLE_EXT(2, max98088_hp_spk_mute),

Why are you doing this rather than just using a standard switch control?
Even if there's a reason to not use one of the standard register
controls this should be presented to the application layer as a switch
rather than as an enumeration.

The same issue applies to the other outputs.

> +static const char *max98088_dcblk[] =  {"Off", "On"};
> +static const struct soc_enum max98088_dcblk_enum[] = {
> +       SOC_ENUM_SINGLE(M98088_REG_20_DAI2_FILTERS, 0, 2, max98088_dcblk),
> +};

Again, present a switch to userspace.

> +       SOC_DOUBLE_R("Headphone volume", M98088_REG_39_LVL_HP_L,
> +               M98088_REG_3A_LVL_HP_R, 0, 31, 0),

Volume, not volume.

> +       SOC_SINGLE("MIC1 gain", M98088_REG_35_LVL_MIC1, 0, 31, 1),
> +       SOC_SINGLE("MIC2 gain", M98088_REG_36_LVL_MIC2, 0, 31, 1),

> +       SOC_SINGLE("MIC1 pre", M98088_REG_35_LVL_MIC1, 5, 3, 0),
> +       SOC_SINGLE("MIC2 pre", M98088_REG_36_LVL_MIC2, 5, 3, 0),
> +
> +       SOC_SINGLE("INA gain", M98088_REG_37_LVL_INA, 0, 7, 1),
> +       SOC_SINGLE("INB gain", M98088_REG_38_LVL_INB, 0, 7, 1),

These should all be named " Volume", as should all your other " gain" or
" pre" controls.

> +       SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
> +       SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),

Capitalise switch.

> +/* Left speaker mixer switch */
> +static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
> +       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),

All of these should be " Switch".

> +static int max98088_hp_event(struct snd_soc_dapm_widget *w,
> +                            struct snd_kcontrol *kcontrol, int event)
> +{
> +       struct snd_soc_codec *codec = w->codec;
> +       u16 status;
> +
> +       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
> +
> +       /* powering down headphone gracefully */
> +       status = max98088_read(codec, M98088_REG_4D_PWR_EN_OUT);
> +       if ((status & M98088_HPEN) == M98088_HPEN) {
> +               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
> +                       (status & ~M98088_HPEN));
> +       }
> +       schedule_timeout_interruptible(msecs_to_jiffies(20));

This should be non-interruptible since there's no handling of any actual
interruptions.

> +/* DAPM AUDIO_MAP: */
> +static const struct snd_soc_dapm_route audio_map[] = {
> +       /* Left headphone output mixer */
> +       {"Left HP Mixer", "Left DAC1", "DACL1"},
> +       {"Left HP Mixer", "Left DAC2", "DACL2"},
> +       {"Left HP Mixer", "Right DAC1", "DACR1"},
> +       {"Left HP Mixer", "Right DAC2", "DACR2"},
> +       {"Left HP Mixer", "MIC1", "Mic Bias"},
> +       {"Left HP Mixer", "MIC2", "Mic Bias"},

This looks wrong - I'd expect this to be switching the microphone input
to the mixer, not the bias.  Mixing the bias in would just present a DC
level which isn't going to be great.  Similarly for all your other
microphone input switching.

> +               /* DAI clock master/slave wrt the codec */
> +               switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {

You should be using the masks for your switch statements.  Using
specific values is at best harder to read and at worst will lead to
false positives.  This applies to all your comparisons in this function.

> +                       /* BCI: normal bclk (rise) */
> +                       /* WCI: invert frame */
> +                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                               M98088_DAI_BCI, M98088_DAI_WCI);
> +                       break;
> +               case SND_SOC_DAIFMT_IB_NF:
> +                       /* BCI: invert bclk (fall) */
> +                       /* WCI: normal frame */
> +                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                               M98088_DAI_WCI, M98088_DAI_BCI);

This code (and most of the other update_bits() usages round here) looks
wrong - I'd expect to see a constant bitmask being supplied as the mask
argument then the value changing to set different values.

> +               max98088_write(codec, M98088_REG_16_DAI1_IOCFG,
> +                       (1<<6) |  /* SEL : map DAI1 to S1 */
> +                       (0<<5) |  /* LTEN : ADC->DAC loop-through enable */
> +                       (0<<4) |  /* LBEN : loopback (0=disable, 1=enable) */
> +                       (0<<3) |  /* DMONO : DAC SDIN (0=stereo, 1=mono) */

These look like things that should be configured by the user, especially
LTEN.

> +                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
> +                       (1<<1) |  /* SDOEN : serial data out ENABLE */
> +                       (1<<0));  /* SDIEN : serial data in ENABLE */

These look like they ought to be DAPM widgets for the AIF.

> +static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
> +                                  int clk_id, unsigned int freq, int dir)
> +{
> +       struct snd_soc_codec *codec = dai->codec;
> +       struct max98088_priv *max98088 = codec->private_data;
> +
> +       if (freq != max98088->sysclk) {

It would be clearer to just return in this case so you don't have so
much indentation.

> +               /* If codec is currently running, toggle reset */
> +               if (max98088_read(codec, M98088_REG_51_PWR_SYS)
> +                       & M98088_SHDNRUN) {
> +                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
> +                               M98088_SHDNRUN, 0);
> +                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
> +                               0, M98088_SHDNRUN);

Will this reset the whole chip?

> +static inline int rate_index(int rate)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
> +               if (rate_table[i].rate >= rate)
> +                       return i;
> +       }
> +       return 0;

This won't indicate if there's a failure to find a match.

> +       /* data 16/24 bit width */
> +       switch (params_format(params)) {
> +       case SNDRV_PCM_FORMAT_S16_LE:
> +               /* WS: 16bit */
> +               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                       M98088_DAI_WS, 0);
> +               break;
> +       case SNDRV_PCM_FORMAT_S24_LE:
> +               /* WS: 24bit */
> +               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
> +                       0, M98088_DAI_WS);
> +               break;
> +       }

Again, your snd_soc_update_bits() usage seems confused.  You're also not
checking for invalid inputs.  There are other instances of this in the
driver, I'll not comment them all.

> +       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
> +
> +       if (rate != cdata->rate) {
> +               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
> +               reg11val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
> +               max98088_write(codec, M98088_REG_11_DAI1_CLKMODE, reg11val);
> +               cdata->rate = rate;

Just directly use snd_soc_update_bits() - it will suppress null changes.

> +       /* Configure NI when operating as master */
> +       if (max98088_read(codec, M98088_REG_14_DAI1_FORMAT)
> +               & M98088_DAI_MAS) {
> +               BUG_ON(max98088->sysclk == 0);

BUG_ON() is unfriendly here - this is a machine driver configuration
error and normally BUG_ON() would be an internal error in the driver.
Better error reporting please.

> +static int max98088_set_bias_level(struct snd_soc_codec *codec,
> +                                 enum snd_soc_bias_level level)
> +{
> +       switch (level) {
> +       case SND_SOC_BIAS_ON:
> +               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
> +               break;
> +
> +       case SND_SOC_BIAS_PREPARE:
> +               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
> +               break;

Drop the _ON cacse if it's the same.  Also, how will this interact with
the management of SHDNRUN in hw_params()?

> +       case SND_SOC_BIAS_STANDBY:
> +               max98088_sync_cache(codec);
> +               max98088_write(codec, M98088_REG_51_PWR_SYS,
> +                       M98088_SHDNRUN|M98088_PWRSV);
> +               break;

Do you really want to sync the cache every time we drop into standby?

> +       /* Configure digital mic / external mic */
> +       /* Digital mic needs REG_15 OSR1=1 */
> +       regval = ((0<<6) | /* digital mic clock freq = PCLK/8 */
> +                 (0<<5) | /* DIGMICL enable */
> +                 (0<<4) | /* DIGMICR enable */
> +                 (0<<0)); /* external mic enable */

These look like they ought to be managed via DAPM?

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-08-31 21:08 Peter Hsiang
  2010-09-01 11:14   ` Mark Brown
  0 siblings, 1 reply; 81+ messages in thread
From: Peter Hsiang @ 2010-08-31 21:08 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Peter Hsiang, Jesse Marroquin,
	Liam Girdwood, Mark Brown, Peter Ujfalusi, Joonyoung Shim,
	alsa-devel, linux-kernel

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang at maxim-ic.com>
---
 include/sound/max98088.h    |   87 ++
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2871 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  161 +++
 5 files changed, 3125 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..7a6c53c
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,87 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+#define EQ_CFG_MAX 32
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* Speaker excursion limiter filter response configurations */
+#define EX_CFG_MAX 32
+
+struct max98088_excursion_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 resp[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq1_cfg;
+       struct max98088_eq_cfg *eq2_cfg;
+       unsigned int eq1_cfgcnt;
+       unsigned int eq2_cfgcnt;
+
+       /* Excursion limiters for DAI1 and DAI2 */
+       struct max98088_excursion_cfg *ex1_cfg;
+       struct max98088_excursion_cfg *ex2_cfg;
+       unsigned int ex1_cfgcnt;
+       unsigned int ex2_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microhpone input
+        */
+       unsigned int digmic_left_enable;
+       unsigned int digmic_right_enable;
+
+       /* Normal microphone connection, or external connection through INA
+        * 0 = normal connection
+        * 1 = INA1 input
+        * 2 = INA2 input
+        */
+       unsigned int extmic_mode;
+
+       /* Bypass option for INA to MIC1 connection
+        * 0 = normal setting
+        * 1 = bypass enabled
+        */
+       unsigned int ina_to_mic1_bypass;
+
+       /* Bypass option for MIC1 to MIC2 connection
+        * 0 = normal setting
+        * 1 = bypass enabled
+        */
+       unsigned int mic1_to_mic2_bypass;
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 155c127..e9a3c74 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -28,6 +28,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
        select SND_SOC_MAX9877 if I2C
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if I2C
@@ -156,6 +157,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 10d468e..fe9d104 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -87,6 +88,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..72f599f
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2871 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+static struct snd_soc_codec *max98088_codec;
+struct snd_soc_codec_device soc_codec_dev_max98088;
+
+/* configurations associated with each channel of DAI stream */
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+
+       /* Equalizer parameters */
+       int eq_textcnt;
+       const char *eq_texts[EQ_CFG_MAX];
+       int eq_sel;
+       struct soc_enum eq_enum;
+
+       /* Excursion limiter parameters */
+       int ex_textcnt;
+       const char *ex_texts[EX_CFG_MAX];
+       int ex_sel;
+       struct soc_enum ex_enum;
+};
+
+/* codec private data */
+struct max98088_priv {
+       struct snd_soc_codec codec;
+       struct max98088_pdata *pdata;
+       u8 reg_cache[M98088_REG_CNT];
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       u8 power_state;
+       unsigned int ex_mode;
+       unsigned int digmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x70, 0x70, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+/*
+ * Read the MAX98088 I2C register space
+ * Note: this driver source code is backward compatible to kernel
+ * version 2.6.32.
+ */
+static unsigned int max98088_read(struct snd_soc_codec *codec,
+                                 unsigned int reg)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct i2c_msg msg[2];
+       struct i2c_client *client;
+       u8 data[2];
+       int ret;
+
+       client = (struct i2c_client *)codec->control_data;
+
+       if (max98088_volatile_register(reg)) {
+               data[0] = reg;
+               msg[0].addr = client->addr;
+               msg[0].flags = 0;
+               msg[0].buf = &data[0];
+               msg[0].len = 1;
+
+               msg[1].addr = client->addr;
+               msg[1].flags = I2C_M_RD;
+               msg[1].buf = &data[1];
+               msg[1].len = 1;
+
+               ret = i2c_transfer(client->adapter, &msg[0], 2);
+               return (ret == 2) ? data[1] : -EIO;
+       } else {
+               if (reg > (ARRAY_SIZE(max98088_reg)))
+                       return -EIO;
+               return max98088->reg_cache[reg];
+       }
+}
+
+/* Write to the MAX98088 register space (cached) */
+static int max98088_write(struct snd_soc_codec *codec, unsigned int reg,
+                         unsigned int value)
+{
+       u8 data[2];
+       struct max98088_priv *max98088 = codec->private_data;
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2) {
+               max98088->reg_cache[reg] =
+                       (value & max98088_access[reg].writable);
+               return 0;
+       } else {
+               return -EIO;
+       }
+}
+
+/* Write to the MAX98088 I2C register space */
+static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 data[2];
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+/*
+ * For kernels compiled without unsigned long long int division
+ */
+unsigned long long int ulldiv(unsigned long long int dividend,
+                             unsigned long long int divisor)
+{
+       unsigned long long int quotient = 0;
+       int shift = 1;
+
+       if (divisor == 0)
+               return 0;
+
+       /* result is 1.0 if divisor and dividend are equal */
+       if (divisor == dividend)
+               return 1;
+
+       /* Normalize divisor */
+       while (!(divisor & 0x8000000000000000ULL)) {
+               divisor <<= 1;
+               ++shift;
+       }
+
+       /* Shift and subtract */
+       while (shift--) {
+               quotient <<= 1;
+
+               if (divisor <= dividend) {
+                       dividend -= divisor;
+                       ++quotient;
+               }
+               divisor >>= 1;
+       }
+
+       /* Round up */
+       if (dividend > divisor)
+               ++quotient;
+
+       return quotient;
+}
+
+#define INA1_PGA_BIT 0x01
+#define INA2_PGA_BIT 0x02
+#define INB1_PGA_BIT 0x04
+#define INB2_PGA_BIT 0x08
+/*
+ * The INx1 and INx2 PGAs share a power control signal.
+ * This function OR's the two power events to keep an unpowered INx
+ * from turning off it's counterpart.
+ * The control names are used to identify the PGA.
+ */
+static int max98088_pga_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       u8 *state = &max98088->power_state;
+       unsigned int val;
+       unsigned int pga;
+       unsigned int mask;
+
+       BUG_ON(w->reg != M98088_REG_4C_PWR_EN_IN);
+
+       if (strncmp(w->name, "INA1", 4) == 0) {
+               pga = INA1_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INA2", 4) == 0) {
+               pga = INA2_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INB1", 4) == 0) {
+               pga = INB1_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else if (strncmp(w->name, "INB2", 4) == 0) {
+               pga = INB2_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else {
+               return -EINVAL;
+       }
+
+       if (event == SND_SOC_DAPM_POST_PMU) {
+               /* ON */
+               *state |= pga;
+
+               /* Turn on, avoiding unnecessary writes */
+               val = max98088_read(codec, w->reg);
+               if (!(val & (1 << w->shift))) {
+                       val |= (1 << w->shift);
+                       max98088_write(codec, w->reg, val);
+               }
+       } else if (event == SND_SOC_DAPM_POST_PMD) {
+               /* OFF */
+               *state &= ~pga;
+
+               /* Turn off if both are off, avoiding unnecessary writes */
+               if (!(*state & mask)) {
+                       val = max98088_read(codec, w->reg);
+                       if (val & (1 << w->shift)) {
+                               val &= ~(1 << w->shift);
+                               max98088_write(codec, w->reg, val);
+                       }
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       if (band > 4)
+               return;
+
+       if (dai > 1)
+               return;
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               max98088_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               max98088_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+
+       return;
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_ex_mode[] = {
+       "Off",
+       "100Hz",
+       "400Hz",
+       "600Hz",
+       "800Hz",
+       "1000Hz",
+       "200-400Hz",
+       "400-600Hz",
+       "400-800Hz",
+       "user-400Hz",
+       "user-600Hz",
+       "user-800Hz",
+       "user-1000Hz"
+};
+
+static const unsigned int ex_mode_table[] = {
+       0x00,           /* disabled */
+       (0<<4)|3,       /* 100Hz */
+       (1<<4)|0,       /* 400Hz */
+       (2<<4)|0,       /* 600Hz */
+       (3<<4)|0,       /* 800Hz */
+       (4<<4)|0,       /* 1000Hz */
+       (1<<4)|1,       /* 200-400Hz */
+       (2<<4)|2,       /* 400-600Hz */
+       (3<<4)|2,       /* 400-800Hz */
+       (1<<4)|3,       /* user-400Hz */
+       (2<<4)|3,       /* user-600Hz */
+       (3<<4)|3,       /* user-800Hz */
+       (4<<4)|3    /* user-1000Hz */
+};
+
+static const struct soc_enum max98088_ex_mode_enum[] = {
+       SOC_ENUM_SINGLE_EXT(13, max98088_ex_mode),
+};
+
+/*
+ * Excursion limiter mode - set mode
+ */
+static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+
+       *mode = ucontrol->value.integer.value[0];
+
+       if (*mode <= ARRAY_SIZE(ex_mode_table))
+               max98088_write(codec, M98088_REG_41_SPKDHP,
+                       ex_mode_table[*mode]);
+
+       return 0;
+}
+
+/*
+ * Excursion limiter mode - get mode
+ */
+static int max98088_ex_mode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+/*
+ * Load user programmable mode excursion limiter filter coefficients
+ */
+static void max98088_ex_resp_control(struct snd_soc_codec *codec,
+                                    int reg, u16 *param)
+{
+       max98088_write(codec,   reg, M98088_BYTE1(param[0]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[0]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[1]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[1]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[2]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[2]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[3]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[3]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[4]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[4]));
+}
+
+static const char *max98088_hp_spk_mute[] = {"disable", "enable"};
+static const struct soc_enum max98088_hp_spk_mute_enum[] = {
+       SOC_ENUM_SINGLE_EXT(2, max98088_hp_spk_mute),
+};
+/* These defines correlate to the position of the like named value in
+ * max98088_hp_spk_mute[] */
+#define MAX98088_HP_SPK_MUTE_DISABLE 0
+#define MAX98088_HP_SPK_MUTE_ENABLE 1
+
+/*
+ * Mute the speaker
+ */
+static int max98088_spk_mute_set(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 lReg = max98088_read(codec, M98088_REG_3D_LVL_SPK_L);
+       u8 rReg = max98088_read(codec, M98088_REG_3E_LVL_SPK_R);
+
+       if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+               lReg |= M98088_SP_MUTE;
+               rReg |= M98088_SP_MUTE;
+       } else {
+               lReg &= ~M98088_SP_MUTE;
+               rReg &= ~M98088_SP_MUTE;
+       }
+
+       max98088_write(codec, M98088_REG_3D_LVL_SPK_L, lReg);
+       max98088_write(codec, M98088_REG_3E_LVL_SPK_R, rReg);
+
+       return 0;
+}
+
+/*
+ * Get speaker mute status
+ */
+static int max98088_spk_mute_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 reg = max98088_read(codec, M98088_REG_3D_LVL_SPK_L);
+
+       ucontrol->value.integer.value[0] = (reg & M98088_SP_MUTE) ? 1 : 0;
+
+       return 0;
+}
+
+/*
+ * Mute the headphone
+ */
+static int max98088_hp_mute_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 lReg = max98088_read(codec, M98088_REG_39_LVL_HP_L);
+       u8 rReg = max98088_read(codec, M98088_REG_3A_LVL_HP_R);
+
+       if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+               lReg |= M98088_HP_MUTE;
+               rReg |= M98088_HP_MUTE;
+       } else {
+               lReg &= ~M98088_HP_MUTE;
+               rReg &= ~M98088_HP_MUTE;
+       }
+
+       max98088_write(codec, M98088_REG_39_LVL_HP_L, lReg);
+       max98088_write(codec, M98088_REG_3A_LVL_HP_R, rReg);
+
+       return 0;
+}
+
+/*
+ * Get current headphone mute status
+ */
+static int max98088_hp_mute_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 reg = max98088_read(codec, M98088_REG_39_LVL_HP_L);
+
+       ucontrol->value.integer.value[0] = (reg & M98088_HP_MUTE) ? 1 : 0;
+
+       return 0;
+}
+
+/*
+ * Mute the receiver
+ */
+static int max98088_rec_mute_set(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 lReg = max98088_read(codec, M98088_REG_3B_LVL_REC_L);
+       u8 rReg = max98088_read(codec, M98088_REG_3C_LVL_REC_R);
+
+       if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+               lReg |= M98088_REC_MUTE;
+               rReg |= M98088_REC_MUTE;
+       } else {
+               lReg &= ~M98088_REC_MUTE;
+               rReg &= ~M98088_REC_MUTE;
+       }
+
+       max98088_write(codec, M98088_REG_3B_LVL_REC_L, lReg);
+       max98088_write(codec, M98088_REG_3C_LVL_REC_R, rReg);
+
+       return 0;
+}
+
+/*
+ * Get current receiver mute status
+ */
+static int max98088_rec_mute_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 reg = max98088_read(codec, M98088_REG_3B_LVL_REC_L);
+
+       ucontrol->value.integer.value[0] = (reg & M98088_REC_MUTE) ? 1 : 0;
+       return 0;
+}
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static const char *max98088_dcblk[] =  {"Off", "On"};
+static const struct soc_enum max98088_dcblk_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_20_DAI2_FILTERS, 0, 2, max98088_dcblk),
+};
+
+/* amixer controls (non-dapm) */
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       /* analog output levels */
+
+       SOC_DOUBLE_R("Headphone volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_ENUM_EXT("Headphone mute", max98088_hp_spk_mute_enum,
+               max98088_hp_mute_get, max98088_hp_mute_set),
+       SOC_ENUM_EXT("Speaker mute", max98088_hp_spk_mute_enum,
+               max98088_spk_mute_get, max98088_spk_mute_set),
+       SOC_ENUM_EXT("Receiver mute", max98088_hp_spk_mute_enum,
+               max98088_rec_mute_get, max98088_rec_mute_set),
+
+       /* analog input levels */
+
+       SOC_SINGLE("MIC1 gain", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 gain", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_SINGLE("MIC1 pre", M98088_REG_35_LVL_MIC1, 5, 3, 0),
+       SOC_SINGLE("MIC2 pre", M98088_REG_36_LVL_MIC2, 5, 3, 0),
+
+       SOC_SINGLE("INA gain", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB gain", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       /* ADC input digital gains and volume control */
+
+       SOC_SINGLE("ADCL volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL gain", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR gain", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       /* equalizer */
+
+       SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       /* excursion limiter */
+
+       SOC_ENUM_EXT("EX limiter mode", max98088_ex_mode_enum,
+               max98088_ex_mode_get, max98088_ex_mode_set),
+       SOC_ENUM("EX limiter threshold", max98088_ex_thresh_enum),
+
+       /* voice/music filters */
+
+       SOC_ENUM("DAI1 filter mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC filter", max98088_dai1_adc_filter_enum),
+       SOC_ENUM("DAI2 DC block", max98088_dcblk_enum),
+
+       /* automatic level control (for both DAI1/DAI2) */
+
+       SOC_SINGLE("ALC switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC release time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       /* power limiter */
+
+       SOC_SINGLE("PWR limiter threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR limiter weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR limiter time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR limiter time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       /* THD distortion limiter */
+
+       SOC_SINGLE("THD limiter thresh", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD limiter time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* DAPM sub-tables of the main dapm_widgets */
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_receiver_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_hp_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 status;
+
+       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+       /* powering down headphone gracefully */
+       status = max98088_read(codec, M98088_REG_4D_PWR_EN_OUT);
+       if ((status & M98088_HPEN) == M98088_HPEN) {
+               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
+                       (status & ~M98088_HPEN));
+       }
+       schedule_timeout_interruptible(msecs_to_jiffies(20));
+
+       return 0;
+}
+
+/* DAPM widgets top level */
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA_E("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_receiver_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_receiver_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("Mic Bias", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+/* DAPM AUDIO_MAP: */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1", "DACL1"},
+       {"Left HP Mixer", "Left DAC2", "DACL2"},
+       {"Left HP Mixer", "Right DAC1", "DACR1"},
+       {"Left HP Mixer", "Right DAC2", "DACR2"},
+       {"Left HP Mixer", "MIC1", "Mic Bias"},
+       {"Left HP Mixer", "MIC2", "Mic Bias"},
+       {"Left HP Mixer", "INA1", "INA1 Input"},
+       {"Left HP Mixer", "INA2", "INA2 Input"},
+       {"Left HP Mixer", "INB1", "INB1 Input"},
+       {"Left HP Mixer", "INB2", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1", "DACL1"},
+       {"Right HP Mixer", "Left DAC2", "DACL2" },
+       {"Right HP Mixer", "Right DAC1", "DACR1"},
+       {"Right HP Mixer", "Right DAC2", "DACR2"},
+       {"Right HP Mixer", "MIC1", "Mic Bias"},
+       {"Right HP Mixer", "MIC2", "Mic Bias"},
+       {"Right HP Mixer", "INA1", "INA1 Input"},
+       {"Right HP Mixer", "INA2", "INA2 Input"},
+       {"Right HP Mixer", "INB1", "INB1 Input"},
+       {"Right HP Mixer", "INB2", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2", "DACR2"},
+       {"Left SPK Mixer", "MIC1", "Mic Bias"},
+       {"Left SPK Mixer", "MIC2", "Mic Bias"},
+       {"Left SPK Mixer", "INA1", "INA1 Input"},
+       {"Left SPK Mixer", "INA2", "INA2 Input"},
+       {"Left SPK Mixer", "INB1", "INB1 Input"},
+       {"Left SPK Mixer", "INB2", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2", "DACR2"},
+       {"Right SPK Mixer", "MIC1", "Mic Bias"},
+       {"Right SPK Mixer", "MIC2", "Mic Bias"},
+       {"Right SPK Mixer", "INA1", "INA1 Input"},
+       {"Right SPK Mixer", "INA2", "INA2 Input"},
+       {"Right SPK Mixer", "INB1", "INB1 Input"},
+       {"Right SPK Mixer", "INB2", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1", "DACL1"},
+       {"Left REC Mixer", "Left DAC2", "DACL2"},
+       {"Left REC Mixer", "Right DAC1", "DACR1"},
+       {"Left REC Mixer", "Right DAC2", "DACR2"},
+       {"Left REC Mixer", "MIC1", "Mic Bias"},
+       {"Left REC Mixer", "MIC2", "Mic Bias"},
+       {"Left REC Mixer", "INA1", "INA1 Input"},
+       {"Left REC Mixer", "INA2", "INA2 Input"},
+       {"Left REC Mixer", "INB1", "INB1 Input"},
+       {"Left REC Mixer", "INB2", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1", "DACL1"},
+       {"Right REC Mixer", "Left DAC2", "DACL2"},
+       {"Right REC Mixer", "Right DAC1", "DACR1"},
+       {"Right REC Mixer", "Right DAC2", "DACR2"},
+       {"Right REC Mixer", "MIC1", "Mic Bias"},
+       {"Right REC Mixer", "MIC2", "Mic Bias"},
+       {"Right REC Mixer", "INA1", "INA1 Input"},
+       {"Right REC Mixer", "INA2", "INA2 Input"},
+       {"Right REC Mixer", "INB1", "INB1 Input"},
+       {"Right REC Mixer", "INB2", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1", "Mic Bias"},
+       {"Left ADC Mixer", "MIC2", "Mic Bias"},
+       {"Left ADC Mixer", "INA1", "INA1 Input"},
+       {"Left ADC Mixer", "INA2", "INA2 Input"},
+       {"Left ADC Mixer", "INB1", "INB1 Input"},
+       {"Left ADC Mixer", "INB2", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1", "Mic Bias"},
+       {"Right ADC Mixer", "MIC2", "Mic Bias"},
+       {"Right ADC Mixer", "INA1", "INA1 Input"},
+       {"Right ADC Mixer", "INA2", "INA2 Input"},
+       {"Right ADC Mixer", "INB1", "INB1 Input"},
+       {"Right ADC Mixer", "INB2", "INB2 Input"},
+
+       /* inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+
+       {"Mic Bias", NULL, "MIC1"},
+       {"Mic Bias", NULL, "MIC2"},
+};
+
+/*
+ * Add widgets
+ */
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       /* set up audio path interconnects */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+/*
+ * Setup DAI1 format
+ */
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       u8 regval;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               /* DAI clock master/slave wrt the codec */
+               switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {
+               case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+                       /* MAS: slave */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_MAS, 0);
+                       /* slave mode PLL */
+                       max98088_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       max98088_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM: /* codec clk, frm master */
+                       /* MAS: master */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               0, M98088_DAI_MAS);
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slv, frm mas */
+               case SND_SOC_DAIFMT_CBM_CFS: /* codec clk mas, frm slv */
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;;
+               }
+
+               /* I2S or TDM */
+               if ((fmt & SND_SOC_DAIFMT_I2S) == SND_SOC_DAIFMT_I2S) {
+                       /* TDM: I2S */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_TDM, 0);
+               } else {
+                       /* TDM: PCM/TDM */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               0, M98088_DAI_TDM);
+               }
+
+               /* DAI hardware signal inversions */
+               switch (fmt & SND_SOC_DAIFMT_NB_NF) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_BCI|M98088_DAI_WCI, 0);
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_BCI, M98088_DAI_WCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_WCI, M98088_DAI_BCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               0, M98088_DAI_BCI|M98088_DAI_WCI);
+                       break;
+               }
+
+               regval = (1<<0); /* BSEL : 64*LRCLK (for master mode only) */
+               if (max98088->digmic_mode)
+                       regval |= (1<<6); /* OSR : oversample ratio */
+               max98088_write(codec, M98088_REG_15_DAI1_CLOCK, regval);
+
+               max98088_write(codec, M98088_REG_16_DAI1_IOCFG,
+                       (1<<6) |  /* SEL : map DAI1 to S1 */
+                       (0<<5) |  /* LTEN : ADC->DAC loop-through enable */
+                       (0<<4) |  /* LBEN : loopback (0=disable, 1=enable) */
+                       (0<<3) |  /* DMONO : DAC SDIN (0=stereo, 1=mono) */
+                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
+                       (1<<1) |  /* SDOEN : serial data out ENABLE */
+                       (1<<0));  /* SDIEN : serial data in ENABLE */
+
+               max98088_write(codec, M98088_REG_17_DAI1_TDM,
+                       (0<<6) |  /* SLOTL : L in 1st slot */
+                       (1<<4) |  /* SLOTR : R in 2nd slot */
+                       (0<<0));  /* SLOTDLY : no delay */
+       }
+
+       return 0;
+}
+
+/*
+ * Setup DAI2 format
+ */
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               /* DAI clock master/slave wrt the codec */
+               switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {
+               case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+                       /* MAS: slave */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_MAS, 0);
+                       /* slave mode PLL */
+                       max98088_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       max98088_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+                       /* MAS: master */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               0, M98088_DAI_MAS);
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slv & frm mas */
+               case SND_SOC_DAIFMT_CBM_CFS: /* codec clk mas & frm slv */
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;;
+               }
+
+               /* I2S or TDM */
+               if ((fmt & SND_SOC_DAIFMT_I2S) == SND_SOC_DAIFMT_I2S) {
+                       /* TDM: I2S */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_TDM, 0);
+               } else {
+                       /* TDM: PCM/TDM */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               0, M98088_DAI_TDM);
+               }
+
+               /* DAI hardware signal inversions */
+               switch (fmt & SND_SOC_DAIFMT_NB_NF) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_BCI|M98088_DAI_WCI, 0);
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_BCI, M98088_DAI_WCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_WCI, M98088_DAI_BCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               0, M98088_DAI_BCI|M98088_DAI_WCI);
+                       break;
+               }
+
+               max98088_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       (1<<0)); /* BSEL: 64*LRCLK (for master mode only) */
+
+               max98088_write(codec, M98088_REG_1E_DAI2_IOCFG,
+                       (2<<6) |  /* SEL : map DAI2 to S2 */
+                       (0<<4) |  /* LBEN : loopback (0=disable) */
+                       (0<<3) |  /* DMONO : DAC SDIN input */
+                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
+                       (1<<1) |  /* SDOEN : serial data out ENABLE */
+                       (1<<0));  /* SDIEN : serial data in ENABLE */
+
+               max98088_write(codec, M98088_REG_1F_DAI2_TDM,
+                       (0<<6) |  /* SLOTL : L in 1st slot */
+                       (1<<4) |  /* SLOTR : R in 2nd slot */
+                       (0<<0));  /* SLOTDLY : no delay */
+       }
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+
+       if (freq != max98088->sysclk) {
+               max98088->sysclk = freq; /* remember current sysclk */
+
+               /* setup clocks for slave mode, and using the PLL
+                * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+                *         0x02 (when master clk is 20MHz to 30MHz)..
+                */
+               if ((freq >= 10000000) && (freq < 20000000)) {
+                       max98088_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+               } else if ((freq >= 20000000) && (freq < 30000000)) {
+                       max98088_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+               } else {
+                       dev_err(codec->dev, "Invalid master clock frequency\n");
+                       return -EINVAL;
+               }
+
+               /* If codec is currently running, toggle reset */
+               if (max98088_read(codec, M98088_REG_51_PWR_SYS)
+                       & M98088_SHDNRUN) {
+                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                               M98088_SHDNRUN, 0);
+                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                               0, M98088_SHDNRUN);
+               }
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+
+struct rate_table_struct {
+       u32 rate;
+       u8  sr1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct rate_table_struct rate_table[] = {
+       {8000,  0x1},
+       {11025, 0x2},
+       {16000, 0x3},
+       {22050, 0x4},
+       {24000, 0x5},
+       {32000, 0x6},
+       {44100, 0x7},
+       {48000, 0x8},
+       {88200, 0x9},
+       {96000, 0xA},
+};
+
+static inline int rate_index(int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate)
+                       return i;
+       }
+       return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 reg11val;
+       u16 ni;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       /* data 16/24 bit width */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               /* WS: 16bit */
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               /* WS: 24bit */
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       0, M98088_DAI_WS);
+               break;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
+               reg11val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
+               max98088_write(codec, M98088_REG_11_DAI1_CLKMODE, reg11val);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (max98088_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               BUG_ON(max98088->sysclk == 0);
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               max98088_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               max98088_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       (1<<3), 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       0, (1<<3));
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 reg19val;
+       u16 ni;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       /* data 16/24 bit width */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               /* WS: 16bit */
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               /* WS: 24bit */
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       0, M98088_DAI_WS);
+               break;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI2 SR2 value for the DSP; FREQ1:0=anyclock */
+               reg19val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
+               max98088_write(codec, M98088_REG_19_DAI2_CLKMODE, reg19val);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (max98088_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               BUG_ON(max98088->sysclk == 0);
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               max98088_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               max98088_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       (1<<3), 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       0, (1<<3));
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       int i;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               max98088_sync_cache(codec);
+               max98088_write(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN|M98088_PWRSV);
+               break;
+       case SND_SOC_BIAS_OFF:
+               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+struct snd_soc_dai max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+EXPORT_SYMBOL_GPL(max98088_dai);
+
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq1_cfg[best].name,
+               pdata->eq1_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq1_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq2_cfg[best].name,
+               pdata->eq2_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq2_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->eq1_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq1(codec);
+       return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->eq2_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq2(codec);
+       return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_handle_eq1_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq1control =
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->dai[0].eq_enum,
+                       max98088_get_eq1_enum,
+                       max98088_put_eq1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq1_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->eq_texts[i] = pdata->eq1_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       BUG_ON(&max98088->codec == 0);
+       BUG_ON((max98088->codec).card == 0);
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq2control =
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->dai[1].eq_enum,
+                       max98088_get_eq2_enum,
+                       max98088_put_eq2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq2_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               cdata->eq_texts[i] = pdata->eq2_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq2control, 1);
+       if (ret != 0)
+               printk(KERN_ERR "Failed to add EQ control: %d\n", ret);
+}
+
+
+static void max98088_setup_ex1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       struct max98088_excursion_cfg *coef_set;
+       int best, best_val, i, sel, fs;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->ex_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->ex_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->ex1_cfgcnt; i++) {
+               if (strcmp(pdata->ex1_cfg[i].name,
+                          cdata->ex_texts[sel]) == 0 &&
+                   abs(pdata->ex1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->ex1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->ex1_cfg[best].name,
+               pdata->ex1_cfg[best].rate, fs);
+
+       coef_set = &pdata->ex1_cfg[sel];
+
+       max98088_ex_resp_control(codec, M98088_REG_B6_DAI1_BIQUAD_BASE,
+               coef_set->resp);
+}
+
+static int max98088_put_ex1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->ex1_cfgcnt)
+               return -EINVAL;
+
+       cdata->ex_sel = sel;
+       max98088_setup_ex1(codec);
+       return 0;
+}
+
+
+static int max98088_get_ex1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+       ucontrol->value.enumerated.item[0] = cdata->ex_sel;
+
+       return 0;
+}
+
+static void max98088_handle_ex1_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new ex1control =
+               SOC_ENUM_EXT("EX1 Limiter Config",
+                            max98088->dai[0].ex_enum,
+                            max98088_get_ex1_enum,
+                            max98088_put_ex1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->ex_textcnt = 0;
+       for (i = 0; i < pdata->ex1_cfgcnt; i++) {
+               for (j = 0; j < cdata->ex_textcnt; j++) {
+                       if (strcmp(pdata->ex1_cfg[i].name,
+                                  cdata->ex_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->ex_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->ex_texts[i] = pdata->ex1_cfg[i].name;
+               cdata->ex_textcnt++;
+
+               if (cdata->ex_textcnt >= EX_CFG_MAX) {
+                       dev_err(codec->dev, "Too many limiter configs\n");
+                       cdata->ex_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EX1 configurations\n",
+               cdata->ex_textcnt);
+
+       /* now point the soc_enum to .texts array */
+       cdata->ex_enum.texts = cdata->ex_texts;
+       cdata->ex_enum.max = cdata->ex_textcnt;
+       ret = snd_soc_add_controls(&max98088->codec, &ex1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add limiter control\n");
+}
+
+
+static void max98088_setup_ex2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       struct max98088_excursion_cfg *coef_set;
+       int best, best_val, i, sel, fs;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->ex_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->ex_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->ex2_cfgcnt; i++) {
+               if (strcmp(pdata->ex2_cfg[i].name,
+                          cdata->ex_texts[sel]) == 0 &&
+                   abs(pdata->ex2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->ex2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->ex2_cfg[best].name,
+               pdata->ex2_cfg[best].rate, fs);
+
+       coef_set = &pdata->ex2_cfg[sel];
+
+       max98088_ex_resp_control(codec, M98088_REG_C0_DAI2_BIQUAD_BASE,
+               coef_set->resp);
+}
+
+static int max98088_put_ex2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->ex2_cfgcnt)
+               return -EINVAL;
+
+       cdata->ex_sel = sel;
+       max98088_setup_ex2(codec);
+       return 0;
+}
+
+static int max98088_get_ex2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+       ucontrol->value.enumerated.item[0] = cdata->ex_sel;
+
+       return 0;
+}
+
+static void max98088_handle_ex2_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new ex2control =
+               SOC_ENUM_EXT("EX2 Limiter Config",
+                            max98088->dai[1].ex_enum,
+                            max98088_get_ex2_enum,
+                            max98088_put_ex2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->ex_textcnt = 0;
+       for (i = 0; i < pdata->ex2_cfgcnt; i++) {
+               for (j = 0; j < cdata->ex_textcnt; j++) {
+                       if (strcmp(pdata->ex2_cfg[i].name,
+                                  cdata->ex_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->ex_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->ex_texts[i] = pdata->ex2_cfg[i].name;
+               cdata->ex_textcnt++;
+
+               if (cdata->ex_textcnt >= EX_CFG_MAX) {
+                       dev_err(codec->dev, "Too many limiter configs\n");
+                       cdata->ex_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EX2 configurations\n",
+               cdata->ex_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->ex_enum.texts = cdata->ex_texts;
+       cdata->ex_enum.max = cdata->ex_textcnt;
+
+       ret = snd_soc_add_controls(&max98088->codec, &ex2control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add limiter control\n");
+}
+
+static void max98088_handle_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval;
+
+       if (!pdata)
+               return;
+
+       /* Configure digital mic / external mic */
+       /* Digital mic needs REG_15 OSR1=1 */
+       regval = ((0<<6) | /* digital mic clock freq = PCLK/8 */
+                 (0<<5) | /* DIGMICL enable */
+                 (0<<4) | /* DIGMICR enable */
+                 (0<<0)); /* external mic enable */
+
+       if (pdata->digmic_left_enable)
+               regval |= (1<<5); /* digital left mic enabled */
+
+       if (pdata->digmic_right_enable)
+               regval |= (1<<4); /* digital right mic enabled */
+
+       max98088->digmic_mode = (regval ? 1 : 0);
+
+       if (pdata->extmic_mode < 3)
+               regval |= (pdata->extmic_mode << 0); /* external mic enabled */
+
+       max98088_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* configure external mic to mic1 bypass, mic1 to mic2 bypass */
+       regval = 0;
+
+       if (pdata->ina_to_mic1_bypass)
+               regval |= (1<<7);
+
+       if (pdata->mic1_to_mic2_bypass)
+               regval |= (1<<4);
+
+       max98088_write(codec, M98088_REG_4A_CFG_BYPASS, regval);
+
+       /* configure receiver output */
+       regval = ((pdata->receiver_mode) ? (1<<7) : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               (1<<7), regval);
+
+       /* configure equalizers */
+       if (pdata->eq1_cfgcnt)
+               max98088_handle_eq1_pdata(max98088);
+
+       if (pdata->eq2_cfgcnt)
+               max98088_handle_eq2_pdata(max98088);
+
+       /* configure excursion limiters */
+       if (pdata->ex1_cfgcnt)
+               max98088_handle_ex1_pdata(max98088);
+
+       if (pdata->ex2_cfgcnt)
+               max98088_handle_ex2_pdata(max98088);
+}
+
+static int max98088_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 *cache = codec->reg_cache;
+
+       /* toggle reset the shutdown bit of the codec hardware */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < M98088_REG_CNT; i++) {
+               if (i == M98088_REG_51_PWR_SYS)
+                       continue;
+
+               if (!max98088_access[i].writable)
+                       continue;
+
+               max98088_hw_write(codec, i, cache[i]);
+       }
+
+       /* now enter into the resume mode bias level */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+
+/*
+ * Make sure that a max98088 is attached to the I2C bus.
+ */
+static int max98088_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+       int ret = 0;
+
+       dev_printk(KERN_INFO, &pdev->dev, "MAX98088 Audio CODEC\n");
+
+       if (!max98088_codec) {
+               dev_err(&pdev->dev, "Codec device is not registered\n");
+               return -EINVAL;
+       }
+
+       socdev->card->codec = max98088_codec;
+       codec = max98088_codec;
+       max98088 = codec->private_data;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       /* install additional amixer controls: EQ and excursion limiter */
+       if (max98088->pdata)
+               max98088_handle_pdata(max98088);
+       else
+               dev_err(&pdev->dev, "No platform data\n");
+
+       max98088_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register card\n");
+               goto card_err;
+       }
+
+       return 0;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+pcm_err:
+       return ret;
+}
+
+static int max98088_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       if (codec->control_data)
+               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+/* expose to machine driver */
+struct snd_soc_codec_device soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98088);
+
+
+static int max98088_register(struct max98088_priv *max98088,
+                            enum snd_soc_control_type control)
+
+{
+       int ret, i, version;
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_cdata *cdata;
+
+       if (max98088_codec) {
+               dev_err(codec->dev, "Another MAX98088 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->name = "MAX98088";
+       codec->owner = THIS_MODULE;
+       codec->read = max98088_read;
+       codec->write = max98088_write;
+
+       /* setup DAPM event bias level and handler function */
+       codec->bias_level = SND_SOC_BIAS_STANDBY;
+       codec->set_bias_level = max98088_set_bias_level;
+
+       codec->dai = max98088_dai;
+       codec->num_dai = ARRAY_SIZE(max98088_dai);
+       codec->private_data = max98088;
+
+       codec->reg_cache_size = M98088_REG_CNT;
+       codec->reg_cache = &max98088->reg_cache;
+       codec->volatile_register = max98088_volatile_register;
+
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       version = max98088_read(codec, M98088_REG_FF_REV_ID);
+       dev_dbg(codec->dev, "MAX98088 revision 0x%02X\n", version);
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+       cdata->ex_textcnt = 0;
+       cdata->ex_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+       cdata->ex_textcnt = 0;
+       cdata->ex_sel = 0;
+
+       max98088_write(codec, M98088_REG_14_DAI1_FORMAT, M98088_DAI_DLY);
+       max98088_write(codec, M98088_REG_1C_DAI2_FORMAT, M98088_DAI_DLY);
+
+       max98088->power_state = 0; /* INA INB power enable state */
+       max98088->ex_mode = 0; /* excursion limiter mode */
+       max98088->digmic_mode = 0; /* 0=normal, 1=digital mic used */
+
+       /* disable device via dapm interface */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       max98088_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       max98088_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       max98088_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       max98088_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       /* power on device */
+       max98088_codec = codec;
+       for (i = 0; i < codec->num_dai; i++)
+               max98088_dai[i].dev = codec->dev;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dais(&max98088_dai[0], codec->num_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+               goto err_codec;
+       }
+
+       return ret;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(max98088);
+
+       return ret;
+}
+
+static void max98088_unregister(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       snd_soc_unregister_dais(max98088_dai, codec->num_dai);
+       snd_soc_unregister_codec(codec);
+       kfree(max98088);
+       max98088_codec = NULL;
+}
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       /* codec structure is inside the private data */
+       codec = &(max98088->codec);
+
+       codec->private_data = max98088;
+
+       codec->hw_write = (hw_write_t) i2c_master_send;
+       i2c_set_clientdata(i2c, max98088);
+       codec->control_data = i2c;
+
+       max98088->pdata = i2c->dev.platform_data;
+
+       codec->dev = &i2c->dev;
+
+       return max98088_register(max98088, SND_SOC_I2C);
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       struct max98088_priv *max98088 = i2c_get_clientdata(client);
+       max98088_unregister(max98088);
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "MAX98088 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+
+module_init(max98088_init);
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang <peter.hsiang@maxim-ic.com>");
+MODULE_AUTHOR("Jesse Marroquin <jesse.marroquin@maxim-ic.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..f7ef4c2
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,161 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_3A_LVL_HEADPHONE_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_RECEIVER_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPEAKER_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+struct max98088_setup_data {
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai max98088_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_max98088;
+
+#endif
--
1.6.3.3


^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH] ASoC: Add max98088 CODEC driver
@ 2010-08-31 21:08 Peter Hsiang
  0 siblings, 0 replies; 81+ messages in thread
From: Peter Hsiang @ 2010-08-31 21:08 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Peter Hsiang, Jesse Marroquin,
	Liam Girdwood

This patch adds the MAX98088 CODEC driver.

Signed-off-by: Peter Hsiang <peter.hsiang at maxim-ic.com>
---
 include/sound/max98088.h    |   87 ++
 sound/soc/codecs/Kconfig    |    4 +
 sound/soc/codecs/Makefile   |    2 +
 sound/soc/codecs/max98088.c | 2871 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98088.h |  161 +++
 5 files changed, 3125 insertions(+), 0 deletions(-)
 create mode 100644 include/sound/max98088.h
 create mode 100644 sound/soc/codecs/max98088.c
 create mode 100644 sound/soc/codecs/max98088.h

diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..7a6c53c
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,87 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+#define EQ_CFG_MAX 32
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 band1[5];
+       u16 band2[5];
+       u16 band3[5];
+       u16 band4[5];
+       u16 band5[5];
+};
+
+/* Speaker excursion limiter filter response configurations */
+#define EX_CFG_MAX 32
+
+struct max98088_excursion_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 resp[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+       /* Equalizers for DAI1 and DAI2 */
+       struct max98088_eq_cfg *eq1_cfg;
+       struct max98088_eq_cfg *eq2_cfg;
+       unsigned int eq1_cfgcnt;
+       unsigned int eq2_cfgcnt;
+
+       /* Excursion limiters for DAI1 and DAI2 */
+       struct max98088_excursion_cfg *ex1_cfg;
+       struct max98088_excursion_cfg *ex2_cfg;
+       unsigned int ex1_cfgcnt;
+       unsigned int ex2_cfgcnt;
+
+       /* Receiver output can be configured as power amplifier or LINE out */
+       /* Set receiver_mode to:
+        * 0 = amplifier output, or
+        * 1 = LINE level output
+        */
+       unsigned int receiver_mode;
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microhpone input
+        */
+       unsigned int digmic_left_enable;
+       unsigned int digmic_right_enable;
+
+       /* Normal microphone connection, or external connection through INA
+        * 0 = normal connection
+        * 1 = INA1 input
+        * 2 = INA2 input
+        */
+       unsigned int extmic_mode;
+
+       /* Bypass option for INA to MIC1 connection
+        * 0 = normal setting
+        * 1 = bypass enabled
+        */
+       unsigned int ina_to_mic1_bypass;
+
+       /* Bypass option for MIC1 to MIC2 connection
+        * 0 = normal setting
+        * 1 = bypass enabled
+        */
+       unsigned int mic1_to_mic2_bypass;
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 155c127..e9a3c74 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -28,6 +28,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_DA7210 if I2C
        select SND_SOC_JZ4740 if SOC_JZ4740
        select SND_SOC_MAX9877 if I2C
+       select SND_SOC_MAX98088 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if I2C
@@ -156,6 +157,9 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate

+config SND_SOC_MAX98088
+       tristate
+
 config SND_SOC_PCM3008
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 10d468e..fe9d104 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
@@ -87,6 +88,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088)  += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..72f599f
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2871 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+static struct snd_soc_codec *max98088_codec;
+struct snd_soc_codec_device soc_codec_dev_max98088;
+
+/* configurations associated with each channel of DAI stream */
+struct max98088_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+
+       /* Equalizer parameters */
+       int eq_textcnt;
+       const char *eq_texts[EQ_CFG_MAX];
+       int eq_sel;
+       struct soc_enum eq_enum;
+
+       /* Excursion limiter parameters */
+       int ex_textcnt;
+       const char *ex_texts[EX_CFG_MAX];
+       int ex_sel;
+       struct soc_enum ex_enum;
+};
+
+/* codec private data */
+struct max98088_priv {
+       struct snd_soc_codec codec;
+       struct max98088_pdata *pdata;
+       u8 reg_cache[M98088_REG_CNT];
+       unsigned int sysclk;
+       struct max98088_cdata dai[2];
+       u8 power_state;
+       unsigned int ex_mode;
+       unsigned int digmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+       0x00, /* 00 IRQ status */
+       0x00, /* 01 MIC status */
+       0x00, /* 02 jack status */
+       0x00, /* 03 battery voltage */
+       0x00, /* 04 */
+       0x00, /* 05 */
+       0x00, /* 06 */
+       0x00, /* 07 */
+       0x00, /* 08 */
+       0x00, /* 09 */
+       0x00, /* 0A */
+       0x00, /* 0B */
+       0x00, /* 0C */
+       0x00, /* 0D */
+       0x00, /* 0E */
+       0x00, /* 0F interrupt enable */
+
+       0x00, /* 10 master clock */
+       0x00, /* 11 DAI1 clock mode */
+       0x00, /* 12 DAI1 clock control */
+       0x00, /* 13 DAI1 clock control */
+       0x00, /* 14 DAI1 format */
+       0x00, /* 15 DAI1 clock */
+       0x00, /* 16 DAI1 config */
+       0x00, /* 17 DAI1 TDM */
+       0x00, /* 18 DAI1 filters */
+       0x00, /* 19 DAI2 clock mode */
+       0x00, /* 1A DAI2 clock control */
+       0x00, /* 1B DAI2 clock control */
+       0x00, /* 1C DAI2 format */
+       0x00, /* 1D DAI2 clock */
+       0x00, /* 1E DAI2 config */
+       0x00, /* 1F DAI2 TDM */
+
+       0x00, /* 20 DAI2 filters */
+       0x00, /* 21 data config */
+       0x00, /* 22 DAC mixer */
+       0x00, /* 23 left ADC mixer */
+       0x00, /* 24 right ADC mixer */
+       0x00, /* 25 left HP mixer */
+       0x00, /* 26 right HP mixer */
+       0x00, /* 27 HP control */
+       0x00, /* 28 left REC mixer */
+       0x00, /* 29 right REC mixer */
+       0x00, /* 2A REC control */
+       0x00, /* 2B left SPK mixer */
+       0x00, /* 2C right SPK mixer */
+       0x00, /* 2D SPK control */
+       0x00, /* 2E sidetone */
+       0x00, /* 2F DAI1 playback level */
+
+       0x00, /* 30 DAI1 playback level */
+       0x00, /* 31 DAI2 playback level */
+       0x00, /* 32 DAI2 playbakc level */
+       0x00, /* 33 left ADC level */
+       0x00, /* 34 right ADC level */
+       0x00, /* 35 MIC1 level */
+       0x00, /* 36 MIC2 level */
+       0x00, /* 37 INA level */
+       0x00, /* 38 INB level */
+       0x00, /* 39 left HP volume */
+       0x00, /* 3A right HP volume */
+       0x00, /* 3B left REC volume */
+       0x00, /* 3C right REC volume */
+       0x00, /* 3D left SPK volume */
+       0x00, /* 3E right SPK volume */
+       0x00, /* 3F MIC config */
+
+       0x00, /* 40 MIC threshold */
+       0x00, /* 41 excursion limiter filter */
+       0x00, /* 42 excursion limiter threshold */
+       0x00, /* 43 ALC */
+       0x00, /* 44 power limiter threshold */
+       0x00, /* 45 power limiter config */
+       0x00, /* 46 distortion limiter config */
+       0x00, /* 47 audio input */
+       0x00, /* 48 microphone */
+       0x00, /* 49 level control */
+       0x00, /* 4A bypass switches */
+       0x00, /* 4B jack detect */
+       0x00, /* 4C input enable */
+       0x00, /* 4D output enable */
+       0xF0, /* 4E bias control */
+       0x00, /* 4F DAC power */
+
+       0x0F, /* 50 DAC power */
+       0x00, /* 51 system */
+       0x00, /* 52 DAI1 EQ1 */
+       0x00, /* 53 DAI1 EQ1 */
+       0x00, /* 54 DAI1 EQ1 */
+       0x00, /* 55 DAI1 EQ1 */
+       0x00, /* 56 DAI1 EQ1 */
+       0x00, /* 57 DAI1 EQ1 */
+       0x00, /* 58 DAI1 EQ1 */
+       0x00, /* 59 DAI1 EQ1 */
+       0x00, /* 5A DAI1 EQ1 */
+       0x00, /* 5B DAI1 EQ1 */
+       0x00, /* 5C DAI1 EQ2 */
+       0x00, /* 5D DAI1 EQ2 */
+       0x00, /* 5E DAI1 EQ2 */
+       0x00, /* 5F DAI1 EQ2 */
+
+       0x00, /* 60 DAI1 EQ2 */
+       0x00, /* 61 DAI1 EQ2 */
+       0x00, /* 62 DAI1 EQ2 */
+       0x00, /* 63 DAI1 EQ2 */
+       0x00, /* 64 DAI1 EQ2 */
+       0x00, /* 65 DAI1 EQ2 */
+       0x00, /* 66 DAI1 EQ3 */
+       0x00, /* 67 DAI1 EQ3 */
+       0x00, /* 68 DAI1 EQ3 */
+       0x00, /* 69 DAI1 EQ3 */
+       0x00, /* 6A DAI1 EQ3 */
+       0x00, /* 6B DAI1 EQ3 */
+       0x00, /* 6C DAI1 EQ3 */
+       0x00, /* 6D DAI1 EQ3 */
+       0x00, /* 6E DAI1 EQ3 */
+       0x00, /* 6F DAI1 EQ3 */
+
+       0x00, /* 70 DAI1 EQ4 */
+       0x00, /* 71 DAI1 EQ4 */
+       0x00, /* 72 DAI1 EQ4 */
+       0x00, /* 73 DAI1 EQ4 */
+       0x00, /* 74 DAI1 EQ4 */
+       0x00, /* 75 DAI1 EQ4 */
+       0x00, /* 76 DAI1 EQ4 */
+       0x00, /* 77 DAI1 EQ4 */
+       0x00, /* 78 DAI1 EQ4 */
+       0x00, /* 79 DAI1 EQ4 */
+       0x00, /* 7A DAI1 EQ5 */
+       0x00, /* 7B DAI1 EQ5 */
+       0x00, /* 7C DAI1 EQ5 */
+       0x00, /* 7D DAI1 EQ5 */
+       0x00, /* 7E DAI1 EQ5 */
+       0x00, /* 7F DAI1 EQ5 */
+
+       0x00, /* 80 DAI1 EQ5 */
+       0x00, /* 81 DAI1 EQ5 */
+       0x00, /* 82 DAI1 EQ5 */
+       0x00, /* 83 DAI1 EQ5 */
+       0x00, /* 84 DAI2 EQ1 */
+       0x00, /* 85 DAI2 EQ1 */
+       0x00, /* 86 DAI2 EQ1 */
+       0x00, /* 87 DAI2 EQ1 */
+       0x00, /* 88 DAI2 EQ1 */
+       0x00, /* 89 DAI2 EQ1 */
+       0x00, /* 8A DAI2 EQ1 */
+       0x00, /* 8B DAI2 EQ1 */
+       0x00, /* 8C DAI2 EQ1 */
+       0x00, /* 8D DAI2 EQ1 */
+       0x00, /* 8E DAI2 EQ2 */
+       0x00, /* 8F DAI2 EQ2 */
+
+       0x00, /* 90 DAI2 EQ2 */
+       0x00, /* 91 DAI2 EQ2 */
+       0x00, /* 92 DAI2 EQ2 */
+       0x00, /* 93 DAI2 EQ2 */
+       0x00, /* 94 DAI2 EQ2 */
+       0x00, /* 95 DAI2 EQ2 */
+       0x00, /* 96 DAI2 EQ2 */
+       0x00, /* 97 DAI2 EQ2 */
+       0x00, /* 98 DAI2 EQ3 */
+       0x00, /* 99 DAI2 EQ3 */
+       0x00, /* 9A DAI2 EQ3 */
+       0x00, /* 9B DAI2 EQ3 */
+       0x00, /* 9C DAI2 EQ3 */
+       0x00, /* 9D DAI2 EQ3 */
+       0x00, /* 9E DAI2 EQ3 */
+       0x00, /* 9F DAI2 EQ3 */
+
+       0x00, /* A0 DAI2 EQ3 */
+       0x00, /* A1 DAI2 EQ3 */
+       0x00, /* A2 DAI2 EQ4 */
+       0x00, /* A3 DAI2 EQ4 */
+       0x00, /* A4 DAI2 EQ4 */
+       0x00, /* A5 DAI2 EQ4 */
+       0x00, /* A6 DAI2 EQ4 */
+       0x00, /* A7 DAI2 EQ4 */
+       0x00, /* A8 DAI2 EQ4 */
+       0x00, /* A9 DAI2 EQ4 */
+       0x00, /* AA DAI2 EQ4 */
+       0x00, /* AB DAI2 EQ4 */
+       0x00, /* AC DAI2 EQ5 */
+       0x00, /* AD DAI2 EQ5 */
+       0x00, /* AE DAI2 EQ5 */
+       0x00, /* AF DAI2 EQ5 */
+
+       0x00, /* B0 DAI2 EQ5 */
+       0x00, /* B1 DAI2 EQ5 */
+       0x00, /* B2 DAI2 EQ5 */
+       0x00, /* B3 DAI2 EQ5 */
+       0x00, /* B4 DAI2 EQ5 */
+       0x00, /* B5 DAI2 EQ5 */
+       0x00, /* B6 DAI1 biquad */
+       0x00, /* B7 DAI1 biquad */
+       0x00, /* B8 DAI1 biquad */
+       0x00, /* B9 DAI1 biquad */
+       0x00, /* BA DAI1 biquad */
+       0x00, /* BB DAI1 biquad */
+       0x00, /* BC DAI1 biquad */
+       0x00, /* BD DAI1 biquad */
+       0x00, /* BE DAI1 biquad */
+       0x00, /* BF DAI1 biquad */
+
+       0x00, /* C0 DAI2 biquad */
+       0x00, /* C1 DAI2 biquad */
+       0x00, /* C2 DAI2 biquad */
+       0x00, /* C3 DAI2 biquad */
+       0x00, /* C4 DAI2 biquad */
+       0x00, /* C5 DAI2 biquad */
+       0x00, /* C6 DAI2 biquad */
+       0x00, /* C7 DAI2 biquad */
+       0x00, /* C8 DAI2 biquad */
+       0x00, /* C9 DAI2 biquad */
+       0x00, /* CA */
+       0x00, /* CB */
+       0x00, /* CC */
+       0x00, /* CD */
+       0x00, /* CE */
+       0x00, /* CF */
+
+       0x00, /* D0 */
+       0x00, /* D1 */
+       0x00, /* D2 */
+       0x00, /* D3 */
+       0x00, /* D4 */
+       0x00, /* D5 */
+       0x00, /* D6 */
+       0x00, /* D7 */
+       0x00, /* D8 */
+       0x00, /* D9 */
+       0x00, /* DA */
+       0x70, /* DB */
+       0x00, /* DC */
+       0x00, /* DD */
+       0x00, /* DE */
+       0x00, /* DF */
+
+       0x00, /* E0 */
+       0x00, /* E1 */
+       0x00, /* E2 */
+       0x00, /* E3 */
+       0x00, /* E4 */
+       0x00, /* E5 */
+       0x00, /* E6 */
+       0x00, /* E7 */
+       0x00, /* E8 */
+       0x00, /* E9 */
+       0x00, /* EA */
+       0x00, /* EB */
+       0x00, /* EC */
+       0x00, /* ED */
+       0x00, /* EE */
+       0x00, /* EF */
+
+       0x00, /* F0 */
+       0x00, /* F1 */
+       0x00, /* F2 */
+       0x00, /* F3 */
+       0x00, /* F4 */
+       0x00, /* F5 */
+       0x00, /* F6 */
+       0x00, /* F7 */
+       0x00, /* F8 */
+       0x00, /* F9 */
+       0x00, /* FA */
+       0x00, /* FB */
+       0x00, /* FC */
+       0x00, /* FD */
+       0x00, /* FE */
+       0x00, /* FF */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} max98088_access[M98088_REG_CNT] = {
+       { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+       { 0xFF, 0x00, 1 }, /* 01 MIC status */
+       { 0xFF, 0x00, 1 }, /* 02 jack status */
+       { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+       { 0xFF, 0xFF, 0 }, /* 04 */
+       { 0xFF, 0xFF, 0 }, /* 05 */
+       { 0xFF, 0xFF, 0 }, /* 06 */
+       { 0xFF, 0xFF, 0 }, /* 07 */
+       { 0xFF, 0xFF, 0 }, /* 08 */
+       { 0xFF, 0xFF, 0 }, /* 09 */
+       { 0xFF, 0xFF, 0 }, /* 0A */
+       { 0xFF, 0xFF, 0 }, /* 0B */
+       { 0xFF, 0xFF, 0 }, /* 0C */
+       { 0xFF, 0xFF, 0 }, /* 0D */
+       { 0xFF, 0xFF, 0 }, /* 0E */
+       { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+       { 0xFF, 0xFF, 0 }, /* 10 master clock */
+       { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+       { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+       { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+       { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+       { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+       { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+       { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+       { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+       { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+       { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+       { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+       { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+       { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+       { 0xFF, 0xFF, 0 }, /* 21 data config */
+       { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+       { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+       { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+       { 0xFF, 0xFF, 0 }, /* 27 HP control */
+       { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+       { 0xFF, 0xFF, 0 }, /* 2A REC control */
+       { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+       { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+       { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+       { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+       { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+       { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+       { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+       { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+       { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+       { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+       { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+       { 0xFF, 0xFF, 0 }, /* 37 INA level */
+       { 0xFF, 0xFF, 0 }, /* 38 INB level */
+       { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+       { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+       { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+       { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+       { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+       { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+       { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 43 ALC */
+       { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+       { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+       { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+       { 0xFF, 0xFF, 0 }, /* 47 audio input */
+       { 0xFF, 0xFF, 0 }, /* 48 microphone */
+       { 0xFF, 0xFF, 0 }, /* 49 level control */
+       { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+       { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+       { 0xFF, 0xFF, 0 }, /* 4C input enable */
+       { 0xFF, 0xFF, 0 }, /* 4D output enable */
+       { 0xFF, 0xFF, 0 }, /* 4E bias control */
+       { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+       { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+       { 0xFF, 0xFF, 0 }, /* 51 system */
+       { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+       { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+       { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+       { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+       { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+       { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+       { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+       { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+       { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+       { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+       { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+       { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+       { 0x00, 0x00, 0 }, /* CA */
+       { 0x00, 0x00, 0 }, /* CB */
+       { 0x00, 0x00, 0 }, /* CC */
+       { 0x00, 0x00, 0 }, /* CD */
+       { 0x00, 0x00, 0 }, /* CE */
+       { 0x00, 0x00, 0 }, /* CF */
+
+       { 0x00, 0x00, 0 }, /* D0 */
+       { 0x00, 0x00, 0 }, /* D1 */
+       { 0x00, 0x00, 0 }, /* D2 */
+       { 0x00, 0x00, 0 }, /* D3 */
+       { 0x00, 0x00, 0 }, /* D4 */
+       { 0x00, 0x00, 0 }, /* D5 */
+       { 0x00, 0x00, 0 }, /* D6 */
+       { 0x00, 0x00, 0 }, /* D7 */
+       { 0x00, 0x00, 0 }, /* D8 */
+       { 0x00, 0x00, 0 }, /* D9 */
+       { 0x00, 0x00, 0 }, /* DA */
+       { 0x70, 0x70, 0 }, /* DB */
+       { 0x00, 0x00, 0 }, /* DC */
+       { 0x00, 0x00, 0 }, /* DD */
+       { 0x00, 0x00, 0 }, /* DE */
+       { 0x00, 0x00, 0 }, /* DF */
+
+       { 0x00, 0x00, 0 }, /* E0 */
+       { 0x00, 0x00, 0 }, /* E1 */
+       { 0x00, 0x00, 0 }, /* E2 */
+       { 0x00, 0x00, 0 }, /* E3 */
+       { 0x00, 0x00, 0 }, /* E4 */
+       { 0x00, 0x00, 0 }, /* E5 */
+       { 0x00, 0x00, 0 }, /* E6 */
+       { 0x00, 0x00, 0 }, /* E7 */
+       { 0x00, 0x00, 0 }, /* E8 */
+       { 0x00, 0x00, 0 }, /* E9 */
+       { 0x00, 0x00, 0 }, /* EA */
+       { 0x00, 0x00, 0 }, /* EB */
+       { 0x00, 0x00, 0 }, /* EC */
+       { 0x00, 0x00, 0 }, /* ED */
+       { 0x00, 0x00, 0 }, /* EE */
+       { 0x00, 0x00, 0 }, /* EF */
+
+       { 0x00, 0x00, 0 }, /* F0 */
+       { 0x00, 0x00, 0 }, /* F1 */
+       { 0x00, 0x00, 0 }, /* F2 */
+       { 0x00, 0x00, 0 }, /* F3 */
+       { 0x00, 0x00, 0 }, /* F4 */
+       { 0x00, 0x00, 0 }, /* F5 */
+       { 0x00, 0x00, 0 }, /* F6 */
+       { 0x00, 0x00, 0 }, /* F7 */
+       { 0x00, 0x00, 0 }, /* F8 */
+       { 0x00, 0x00, 0 }, /* F9 */
+       { 0x00, 0x00, 0 }, /* FA */
+       { 0x00, 0x00, 0 }, /* FB */
+       { 0x00, 0x00, 0 }, /* FC */
+       { 0x00, 0x00, 0 }, /* FD */
+       { 0x00, 0x00, 0 }, /* FE */
+       { 0xFF, 0x00, 1 }, /* FF */
+};
+
+
+static int max98088_volatile_register(unsigned int reg)
+{
+       return max98088_access[reg].vol;
+}
+
+/*
+ * Read the MAX98088 I2C register space
+ * Note: this driver source code is backward compatible to kernel
+ * version 2.6.32.
+ */
+static unsigned int max98088_read(struct snd_soc_codec *codec,
+                                 unsigned int reg)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct i2c_msg msg[2];
+       struct i2c_client *client;
+       u8 data[2];
+       int ret;
+
+       client = (struct i2c_client *)codec->control_data;
+
+       if (max98088_volatile_register(reg)) {
+               data[0] = reg;
+               msg[0].addr = client->addr;
+               msg[0].flags = 0;
+               msg[0].buf = &data[0];
+               msg[0].len = 1;
+
+               msg[1].addr = client->addr;
+               msg[1].flags = I2C_M_RD;
+               msg[1].buf = &data[1];
+               msg[1].len = 1;
+
+               ret = i2c_transfer(client->adapter, &msg[0], 2);
+               return (ret == 2) ? data[1] : -EIO;
+       } else {
+               if (reg > (ARRAY_SIZE(max98088_reg)))
+                       return -EIO;
+               return max98088->reg_cache[reg];
+       }
+}
+
+/* Write to the MAX98088 register space (cached) */
+static int max98088_write(struct snd_soc_codec *codec, unsigned int reg,
+                         unsigned int value)
+{
+       u8 data[2];
+       struct max98088_priv *max98088 = codec->private_data;
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2) {
+               max98088->reg_cache[reg] =
+                       (value & max98088_access[reg].writable);
+               return 0;
+       } else {
+               return -EIO;
+       }
+}
+
+/* Write to the MAX98088 I2C register space */
+static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 data[2];
+
+       data[0] = reg;
+       data[1] = value;
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+/*
+ * For kernels compiled without unsigned long long int division
+ */
+unsigned long long int ulldiv(unsigned long long int dividend,
+                             unsigned long long int divisor)
+{
+       unsigned long long int quotient = 0;
+       int shift = 1;
+
+       if (divisor == 0)
+               return 0;
+
+       /* result is 1.0 if divisor and dividend are equal */
+       if (divisor == dividend)
+               return 1;
+
+       /* Normalize divisor */
+       while (!(divisor & 0x8000000000000000ULL)) {
+               divisor <<= 1;
+               ++shift;
+       }
+
+       /* Shift and subtract */
+       while (shift--) {
+               quotient <<= 1;
+
+               if (divisor <= dividend) {
+                       dividend -= divisor;
+                       ++quotient;
+               }
+               divisor >>= 1;
+       }
+
+       /* Round up */
+       if (dividend > divisor)
+               ++quotient;
+
+       return quotient;
+}
+
+#define INA1_PGA_BIT 0x01
+#define INA2_PGA_BIT 0x02
+#define INB1_PGA_BIT 0x04
+#define INB2_PGA_BIT 0x08
+/*
+ * The INx1 and INx2 PGAs share a power control signal.
+ * This function OR's the two power events to keep an unpowered INx
+ * from turning off it's counterpart.
+ * The control names are used to identify the PGA.
+ */
+static int max98088_pga_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       u8 *state = &max98088->power_state;
+       unsigned int val;
+       unsigned int pga;
+       unsigned int mask;
+
+       BUG_ON(w->reg != M98088_REG_4C_PWR_EN_IN);
+
+       if (strncmp(w->name, "INA1", 4) == 0) {
+               pga = INA1_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INA2", 4) == 0) {
+               pga = INA2_PGA_BIT;
+               mask = INA1_PGA_BIT | INA2_PGA_BIT;
+       } else if (strncmp(w->name, "INB1", 4) == 0) {
+               pga = INB1_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else if (strncmp(w->name, "INB2", 4) == 0) {
+               pga = INB2_PGA_BIT;
+               mask = INB1_PGA_BIT | INB2_PGA_BIT;
+       } else {
+               return -EINVAL;
+       }
+
+       if (event == SND_SOC_DAPM_POST_PMU) {
+               /* ON */
+               *state |= pga;
+
+               /* Turn on, avoiding unnecessary writes */
+               val = max98088_read(codec, w->reg);
+               if (!(val & (1 << w->shift))) {
+                       val |= (1 << w->shift);
+                       max98088_write(codec, w->reg, val);
+               }
+       } else if (event == SND_SOC_DAPM_POST_PMD) {
+               /* OFF */
+               *state &= ~pga;
+
+               /* Turn off if both are off, avoiding unnecessary writes */
+               if (!(*state & mask)) {
+                       val = max98088_read(codec, w->reg);
+                       if (val & (1 << w->shift)) {
+                               val &= ~(1 << w->shift);
+                               max98088_write(codec, w->reg, val);
+                       }
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+                   unsigned int band, u16 *coefs)
+{
+       unsigned int eq_reg;
+       unsigned int i;
+
+       if (band > 4)
+               return;
+
+       if (dai > 1)
+               return;
+
+       /* Load the base register address */
+       eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+       /* Add the band address offset, note adjustment for word address */
+       eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+       /* Step through the registers and coefs */
+       for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+               max98088_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+               max98088_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+       }
+
+       return;
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_ex_mode[] = {
+       "Off",
+       "100Hz",
+       "400Hz",
+       "600Hz",
+       "800Hz",
+       "1000Hz",
+       "200-400Hz",
+       "400-600Hz",
+       "400-800Hz",
+       "user-400Hz",
+       "user-600Hz",
+       "user-800Hz",
+       "user-1000Hz"
+};
+
+static const unsigned int ex_mode_table[] = {
+       0x00,           /* disabled */
+       (0<<4)|3,       /* 100Hz */
+       (1<<4)|0,       /* 400Hz */
+       (2<<4)|0,       /* 600Hz */
+       (3<<4)|0,       /* 800Hz */
+       (4<<4)|0,       /* 1000Hz */
+       (1<<4)|1,       /* 200-400Hz */
+       (2<<4)|2,       /* 400-600Hz */
+       (3<<4)|2,       /* 400-800Hz */
+       (1<<4)|3,       /* user-400Hz */
+       (2<<4)|3,       /* user-600Hz */
+       (3<<4)|3,       /* user-800Hz */
+       (4<<4)|3    /* user-1000Hz */
+};
+
+static const struct soc_enum max98088_ex_mode_enum[] = {
+       SOC_ENUM_SINGLE_EXT(13, max98088_ex_mode),
+};
+
+/*
+ * Excursion limiter mode - set mode
+ */
+static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+
+       *mode = ucontrol->value.integer.value[0];
+
+       if (*mode <= ARRAY_SIZE(ex_mode_table))
+               max98088_write(codec, M98088_REG_41_SPKDHP,
+                       ex_mode_table[*mode]);
+
+       return 0;
+}
+
+/*
+ * Excursion limiter mode - get mode
+ */
+static int max98088_ex_mode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       unsigned int *mode = &max98088->ex_mode;
+
+       ucontrol->value.integer.value[0] = *mode;
+       return 0;
+}
+
+/*
+ * Load user programmable mode excursion limiter filter coefficients
+ */
+static void max98088_ex_resp_control(struct snd_soc_codec *codec,
+                                    int reg, u16 *param)
+{
+       max98088_write(codec,   reg, M98088_BYTE1(param[0]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[0]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[1]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[1]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[2]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[2]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[3]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[3]));
+       max98088_write(codec, ++reg, M98088_BYTE1(param[4]));
+       max98088_write(codec, ++reg, M98088_BYTE0(param[4]));
+}
+
+static const char *max98088_hp_spk_mute[] = {"disable", "enable"};
+static const struct soc_enum max98088_hp_spk_mute_enum[] = {
+       SOC_ENUM_SINGLE_EXT(2, max98088_hp_spk_mute),
+};
+/* These defines correlate to the position of the like named value in
+ * max98088_hp_spk_mute[] */
+#define MAX98088_HP_SPK_MUTE_DISABLE 0
+#define MAX98088_HP_SPK_MUTE_ENABLE 1
+
+/*
+ * Mute the speaker
+ */
+static int max98088_spk_mute_set(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 lReg = max98088_read(codec, M98088_REG_3D_LVL_SPK_L);
+       u8 rReg = max98088_read(codec, M98088_REG_3E_LVL_SPK_R);
+
+       if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+               lReg |= M98088_SP_MUTE;
+               rReg |= M98088_SP_MUTE;
+       } else {
+               lReg &= ~M98088_SP_MUTE;
+               rReg &= ~M98088_SP_MUTE;
+       }
+
+       max98088_write(codec, M98088_REG_3D_LVL_SPK_L, lReg);
+       max98088_write(codec, M98088_REG_3E_LVL_SPK_R, rReg);
+
+       return 0;
+}
+
+/*
+ * Get speaker mute status
+ */
+static int max98088_spk_mute_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 reg = max98088_read(codec, M98088_REG_3D_LVL_SPK_L);
+
+       ucontrol->value.integer.value[0] = (reg & M98088_SP_MUTE) ? 1 : 0;
+
+       return 0;
+}
+
+/*
+ * Mute the headphone
+ */
+static int max98088_hp_mute_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 lReg = max98088_read(codec, M98088_REG_39_LVL_HP_L);
+       u8 rReg = max98088_read(codec, M98088_REG_3A_LVL_HP_R);
+
+       if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+               lReg |= M98088_HP_MUTE;
+               rReg |= M98088_HP_MUTE;
+       } else {
+               lReg &= ~M98088_HP_MUTE;
+               rReg &= ~M98088_HP_MUTE;
+       }
+
+       max98088_write(codec, M98088_REG_39_LVL_HP_L, lReg);
+       max98088_write(codec, M98088_REG_3A_LVL_HP_R, rReg);
+
+       return 0;
+}
+
+/*
+ * Get current headphone mute status
+ */
+static int max98088_hp_mute_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 reg = max98088_read(codec, M98088_REG_39_LVL_HP_L);
+
+       ucontrol->value.integer.value[0] = (reg & M98088_HP_MUTE) ? 1 : 0;
+
+       return 0;
+}
+
+/*
+ * Mute the receiver
+ */
+static int max98088_rec_mute_set(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 lReg = max98088_read(codec, M98088_REG_3B_LVL_REC_L);
+       u8 rReg = max98088_read(codec, M98088_REG_3C_LVL_REC_R);
+
+       if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+               lReg |= M98088_REC_MUTE;
+               rReg |= M98088_REC_MUTE;
+       } else {
+               lReg &= ~M98088_REC_MUTE;
+               rReg &= ~M98088_REC_MUTE;
+       }
+
+       max98088_write(codec, M98088_REG_3B_LVL_REC_L, lReg);
+       max98088_write(codec, M98088_REG_3C_LVL_REC_R, rReg);
+
+       return 0;
+}
+
+/*
+ * Get current receiver mute status
+ */
+static int max98088_rec_mute_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 reg = max98088_read(codec, M98088_REG_3B_LVL_REC_L);
+
+       ucontrol->value.integer.value[0] = (reg & M98088_REC_MUTE) ? 1 : 0;
+       return 0;
+}
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+       "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+               max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_dai1_fltr[] = {
+       "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+       "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static const char *max98088_dcblk[] =  {"Off", "On"};
+static const struct soc_enum max98088_dcblk_enum[] = {
+       SOC_ENUM_SINGLE(M98088_REG_20_DAI2_FILTERS, 0, 2, max98088_dcblk),
+};
+
+/* amixer controls (non-dapm) */
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+       /* analog output levels */
+
+       SOC_DOUBLE_R("Headphone volume", M98088_REG_39_LVL_HP_L,
+               M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+       SOC_DOUBLE_R("Speaker volume", M98088_REG_3D_LVL_SPK_L,
+               M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+       SOC_DOUBLE_R("Receiver volume", M98088_REG_3B_LVL_REC_L,
+               M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+       SOC_ENUM_EXT("Headphone mute", max98088_hp_spk_mute_enum,
+               max98088_hp_mute_get, max98088_hp_mute_set),
+       SOC_ENUM_EXT("Speaker mute", max98088_hp_spk_mute_enum,
+               max98088_spk_mute_get, max98088_spk_mute_set),
+       SOC_ENUM_EXT("Receiver mute", max98088_hp_spk_mute_enum,
+               max98088_rec_mute_get, max98088_rec_mute_set),
+
+       /* analog input levels */
+
+       SOC_SINGLE("MIC1 gain", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+       SOC_SINGLE("MIC2 gain", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+       SOC_SINGLE("MIC1 pre", M98088_REG_35_LVL_MIC1, 5, 3, 0),
+       SOC_SINGLE("MIC2 pre", M98088_REG_36_LVL_MIC2, 5, 3, 0),
+
+       SOC_SINGLE("INA gain", M98088_REG_37_LVL_INA, 0, 7, 1),
+       SOC_SINGLE("INB gain", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+       /* ADC input digital gains and volume control */
+
+       SOC_SINGLE("ADCL volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+       SOC_SINGLE("ADCR volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+       SOC_SINGLE("ADCL gain", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+       SOC_SINGLE("ADCR gain", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+       /* equalizer */
+
+       SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+       SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+       /* excursion limiter */
+
+       SOC_ENUM_EXT("EX limiter mode", max98088_ex_mode_enum,
+               max98088_ex_mode_get, max98088_ex_mode_set),
+       SOC_ENUM("EX limiter threshold", max98088_ex_thresh_enum),
+
+       /* voice/music filters */
+
+       SOC_ENUM("DAI1 filter mode", max98088_filter_mode_enum),
+       SOC_ENUM("DAI1 DAC filter", max98088_dai1_dac_filter_enum),
+       SOC_ENUM("DAI1 ADC filter", max98088_dai1_adc_filter_enum),
+       SOC_ENUM("DAI2 DC block", max98088_dcblk_enum),
+
+       /* automatic level control (for both DAI1/DAI2) */
+
+       SOC_SINGLE("ALC switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+       SOC_SINGLE("ALC threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+       SOC_SINGLE("ALC multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+       SOC_SINGLE("ALC release time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+       /* power limiter */
+
+       SOC_SINGLE("PWR limiter threshold", M98088_REG_44_PWRLMT_CFG,
+               4, 15, 0),
+       SOC_SINGLE("PWR limiter weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+       SOC_SINGLE("PWR limiter time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+       SOC_SINGLE("PWR limiter time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+       /* THD distortion limiter */
+
+       SOC_SINGLE("THD limiter thresh", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+       SOC_SINGLE("THD limiter time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* DAPM sub-tables of the main dapm_widgets */
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_receiver_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC1", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+       SOC_DAPM_SINGLE("MIC1", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+       SOC_DAPM_SINGLE("MIC2", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+       SOC_DAPM_SINGLE("INA1", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+       SOC_DAPM_SINGLE("INA2", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+       SOC_DAPM_SINGLE("INB1", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+       SOC_DAPM_SINGLE("INB2", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_hp_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 status;
+
+       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+       /* powering down headphone gracefully */
+       status = max98088_read(codec, M98088_REG_4D_PWR_EN_OUT);
+       if ((status & M98088_HPEN) == M98088_HPEN) {
+               max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
+                       (status & ~M98088_HPEN));
+       }
+       schedule_timeout_interruptible(msecs_to_jiffies(20));
+
+       return 0;
+}
+
+/* DAPM widgets top level */
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+       SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+       SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+       SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 1, 0),
+       SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+               M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+       SND_SOC_DAPM_PGA_E("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+               7, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+               6, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+               4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_left_receiver_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_receiver_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+         &max98088_right_rec_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_left_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98088_right_ADC_mixer_controls[0],
+               ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+       SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+               7, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+               6, 0, NULL, 0, max98088_pga_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MICBIAS("Mic Bias", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RECL"),
+       SND_SOC_DAPM_OUTPUT("RECR"),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("INA1"),
+       SND_SOC_DAPM_INPUT("INA2"),
+       SND_SOC_DAPM_INPUT("INB1"),
+       SND_SOC_DAPM_INPUT("INB2"),
+};
+
+/* DAPM AUDIO_MAP: */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Left headphone output mixer */
+       {"Left HP Mixer", "Left DAC1", "DACL1"},
+       {"Left HP Mixer", "Left DAC2", "DACL2"},
+       {"Left HP Mixer", "Right DAC1", "DACR1"},
+       {"Left HP Mixer", "Right DAC2", "DACR2"},
+       {"Left HP Mixer", "MIC1", "Mic Bias"},
+       {"Left HP Mixer", "MIC2", "Mic Bias"},
+       {"Left HP Mixer", "INA1", "INA1 Input"},
+       {"Left HP Mixer", "INA2", "INA2 Input"},
+       {"Left HP Mixer", "INB1", "INB1 Input"},
+       {"Left HP Mixer", "INB2", "INB2 Input"},
+
+       /* Right headphone output mixer */
+       {"Right HP Mixer", "Left DAC1", "DACL1"},
+       {"Right HP Mixer", "Left DAC2", "DACL2" },
+       {"Right HP Mixer", "Right DAC1", "DACR1"},
+       {"Right HP Mixer", "Right DAC2", "DACR2"},
+       {"Right HP Mixer", "MIC1", "Mic Bias"},
+       {"Right HP Mixer", "MIC2", "Mic Bias"},
+       {"Right HP Mixer", "INA1", "INA1 Input"},
+       {"Right HP Mixer", "INA2", "INA2 Input"},
+       {"Right HP Mixer", "INB1", "INB1 Input"},
+       {"Right HP Mixer", "INB2", "INB2 Input"},
+
+       /* Left speaker output mixer */
+       {"Left SPK Mixer", "Left DAC1", "DACL1"},
+       {"Left SPK Mixer", "Left DAC2", "DACL2"},
+       {"Left SPK Mixer", "Right DAC1", "DACR1"},
+       {"Left SPK Mixer", "Right DAC2", "DACR2"},
+       {"Left SPK Mixer", "MIC1", "Mic Bias"},
+       {"Left SPK Mixer", "MIC2", "Mic Bias"},
+       {"Left SPK Mixer", "INA1", "INA1 Input"},
+       {"Left SPK Mixer", "INA2", "INA2 Input"},
+       {"Left SPK Mixer", "INB1", "INB1 Input"},
+       {"Left SPK Mixer", "INB2", "INB2 Input"},
+
+       /* Right speaker output mixer */
+       {"Right SPK Mixer", "Left DAC1", "DACL1"},
+       {"Right SPK Mixer", "Left DAC2", "DACL2"},
+       {"Right SPK Mixer", "Right DAC1", "DACR1"},
+       {"Right SPK Mixer", "Right DAC2", "DACR2"},
+       {"Right SPK Mixer", "MIC1", "Mic Bias"},
+       {"Right SPK Mixer", "MIC2", "Mic Bias"},
+       {"Right SPK Mixer", "INA1", "INA1 Input"},
+       {"Right SPK Mixer", "INA2", "INA2 Input"},
+       {"Right SPK Mixer", "INB1", "INB1 Input"},
+       {"Right SPK Mixer", "INB2", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Left REC Mixer", "Left DAC1", "DACL1"},
+       {"Left REC Mixer", "Left DAC2", "DACL2"},
+       {"Left REC Mixer", "Right DAC1", "DACR1"},
+       {"Left REC Mixer", "Right DAC2", "DACR2"},
+       {"Left REC Mixer", "MIC1", "Mic Bias"},
+       {"Left REC Mixer", "MIC2", "Mic Bias"},
+       {"Left REC Mixer", "INA1", "INA1 Input"},
+       {"Left REC Mixer", "INA2", "INA2 Input"},
+       {"Left REC Mixer", "INB1", "INB1 Input"},
+       {"Left REC Mixer", "INB2", "INB2 Input"},
+
+       /* Earpiece/Receiver output mixer */
+       {"Right REC Mixer", "Left DAC1", "DACL1"},
+       {"Right REC Mixer", "Left DAC2", "DACL2"},
+       {"Right REC Mixer", "Right DAC1", "DACR1"},
+       {"Right REC Mixer", "Right DAC2", "DACR2"},
+       {"Right REC Mixer", "MIC1", "Mic Bias"},
+       {"Right REC Mixer", "MIC2", "Mic Bias"},
+       {"Right REC Mixer", "INA1", "INA1 Input"},
+       {"Right REC Mixer", "INA2", "INA2 Input"},
+       {"Right REC Mixer", "INB1", "INB1 Input"},
+       {"Right REC Mixer", "INB2", "INB2 Input"},
+
+       {"HP Left Out", NULL, "Left HP Mixer"},
+       {"HP Right Out", NULL, "Right HP Mixer"},
+       {"SPK Left Out", NULL, "Left SPK Mixer"},
+       {"SPK Right Out", NULL, "Right SPK Mixer"},
+       {"REC Left Out", NULL, "Left REC Mixer"},
+       {"REC Right Out", NULL, "Right REC Mixer"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RECL", NULL, "REC Left Out"},
+       {"RECR", NULL, "REC Right Out"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "MIC1", "Mic Bias"},
+       {"Left ADC Mixer", "MIC2", "Mic Bias"},
+       {"Left ADC Mixer", "INA1", "INA1 Input"},
+       {"Left ADC Mixer", "INA2", "INA2 Input"},
+       {"Left ADC Mixer", "INB1", "INB1 Input"},
+       {"Left ADC Mixer", "INB2", "INB2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "MIC1", "Mic Bias"},
+       {"Right ADC Mixer", "MIC2", "Mic Bias"},
+       {"Right ADC Mixer", "INA1", "INA1 Input"},
+       {"Right ADC Mixer", "INA2", "INA2 Input"},
+       {"Right ADC Mixer", "INB1", "INB1 Input"},
+       {"Right ADC Mixer", "INB2", "INB2 Input"},
+
+       /* inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+
+       {"INA1 Input", NULL, "INA1"},
+       {"INA2 Input", NULL, "INA2"},
+       {"INB1 Input", NULL, "INB1"},
+       {"INB2 Input", NULL, "INB2"},
+
+       {"Mic Bias", NULL, "MIC1"},
+       {"Mic Bias", NULL, "MIC2"},
+};
+
+/*
+ * Add widgets
+ */
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+                                 ARRAY_SIZE(max98088_dapm_widgets));
+
+       /* set up audio path interconnects */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+/*
+ * Setup DAI1 format
+ */
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       u8 regval;
+
+       cdata = &max98088->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               /* DAI clock master/slave wrt the codec */
+               switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {
+               case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+                       /* MAS: slave */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_MAS, 0);
+                       /* slave mode PLL */
+                       max98088_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                               0x80);
+                       max98088_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM: /* codec clk, frm master */
+                       /* MAS: master */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               0, M98088_DAI_MAS);
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slv, frm mas */
+               case SND_SOC_DAIFMT_CBM_CFS: /* codec clk mas, frm slv */
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;;
+               }
+
+               /* I2S or TDM */
+               if ((fmt & SND_SOC_DAIFMT_I2S) == SND_SOC_DAIFMT_I2S) {
+                       /* TDM: I2S */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_TDM, 0);
+               } else {
+                       /* TDM: PCM/TDM */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               0, M98088_DAI_TDM);
+               }
+
+               /* DAI hardware signal inversions */
+               switch (fmt & SND_SOC_DAIFMT_NB_NF) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_BCI|M98088_DAI_WCI, 0);
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_BCI, M98088_DAI_WCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               M98088_DAI_WCI, M98088_DAI_BCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                               0, M98088_DAI_BCI|M98088_DAI_WCI);
+                       break;
+               }
+
+               regval = (1<<0); /* BSEL : 64*LRCLK (for master mode only) */
+               if (max98088->digmic_mode)
+                       regval |= (1<<6); /* OSR : oversample ratio */
+               max98088_write(codec, M98088_REG_15_DAI1_CLOCK, regval);
+
+               max98088_write(codec, M98088_REG_16_DAI1_IOCFG,
+                       (1<<6) |  /* SEL : map DAI1 to S1 */
+                       (0<<5) |  /* LTEN : ADC->DAC loop-through enable */
+                       (0<<4) |  /* LBEN : loopback (0=disable, 1=enable) */
+                       (0<<3) |  /* DMONO : DAC SDIN (0=stereo, 1=mono) */
+                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
+                       (1<<1) |  /* SDOEN : serial data out ENABLE */
+                       (1<<0));  /* SDIEN : serial data in ENABLE */
+
+               max98088_write(codec, M98088_REG_17_DAI1_TDM,
+                       (0<<6) |  /* SLOTL : L in 1st slot */
+                       (1<<4) |  /* SLOTR : R in 2nd slot */
+                       (0<<0));  /* SLOTDLY : no delay */
+       }
+
+       return 0;
+}
+
+/*
+ * Setup DAI2 format
+ */
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               /* DAI clock master/slave wrt the codec */
+               switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {
+               case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+                       /* MAS: slave */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_MAS, 0);
+                       /* slave mode PLL */
+                       max98088_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                               0x80);
+                       max98088_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                               0x00);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+                       /* MAS: master */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               0, M98088_DAI_MAS);
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slv & frm mas */
+               case SND_SOC_DAIFMT_CBM_CFS: /* codec clk mas & frm slv */
+                       dev_err(codec->dev, "Clock mode unsupported");
+                       return -EINVAL;;
+               }
+
+               /* I2S or TDM */
+               if ((fmt & SND_SOC_DAIFMT_I2S) == SND_SOC_DAIFMT_I2S) {
+                       /* TDM: I2S */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_TDM, 0);
+               } else {
+                       /* TDM: PCM/TDM */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               0, M98088_DAI_TDM);
+               }
+
+               /* DAI hardware signal inversions */
+               switch (fmt & SND_SOC_DAIFMT_NB_NF) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_BCI|M98088_DAI_WCI, 0);
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       /* BCI: normal bclk (rise) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_BCI, M98088_DAI_WCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: normal frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               M98088_DAI_WCI, M98088_DAI_BCI);
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       /* BCI: invert bclk (fall) */
+                       /* WCI: invert frame */
+                       snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                               0, M98088_DAI_BCI|M98088_DAI_WCI);
+                       break;
+               }
+
+               max98088_write(codec, M98088_REG_1D_DAI2_CLOCK,
+                       (1<<0)); /* BSEL: 64*LRCLK (for master mode only) */
+
+               max98088_write(codec, M98088_REG_1E_DAI2_IOCFG,
+                       (2<<6) |  /* SEL : map DAI2 to S2 */
+                       (0<<4) |  /* LBEN : loopback (0=disable) */
+                       (0<<3) |  /* DMONO : DAC SDIN input */
+                       (0<<2) |  /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
+                       (1<<1) |  /* SDOEN : serial data out ENABLE */
+                       (1<<0));  /* SDIEN : serial data in ENABLE */
+
+               max98088_write(codec, M98088_REG_1F_DAI2_TDM,
+                       (0<<6) |  /* SLOTL : L in 1st slot */
+                       (1<<4) |  /* SLOTR : R in 2nd slot */
+                       (0<<0));  /* SLOTDLY : no delay */
+       }
+
+       return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+
+       if (freq != max98088->sysclk) {
+               max98088->sysclk = freq; /* remember current sysclk */
+
+               /* setup clocks for slave mode, and using the PLL
+                * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+                *         0x02 (when master clk is 20MHz to 30MHz)..
+                */
+               if ((freq >= 10000000) && (freq < 20000000)) {
+                       max98088_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+               } else if ((freq >= 20000000) && (freq < 30000000)) {
+                       max98088_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+               } else {
+                       dev_err(codec->dev, "Invalid master clock frequency\n");
+                       return -EINVAL;
+               }
+
+               /* If codec is currently running, toggle reset */
+               if (max98088_read(codec, M98088_REG_51_PWR_SYS)
+                       & M98088_SHDNRUN) {
+                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                               M98088_SHDNRUN, 0);
+                       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+                               0, M98088_SHDNRUN);
+               }
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       max98088->sysclk = freq;
+       return 0;
+}
+
+
+struct rate_table_struct {
+       u32 rate;
+       u8  sr1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct rate_table_struct rate_table[] = {
+       {8000,  0x1},
+       {11025, 0x2},
+       {16000, 0x3},
+       {22050, 0x4},
+       {24000, 0x5},
+       {32000, 0x6},
+       {44100, 0x7},
+       {48000, 0x8},
+       {88200, 0x9},
+       {96000, 0xA},
+};
+
+static inline int rate_index(int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate)
+                       return i;
+       }
+       return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 reg11val;
+       u16 ni;
+
+       cdata = &max98088->dai[0];
+
+       rate = params_rate(params);
+
+       /* data 16/24 bit width */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               /* WS: 16bit */
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               /* WS: 24bit */
+               snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+                       0, M98088_DAI_WS);
+               break;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
+               reg11val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
+               max98088_write(codec, M98088_REG_11_DAI1_CLKMODE, reg11val);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (max98088_read(codec, M98088_REG_14_DAI1_FORMAT)
+               & M98088_DAI_MAS) {
+               BUG_ON(max98088->sysclk == 0);
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               max98088_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               max98088_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       (1<<3), 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+                       0, (1<<3));
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+       unsigned int rate;
+       u8 reg19val;
+       u16 ni;
+
+       cdata = &max98088->dai[1];
+
+       rate = params_rate(params);
+
+       /* data 16/24 bit width */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               /* WS: 16bit */
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       M98088_DAI_WS, 0);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               /* WS: 24bit */
+               snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+                       0, M98088_DAI_WS);
+               break;
+       }
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+       if (rate != cdata->rate) {
+               /* set DAI2 SR2 value for the DSP; FREQ1:0=anyclock */
+               reg19val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
+               max98088_write(codec, M98088_REG_19_DAI2_CLKMODE, reg19val);
+               cdata->rate = rate;
+       }
+
+       /* Configure NI when operating as master */
+       if (max98088_read(codec, M98088_REG_1C_DAI2_FORMAT)
+               & M98088_DAI_MAS) {
+               BUG_ON(max98088->sysclk == 0);
+               ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+                               * (unsigned long long int)rate,
+                               (unsigned long long int)max98088->sysclk);
+               max98088_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+                       (ni >> 8) & 0x7f);
+               max98088_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+                       ni & 0xff);
+       }
+
+       /* Update sample rate mode */
+       if (rate < 50000)
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       (1<<3), 0);
+       else
+               snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+                       0, (1<<3));
+
+       snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+       return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       int i;
+
+       /* write back cached values if they're writeable and
+        * different from the hardware default.
+        */
+       for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+               if (!max98088_access[i].writable)
+                       continue;
+
+               if (max98088->reg_cache[i] == max98088_reg[i])
+                       continue;
+
+               snd_soc_write(codec, i, max98088->reg_cache[i]);
+       }
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               max98088_sync_cache(codec);
+               max98088_write(codec, M98088_REG_51_PWR_SYS,
+                       M98088_SHDNRUN|M98088_PWRSV);
+               break;
+       case SND_SOC_BIAS_OFF:
+               max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai1_set_fmt,
+       .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+       .set_sysclk = max98088_dai_set_sysclk,
+       .set_fmt = max98088_dai2_set_fmt,
+       .hw_params = max98088_dai2_hw_params,
+};
+
+struct snd_soc_dai max98088_dai[] = {
+{
+       .name = "HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+        .ops = &max98088_dai1_ops,
+},
+{
+       .name = "Aux",
+       .playback = {
+               .stream_name = "Aux Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98088_RATES,
+               .formats = MAX98088_FORMATS,
+       },
+       .ops = &max98088_dai2_ops,
+}
+};
+EXPORT_SYMBOL_GPL(max98088_dai);
+
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+       sel = cdata->eq_sel;
+
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq1_cfg[best].name,
+               pdata->eq1_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+       coef_set = &pdata->eq1_cfg[sel];
+
+       m98088_eq_band(codec, 0, 0, coef_set->band1);
+       m98088_eq_band(codec, 0, 1, coef_set->band2);
+       m98088_eq_band(codec, 0, 2, coef_set->band3);
+       m98088_eq_band(codec, 0, 3, coef_set->band4);
+       m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_eq_cfg *coef_set;
+       int best, best_val, save, i, sel, fs;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->eq_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->eq_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+                   abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->eq2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->eq2_cfg[best].name,
+               pdata->eq2_cfg[best].rate, fs);
+
+       /* Disable EQ while configuring, and save current on/off state */
+       save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+       coef_set = &pdata->eq2_cfg[sel];
+
+       m98088_eq_band(codec, 1, 0, coef_set->band1);
+       m98088_eq_band(codec, 1, 1, coef_set->band2);
+       m98088_eq_band(codec, 1, 2, coef_set->band3);
+       m98088_eq_band(codec, 1, 3, coef_set->band4);
+       m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+       /* restore original on/off state */
+       snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+               save);
+}
+
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->eq1_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq1(codec);
+       return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->eq2_cfgcnt)
+               return -EINVAL;
+
+       cdata->eq_sel = sel;
+       max98088_setup_eq2(codec);
+       return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+
+       ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+       return 0;
+}
+
+static void max98088_handle_eq1_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq1control =
+               SOC_ENUM_EXT("EQ1 Mode",
+                       max98088->dai[0].eq_enum,
+                       max98088_get_eq1_enum,
+                       max98088_put_eq1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq1_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->eq_texts[i] = pdata->eq1_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       BUG_ON(&max98088->codec == 0);
+       BUG_ON((max98088->codec).card == 0);
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new eq2control =
+               SOC_ENUM_EXT("EQ2 Mode",
+                       max98088->dai[1].eq_enum,
+                       max98088_get_eq2_enum,
+                       max98088_put_eq2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->eq_textcnt = 0;
+       for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+               for (j = 0; j < cdata->eq_textcnt; j++) {
+                       if (strcmp(pdata->eq2_cfg[i].name,
+                                  cdata->eq_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->eq_textcnt)
+                       continue;
+
+               cdata->eq_texts[i] = pdata->eq2_cfg[i].name;
+               cdata->eq_textcnt++;
+
+               if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+                       dev_err(codec->dev, "Too many EQ config entries\n");
+                       cdata->eq_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+               cdata->eq_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->eq_enum.texts = cdata->eq_texts;
+       cdata->eq_enum.max = cdata->eq_textcnt;
+
+       ret = snd_soc_add_controls(&max98088->codec, &eq2control, 1);
+       if (ret != 0)
+               printk(KERN_ERR "Failed to add EQ control: %d\n", ret);
+}
+
+
+static void max98088_setup_ex1(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       struct max98088_excursion_cfg *coef_set;
+       int best, best_val, i, sel, fs;
+
+       cdata = &max98088->dai[0];
+
+       if (!pdata || !cdata->ex_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->ex_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->ex1_cfgcnt; i++) {
+               if (strcmp(pdata->ex1_cfg[i].name,
+                          cdata->ex_texts[sel]) == 0 &&
+                   abs(pdata->ex1_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->ex1_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->ex1_cfg[best].name,
+               pdata->ex1_cfg[best].rate, fs);
+
+       coef_set = &pdata->ex1_cfg[sel];
+
+       max98088_ex_resp_control(codec, M98088_REG_B6_DAI1_BIQUAD_BASE,
+               coef_set->resp);
+}
+
+static int max98088_put_ex1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[0];
+
+       if (sel >= pdata->ex1_cfgcnt)
+               return -EINVAL;
+
+       cdata->ex_sel = sel;
+       max98088_setup_ex1(codec);
+       return 0;
+}
+
+
+static int max98088_get_ex1_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[0];
+       ucontrol->value.enumerated.item[0] = cdata->ex_sel;
+
+       return 0;
+}
+
+static void max98088_handle_ex1_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new ex1control =
+               SOC_ENUM_EXT("EX1 Limiter Config",
+                            max98088->dai[0].ex_enum,
+                            max98088_get_ex1_enum,
+                            max98088_put_ex1_enum);
+
+       cdata = &max98088->dai[0];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->ex_textcnt = 0;
+       for (i = 0; i < pdata->ex1_cfgcnt; i++) {
+               for (j = 0; j < cdata->ex_textcnt; j++) {
+                       if (strcmp(pdata->ex1_cfg[i].name,
+                                  cdata->ex_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->ex_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->ex_texts[i] = pdata->ex1_cfg[i].name;
+               cdata->ex_textcnt++;
+
+               if (cdata->ex_textcnt >= EX_CFG_MAX) {
+                       dev_err(codec->dev, "Too many limiter configs\n");
+                       cdata->ex_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EX1 configurations\n",
+               cdata->ex_textcnt);
+
+       /* now point the soc_enum to .texts array */
+       cdata->ex_enum.texts = cdata->ex_texts;
+       cdata->ex_enum.max = cdata->ex_textcnt;
+       ret = snd_soc_add_controls(&max98088->codec, &ex1control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add limiter control\n");
+}
+
+
+static void max98088_setup_ex2(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       struct max98088_excursion_cfg *coef_set;
+       int best, best_val, i, sel, fs;
+
+       cdata = &max98088->dai[1];
+
+       if (!pdata || !cdata->ex_textcnt)
+               return;
+
+       /* Find the selected configuration with nearest sample rate */
+       fs = cdata->rate;
+
+       sel = cdata->ex_sel;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->ex2_cfgcnt; i++) {
+               if (strcmp(pdata->ex2_cfg[i].name,
+                          cdata->ex_texts[sel]) == 0 &&
+                   abs(pdata->ex2_cfg[i].rate - fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->ex2_cfg[i].rate - fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+               pdata->ex2_cfg[best].name,
+               pdata->ex2_cfg[best].rate, fs);
+
+       coef_set = &pdata->ex2_cfg[sel];
+
+       max98088_ex_resp_control(codec, M98088_REG_C0_DAI2_BIQUAD_BASE,
+               coef_set->resp);
+}
+
+static int max98088_put_ex2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int sel = ucontrol->value.integer.value[0];
+
+       cdata = &max98088->dai[1];
+
+       if (sel >= pdata->ex2_cfgcnt)
+               return -EINVAL;
+
+       cdata->ex_sel = sel;
+       max98088_setup_ex2(codec);
+       return 0;
+}
+
+static int max98088_get_ex2_enum(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98088_priv *max98088 = codec->private_data;
+       struct max98088_cdata *cdata;
+
+       cdata = &max98088->dai[1];
+       ucontrol->value.enumerated.item[0] = cdata->ex_sel;
+
+       return 0;
+}
+
+static void max98088_handle_ex2_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       struct max98088_cdata *cdata;
+       int ret, i, j;
+
+       struct snd_kcontrol_new ex2control =
+               SOC_ENUM_EXT("EX2 Limiter Config",
+                            max98088->dai[1].ex_enum,
+                            max98088_get_ex2_enum,
+                            max98088_put_ex2_enum);
+
+       cdata = &max98088->dai[1];
+
+       /* Build an array of texts for the enum API. The number
+        * of texts is likely fewer than the number of configurations
+        * due to multiple sample rates for the same text name. */
+       cdata->ex_textcnt = 0;
+       for (i = 0; i < pdata->ex2_cfgcnt; i++) {
+               for (j = 0; j < cdata->ex_textcnt; j++) {
+                       if (strcmp(pdata->ex2_cfg[i].name,
+                                  cdata->ex_texts[j]) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != cdata->ex_textcnt)
+                       continue;
+
+               /* ...and remember the new version. */
+               cdata->ex_texts[i] = pdata->ex2_cfg[i].name;
+               cdata->ex_textcnt++;
+
+               if (cdata->ex_textcnt >= EX_CFG_MAX) {
+                       dev_err(codec->dev, "Too many limiter configs\n");
+                       cdata->ex_textcnt--;
+                       break;
+               }
+       }
+
+       dev_dbg(codec->dev, "Installed %d EX2 configurations\n",
+               cdata->ex_textcnt);
+
+       /* now point the soc_enum to .texts array items */
+       cdata->ex_enum.texts = cdata->ex_texts;
+       cdata->ex_enum.max = cdata->ex_textcnt;
+
+       ret = snd_soc_add_controls(&max98088->codec, &ex2control, 1);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add limiter control\n");
+}
+
+static void max98088_handle_pdata(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_pdata *pdata = max98088->pdata;
+       u8 regval;
+
+       if (!pdata)
+               return;
+
+       /* Configure digital mic / external mic */
+       /* Digital mic needs REG_15 OSR1=1 */
+       regval = ((0<<6) | /* digital mic clock freq = PCLK/8 */
+                 (0<<5) | /* DIGMICL enable */
+                 (0<<4) | /* DIGMICR enable */
+                 (0<<0)); /* external mic enable */
+
+       if (pdata->digmic_left_enable)
+               regval |= (1<<5); /* digital left mic enabled */
+
+       if (pdata->digmic_right_enable)
+               regval |= (1<<4); /* digital right mic enabled */
+
+       max98088->digmic_mode = (regval ? 1 : 0);
+
+       if (pdata->extmic_mode < 3)
+               regval |= (pdata->extmic_mode << 0); /* external mic enabled */
+
+       max98088_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+       /* configure external mic to mic1 bypass, mic1 to mic2 bypass */
+       regval = 0;
+
+       if (pdata->ina_to_mic1_bypass)
+               regval |= (1<<7);
+
+       if (pdata->mic1_to_mic2_bypass)
+               regval |= (1<<4);
+
+       max98088_write(codec, M98088_REG_4A_CFG_BYPASS, regval);
+
+       /* configure receiver output */
+       regval = ((pdata->receiver_mode) ? (1<<7) : 0);
+       snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+               (1<<7), regval);
+
+       /* configure equalizers */
+       if (pdata->eq1_cfgcnt)
+               max98088_handle_eq1_pdata(max98088);
+
+       if (pdata->eq2_cfgcnt)
+               max98088_handle_eq2_pdata(max98088);
+
+       /* configure excursion limiters */
+       if (pdata->ex1_cfgcnt)
+               max98088_handle_ex1_pdata(max98088);
+
+       if (pdata->ex2_cfgcnt)
+               max98088_handle_ex2_pdata(max98088);
+}
+
+static int max98088_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max98088_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 *cache = codec->reg_cache;
+
+       /* toggle reset the shutdown bit of the codec hardware */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < M98088_REG_CNT; i++) {
+               if (i == M98088_REG_51_PWR_SYS)
+                       continue;
+
+               if (!max98088_access[i].writable)
+                       continue;
+
+               max98088_hw_write(codec, i, cache[i]);
+       }
+
+       /* now enter into the resume mode bias level */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+
+/*
+ * Make sure that a max98088 is attached to the I2C bus.
+ */
+static int max98088_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+       int ret = 0;
+
+       dev_printk(KERN_INFO, &pdev->dev, "MAX98088 Audio CODEC\n");
+
+       if (!max98088_codec) {
+               dev_err(&pdev->dev, "Codec device is not registered\n");
+               return -EINVAL;
+       }
+
+       socdev->card->codec = max98088_codec;
+       codec = max98088_codec;
+       max98088 = codec->private_data;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, max98088_snd_controls,
+                            ARRAY_SIZE(max98088_snd_controls));
+
+       /* install additional amixer controls: EQ and excursion limiter */
+       if (max98088->pdata)
+               max98088_handle_pdata(max98088);
+       else
+               dev_err(&pdev->dev, "No platform data\n");
+
+       max98088_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register card\n");
+               goto card_err;
+       }
+
+       return 0;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+pcm_err:
+       return ret;
+}
+
+static int max98088_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       if (codec->control_data)
+               max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+/* expose to machine driver */
+struct snd_soc_codec_device soc_codec_dev_max98088 = {
+       .probe   = max98088_probe,
+       .remove  = max98088_remove,
+       .suspend = max98088_suspend,
+       .resume  = max98088_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98088);
+
+
+static int max98088_register(struct max98088_priv *max98088,
+                            enum snd_soc_control_type control)
+
+{
+       int ret, i, version;
+       struct snd_soc_codec *codec = &max98088->codec;
+       struct max98088_cdata *cdata;
+
+       if (max98088_codec) {
+               dev_err(codec->dev, "Another MAX98088 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->name = "MAX98088";
+       codec->owner = THIS_MODULE;
+       codec->read = max98088_read;
+       codec->write = max98088_write;
+
+       /* setup DAPM event bias level and handler function */
+       codec->bias_level = SND_SOC_BIAS_STANDBY;
+       codec->set_bias_level = max98088_set_bias_level;
+
+       codec->dai = max98088_dai;
+       codec->num_dai = ARRAY_SIZE(max98088_dai);
+       codec->private_data = max98088;
+
+       codec->reg_cache_size = M98088_REG_CNT;
+       codec->reg_cache = &max98088->reg_cache;
+       codec->volatile_register = max98088_volatile_register;
+
+       memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+       version = max98088_read(codec, M98088_REG_FF_REV_ID);
+       dev_dbg(codec->dev, "MAX98088 revision 0x%02X\n", version);
+
+       /* initalize private data */
+
+       max98088->sysclk = (unsigned)-1;
+
+       cdata = &max98088->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+       cdata->ex_textcnt = 0;
+       cdata->ex_sel = 0;
+
+       cdata = &max98088->dai[1];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+       cdata->eq_textcnt = 0;
+       cdata->eq_sel = 0;
+       cdata->ex_textcnt = 0;
+       cdata->ex_sel = 0;
+
+       max98088_write(codec, M98088_REG_14_DAI1_FORMAT, M98088_DAI_DLY);
+       max98088_write(codec, M98088_REG_1C_DAI2_FORMAT, M98088_DAI_DLY);
+
+       max98088->power_state = 0; /* INA INB power enable state */
+       max98088->ex_mode = 0; /* excursion limiter mode */
+       max98088->digmic_mode = 0; /* 0=normal, 1=digital mic used */
+
+       /* disable device via dapm interface */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* initialize registers cache to hardware default */
+       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       max98088_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+       max98088_write(codec, M98088_REG_22_MIX_DAC,
+               M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+               M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+       max98088_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+       max98088_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+       /* power on device */
+       max98088_codec = codec;
+       for (i = 0; i < codec->num_dai; i++)
+               max98088_dai[i].dev = codec->dev;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dais(&max98088_dai[0], codec->num_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+               goto err_codec;
+       }
+
+       return ret;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(max98088);
+
+       return ret;
+}
+
+static void max98088_unregister(struct max98088_priv *max98088)
+{
+       struct snd_soc_codec *codec = &max98088->codec;
+       snd_soc_unregister_dais(max98088_dai, codec->num_dai);
+       snd_soc_unregister_codec(codec);
+       kfree(max98088);
+       max98088_codec = NULL;
+}
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct snd_soc_codec *codec;
+       struct max98088_priv *max98088;
+
+       max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+       if (max98088 == NULL)
+               return -ENOMEM;
+
+       /* codec structure is inside the private data */
+       codec = &(max98088->codec);
+
+       codec->private_data = max98088;
+
+       codec->hw_write = (hw_write_t) i2c_master_send;
+       i2c_set_clientdata(i2c, max98088);
+       codec->control_data = i2c;
+
+       max98088->pdata = i2c->dev.platform_data;
+
+       codec->dev = &i2c->dev;
+
+       return max98088_register(max98088, SND_SOC_I2C);
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+       struct max98088_priv *max98088 = i2c_get_clientdata(client);
+       max98088_unregister(max98088);
+       return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+       { "max98088", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+
+static struct i2c_driver max98088_i2c_driver = {
+       .driver = {
+               .name = "MAX98088 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = __devexit_p(max98088_i2c_remove),
+       .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&max98088_i2c_driver);
+       if (ret)
+               pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+       return ret;
+}
+
+static void __exit max98088_exit(void)
+{
+       i2c_del_driver(&max98088_i2c_driver);
+}
+
+module_init(max98088_init);
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang <peter.hsiang@maxim-ic.com>");
+MODULE_AUTHOR("Jesse Marroquin <jesse.marroquin@maxim-ic.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..f7ef4c2
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,161 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS            0x00
+#define M98088_REG_01_MIC_STATUS            0x01
+#define M98088_REG_02_JACK_STAUS            0x02
+#define M98088_REG_03_BATTERY_VOLTAGE       0x03
+#define M98088_REG_0F_IRQ_ENABLE            0x0F
+#define M98088_REG_10_SYS_CLK               0x10
+#define M98088_REG_11_DAI1_CLKMODE          0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI        0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO        0x13
+#define M98088_REG_14_DAI1_FORMAT           0x14
+#define M98088_REG_15_DAI1_CLOCK            0x15
+#define M98088_REG_16_DAI1_IOCFG            0x16
+#define M98088_REG_17_DAI1_TDM              0x17
+#define M98088_REG_18_DAI1_FILTERS          0x18
+#define M98088_REG_19_DAI2_CLKMODE          0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI        0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO        0x1B
+#define M98088_REG_1C_DAI2_FORMAT           0x1C
+#define M98088_REG_1D_DAI2_CLOCK            0x1D
+#define M98088_REG_1E_DAI2_IOCFG            0x1E
+#define M98088_REG_1F_DAI2_TDM              0x1F
+#define M98088_REG_20_DAI2_FILTERS          0x20
+#define M98088_REG_21_SRC                   0x21
+#define M98088_REG_22_MIX_DAC               0x22
+#define M98088_REG_23_MIX_ADC_LEFT          0x23
+#define M98088_REG_24_MIX_ADC_RIGHT         0x24
+#define M98088_REG_25_MIX_HP_LEFT           0x25
+#define M98088_REG_26_MIX_HP_RIGHT          0x26
+#define M98088_REG_27_MIX_HP_CNTL           0x27
+#define M98088_REG_28_MIX_REC_LEFT          0x28
+#define M98088_REG_29_MIX_REC_RIGHT         0x29
+#define M98088_REG_2A_MIC_REC_CNTL          0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT          0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT         0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL          0x2D
+#define M98088_REG_2E_LVL_SIDETONE          0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY         0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ      0x30
+#define M98088_REG_31_LVL_DAI2_PLAY         0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ      0x32
+#define M98088_REG_33_LVL_ADC_L             0x33
+#define M98088_REG_34_LVL_ADC_R             0x34
+#define M98088_REG_35_LVL_MIC1              0x35
+#define M98088_REG_36_LVL_MIC2              0x36
+#define M98088_REG_37_LVL_INA               0x37
+#define M98088_REG_38_LVL_INB               0x38
+#define M98088_REG_39_LVL_HP_L              0x39
+#define M98088_REG_3A_LVL_HP_R              0x3A
+#define M98088_REG_3B_LVL_REC_L             0x3B
+#define M98088_REG_3C_LVL_REC_R             0x3C
+#define M98088_REG_3D_LVL_SPK_L             0x3D
+#define M98088_REG_3E_LVL_SPK_R             0x3E
+#define M98088_REG_3F_MICAGC_CFG            0x3F
+#define M98088_REG_40_MICAGC_THRESH         0x40
+#define M98088_REG_41_SPKDHP                0x41
+#define M98088_REG_42_SPKDHP_THRESH         0x42
+#define M98088_REG_43_SPKALC_COMP           0x43
+#define M98088_REG_44_PWRLMT_CFG            0x44
+#define M98088_REG_45_PWRLMT_TIME           0x45
+#define M98088_REG_46_THDLMT_CFG            0x46
+#define M98088_REG_47_CFG_AUDIO_IN          0x47
+#define M98088_REG_48_CFG_MIC               0x48
+#define M98088_REG_49_CFG_LEVEL             0x49
+#define M98088_REG_4A_CFG_BYPASS            0x4A
+#define M98088_REG_4B_CFG_JACKDET           0x4B
+#define M98088_REG_4C_PWR_EN_IN             0x4C
+#define M98088_REG_4D_PWR_EN_OUT            0x4D
+#define M98088_REG_4E_BIAS_CNTL             0x4E
+#define M98088_REG_4F_DAC_BIAS1             0x4F
+#define M98088_REG_50_DAC_BIAS2             0x50
+#define M98088_REG_51_PWR_SYS               0x51
+#define M98088_REG_52_DAI1_EQ_BASE          0x52
+#define M98088_REG_84_DAI2_EQ_BASE          0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE      0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE      0xC0
+#define M98088_REG_FF_REV_ID                0xFF
+
+#define M98088_REG_CNT                      (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+       #define M98088_DAI_MAS                  (1<<7)
+       #define M98088_DAI_WCI                  (1<<6)
+       #define M98088_DAI_BCI                  (1<<5)
+       #define M98088_DAI_DLY                  (1<<4)
+       #define M98088_DAI_TDM                  (1<<2)
+       #define M98088_DAI_FSW                  (1<<1)
+       #define M98088_DAI_WS                   (1<<0)
+
+/* M98088_REG_22_MIX_DAC */
+       #define M98088_DAI1L_TO_DACL            (1<<7)
+       #define M98088_DAI1R_TO_DACL            (1<<6)
+       #define M98088_DAI2L_TO_DACL            (1<<5)
+       #define M98088_DAI2R_TO_DACL            (1<<4)
+       #define M98088_DAI1L_TO_DACR            (1<<3)
+       #define M98088_DAI1R_TO_DACR            (1<<2)
+       #define M98088_DAI2L_TO_DACR            (1<<1)
+       #define M98088_DAI2R_TO_DACR            (1<<0)
+
+/* M98088_REG_3A_LVL_HEADPHONE_R */
+       #define M98088_HP_MUTE                  (1<<7)
+
+/* M98088_REG_3C_LVL_RECEIVER_R */
+       #define M98088_REC_MUTE                 (1<<7)
+
+/* M98088_REG_3E_LVL_SPEAKER_R */
+       #define M98088_SP_MUTE                  (1<<7)
+
+/* M98088_REG_49_CFG_LEVEL */
+       #define M98088_VSEN                     (1<<6)
+       #define M98088_ZDEN                     (1<<5)
+       #define M98088_EQ2EN                    (1<<1)
+       #define M98088_EQ1EN                    (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+       #define M98088_HPLEN                    (1<<7)
+       #define M98088_HPREN                    (1<<6)
+       #define M98088_HPEN                     ((1<<7)|(1<<6))
+       #define M98088_SPLEN                    (1<<5)
+       #define M98088_SPREN                    (1<<4)
+       #define M98088_RECEN                    (1<<3)
+       #define M98088_DALEN                    (1<<1)
+       #define M98088_DAREN                    (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+       #define M98088_SHDNRUN                  (1<<7)
+       #define M98088_PERFMODE                 (1<<3)
+       #define M98088_HPPLYBACK                (1<<2)
+       #define M98088_PWRSV8K                  (1<<1)
+       #define M98088_PWRSV                    (1<<0)
+
+#define M98088_COEFS_PER_BAND               5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+struct max98088_setup_data {
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai max98088_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_max98088;
+
+#endif
--
1.6.3.3

^ permalink raw reply related	[flat|nested] 81+ messages in thread

end of thread, other threads:[~2010-10-15 17:22 UTC | newest]

Thread overview: 81+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-29  2:34 [PATCH] ASoC: Add max98088 CODEC driver Peter Hsiang
2010-09-29  2:34 ` Peter Hsiang
2010-09-29  3:37 ` Mark Brown
2010-09-29  3:37   ` Mark Brown
2010-09-29 21:42   ` Peter Hsiang
2010-09-29 21:42     ` Peter Hsiang
2010-09-29 22:18     ` Mark Brown
2010-09-29 22:18       ` Mark Brown
2010-09-30  0:52       ` Peter Hsiang
2010-09-30  0:58         ` Mark Brown
2010-09-30  0:58           ` Mark Brown
2010-09-30  1:20           ` Peter Hsiang
2010-09-30 17:23           ` user space control app driver interface for sound soc Peter Hsiang
2010-09-30 20:31             ` Mark Brown
2010-09-30 21:55               ` Peter Hsiang
2010-09-30 22:09                 ` Mark Brown
2010-09-30 23:10                   ` Peter Hsiang
2010-09-30 23:34                     ` Mark Brown
2010-10-01  1:56                       ` Peter Hsiang
2010-10-01  2:37                         ` Mark Brown
2010-10-01  6:56                           ` Clemens Ladisch
2010-10-01  7:12                             ` Mark Brown
2010-10-01 13:42                               ` Takashi Iwai
2010-10-01 17:35                                 ` Mark Brown
2010-10-01 21:57                                 ` Peter Hsiang
2010-10-03  9:09                                   ` Takashi Iwai
2010-10-13  1:20 ` [PATCH] ASoC: Add max98088 CODEC driver Peter Hsiang
2010-10-13  1:47   ` Joe Perches
2010-10-13  8:24     ` Mark Brown
2010-10-13  8:24       ` Mark Brown
2010-10-13 12:10       ` [PATCH] sound/soc: rename vol to volatile_register as appropriate Joe Perches
2010-10-13 12:33         ` Mark Brown
2010-10-13 12:33           ` Mark Brown
2010-10-13 12:55           ` Joe Perches
2010-10-13 15:11             ` Mark Brown
2010-10-13 15:11               ` Mark Brown
2010-10-13 15:27               ` Joe Perches
2010-10-13 15:29                 ` Mark Brown
2010-10-13 15:29                   ` Mark Brown
2010-10-13 15:35                   ` Joe Perches
2010-10-13 19:10                   ` [RFC PATCH] sound/soc/codecs/wm8962.c: Use register index, save 100kb text Joe Perches
2010-10-13 19:40                     ` Mark Brown
2010-10-13 19:40                       ` Mark Brown
2010-10-13 20:06                       ` Joe Perches
2010-10-13 20:29                         ` Mark Brown
2010-10-13 20:29                           ` Mark Brown
2010-10-13 15:19           ` [PATCH] sound/soc/codecs/wm8994.c: Remove unused vol Joe Perches
2010-10-15 10:08             ` Liam Girdwood
2010-10-15 10:08               ` Liam Girdwood
2010-10-15 10:39             ` Mark Brown
2010-10-15 10:39               ` Mark Brown
2010-10-13 10:32   ` [PATCH] ASoC: Add max98088 CODEC driver Mark Brown
2010-10-13 10:32     ` Mark Brown
2010-10-14  3:18     ` Peter Hsiang
2010-10-14  3:18       ` Peter Hsiang
2010-10-14  3:30   ` Peter Hsiang
2010-10-15 10:04     ` Liam Girdwood
2010-10-15 10:04       ` Liam Girdwood
2010-10-15 10:55     ` Mark Brown
2010-10-15 10:55       ` Mark Brown
2010-10-15 17:23       ` Peter Hsiang
2010-10-15 17:23         ` Peter Hsiang
  -- strict thread matches above, loose matches on Subject: below --
2010-09-23  2:58 Peter Hsiang
2010-09-23  2:58 ` Peter Hsiang
2010-09-23 12:04 ` Mark Brown
2010-09-23 12:04   ` Mark Brown
2010-09-23 17:56   ` Peter Hsiang
2010-09-23 17:56     ` Peter Hsiang
2010-09-23 18:38     ` Mark Brown
2010-09-23 18:38       ` Mark Brown
2010-08-31 21:08 Peter Hsiang
2010-08-31 21:08 Peter Hsiang
2010-09-01 11:14 ` Mark Brown
2010-09-01 11:14   ` Mark Brown
2010-09-02 23:30   ` Peter Hsiang
2010-09-03 10:17     ` Mark Brown
2010-09-03 10:17       ` Mark Brown
2010-09-22  2:49       ` Peter Hsiang
2010-09-22  2:49         ` Peter Hsiang
2010-09-22 10:38         ` Mark Brown
2010-09-22 10:38           ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.