summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorInha Song <ideal.song@samsung.com>2014-11-10 13:40:23 +0900
committerSeung-Woo Kim <sw0312.kim@samsung.com>2016-12-14 13:40:50 +0900
commitadf20b9c4ff6093f4aa1a51cae5e3ef34728f2d6 (patch)
tree78a750a22597320de3554de74f872851507e2d09 /sound
parent33a8621763a7a9baa302d9632f22689f4117c9eb (diff)
LOCAL / ASoC: max98504A: Add max98504A Speaker amplifier driver
This patch add MAX98504A Class D Speaker Amplifier driver. The MAX98504A is a high efficiency mono Class D audio amplifier that features an integrated boost converter with voltage and current sensing ADCs for dynamic speaker management solutions. Signed-off-by: Inha Song <ideal.song@samsung.com> [k.kozlowski: rebased on 4.1] Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/max98504a.c655
-rw-r--r--sound/soc/codecs/max98504a.h590
4 files changed, 1251 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061c46587628..3b5b2b4434a4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -465,6 +465,10 @@ config SND_SOC_MAX98357A
config SND_SOC_MAX98925
tristate
+config SND_SOC_MAX98504A
+ tristate "Support MAX98504A Amplifier"
+ depends on I2C
+
config SND_SOC_MAX9850
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index abe2d7edf65c..667b461bdaf2 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -178,6 +178,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
+snd-soc-max98504a-objs := max98504a.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
@@ -360,4 +361,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_MAX98504A) += snd-soc-max98504a.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
diff --git a/sound/soc/codecs/max98504a.c b/sound/soc/codecs/max98504a.c
new file mode 100644
index 000000000000..6e963c12318c
--- /dev/null
+++ b/sound/soc/codecs/max98504a.c
@@ -0,0 +1,655 @@
+/*
+* max98504.c -- MAX98504 SoC Audio driver
+*
+* Copyright 2013-2014 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/init.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <sound/tlv.h>
+#include <sound/tlv.h>
+#include <sound/max98504a.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+
+#include "max98504a.h"
+#ifdef CONFIG_SND_SOC_MAXIM_DSM_A
+#include <sound/maxim_dsm_a.h>
+#endif
+
+#ifdef DEBUG_MAX98504
+#define msg_maxim(format, args...) \
+ printk(KERN_INFO "[MAX98504_DEBUG] %s " format, __func__, ## args)
+#else
+#define msg_maxim(format, args...)
+#endif
+
+static struct regmap *regmap;
+
+static DEFINE_MUTEX(max98504_lock);
+
+static struct reg_default max98504_reg[] = {
+ {MAX98504_REG_01_INTERRUPT_STATUS, 0x00},
+ {MAX98504_REG_02_INTERRUPT_FLAGS, 0x00},
+ {MAX98504_REG_03_INTERRUPT_ENABLES, 0x00},
+ {MAX98504_REG_04_INTERRUPT_FLAG_CLEARS, 0x00},
+ {MAX98504_REG_10_GPIO_ENABLE, 0x00},
+ {MAX98504_REG_11_GPIO_CONFIG, 0x00},
+ {MAX98504_REG_12_WATCHDOG_ENABLE, 0x00},
+ {MAX98504_REG_13_WATCHDOG_CONFIG, 0x00},
+ {MAX98504_REG_14_WATCHDOG_CLEAR, 0x00},
+ {MAX98504_REG_15_CLOCK_MONITOR_ENABLE, 0x00},
+ {MAX98504_REG_16_PVDD_BROWNOUT_ENABLE, 0x00},
+ {MAX98504_REG_17_PVDD_BROWNOUT_CONFIG_1, 0x00},
+ {MAX98504_REG_18_PVDD_BROWNOUT_CONFIG_2, 0x00},
+ {MAX98504_REG_19_PVDD_BROWNOUT_CONFIG_3, 0x00},
+ {MAX98504_REG_1A_PVDD_BROWNOUT_CONFIG_4, 0x00},
+ {MAX98504_REG_20_PCM_RX_ENABLES, 0x00},
+ {MAX98504_REG_21_PCM_TX_ENABLES, 0x00},
+ {MAX98504_REG_22_PCM_TX_HIZ_CONTROL, 0x00},
+ {MAX98504_REG_23_PCM_TX_CHANNEL_SOURCES, 0x00},
+ {MAX98504_REG_24_PCM_MODE_CONFIG, 0x00},
+ {MAX98504_REG_25_PCM_DSP_CONFIG, 0x00},
+ {MAX98504_REG_26_PCM_CLOCK_SETUP, 0x00},
+ {MAX98504_REG_27_PCM_SAMPLE_RATE_SETUP, 0x00},
+ {MAX98504_REG_28_PCM_TO_SPEAKER_MONOMIX, 0x00},
+ {MAX98504_REG_30_PDM_TX_ENABLES, 0x00},
+ {MAX98504_REG_31_PDM_TX_HIZ_CONTROL, 0x00},
+ {MAX98504_REG_32_PDM_TX_CONTROL, 0x00},
+ {MAX98504_REG_33_PDM_RX_ENABLE, 0x00},
+ {MAX98504_REG_34_SPEAKER_ENABLE, 0x00},
+ {MAX98504_REG_35_SPEAKER_SOURCE_SELECT, 0x00},
+ {MAX98504_REG_36_MEASUREMENT_ENABLES, 0x00},
+ {MAX98504_REG_37_ANALOGUE_INPUT_GAIN, 0x00},
+ {MAX98504_REG_38_TEMPERATURE_LIMIT_CONFIG, 0x00},
+ {MAX98504_REG_39_ANALOGUE_SPARE, 0x00},
+ {MAX98504_REG_40_GLOBAL_ENABLE, 0x00},
+ {MAX98504_REG_41_SOFTWARE_RESET, 0x00},
+};
+
+static struct {
+ u8 read;
+ u8 write;
+ u8 vol;
+} max98504_reg_access[MAX98504_REG_CNT] = {
+ [MAX98504_REG_01_INTERRUPT_STATUS] = { 0xFF, 0x00, 0xFF },
+ [MAX98504_REG_02_INTERRUPT_FLAGS] = { 0xFF, 0x00, 0xFF },
+ [MAX98504_REG_03_INTERRUPT_ENABLES] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_04_INTERRUPT_FLAG_CLEARS] = { 0x00, 0xFF, 0xFF },
+ [MAX98504_REG_10_GPIO_ENABLE] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_11_GPIO_CONFIG] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_12_WATCHDOG_ENABLE] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_13_WATCHDOG_CONFIG] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_14_WATCHDOG_CLEAR] = { 0x00, 0xFF, 0xFF },
+ [MAX98504_REG_15_CLOCK_MONITOR_ENABLE] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_16_PVDD_BROWNOUT_ENABLE] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_17_PVDD_BROWNOUT_CONFIG_1] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_18_PVDD_BROWNOUT_CONFIG_2] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_19_PVDD_BROWNOUT_CONFIG_3] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_1A_PVDD_BROWNOUT_CONFIG_4] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_20_PCM_RX_ENABLES] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_21_PCM_TX_ENABLES] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_22_PCM_TX_HIZ_CONTROL] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_23_PCM_TX_CHANNEL_SOURCES] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_24_PCM_MODE_CONFIG] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_25_PCM_DSP_CONFIG] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_26_PCM_CLOCK_SETUP] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_27_PCM_SAMPLE_RATE_SETUP] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_28_PCM_TO_SPEAKER_MONOMIX] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_30_PDM_TX_ENABLES] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_31_PDM_TX_HIZ_CONTROL] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_32_PDM_TX_CONTROL] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_33_PDM_RX_ENABLE] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_34_SPEAKER_ENABLE] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_35_SPEAKER_SOURCE_SELECT] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_36_MEASUREMENT_ENABLES] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_37_ANALOGUE_INPUT_GAIN] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_38_TEMPERATURE_LIMIT_CONFIG] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_39_ANALOGUE_SPARE] = { 0xFF, 0xFF, 0x00 },
+ [MAX98504_REG_40_GLOBAL_ENABLE] = { 0xFF, 0xFF, 0xFF },
+ [MAX98504_REG_41_SOFTWARE_RESET] = { 0x00, 0xFF, 0xFF },
+};
+
+static bool max98504_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (max98504_reg_access[reg].vol)
+ return 1;
+ else
+ return 0;
+}
+
+static bool max98504_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg >= MAX98504_REG_CNT)
+ return 0;
+ return max98504_reg_access[reg].read != 0;
+}
+
+static int max98504_reset(struct max98504_priv *max98504)
+{
+ int ret;
+
+ ret = regmap_write(max98504->regmap,
+ MAX98504_REG_41_SOFTWARE_RESET, M98504_SOFTWARE_RESET_MASK);
+ msleep(20);
+
+ return ret;
+}
+
+#ifdef CONFIG_SND_SOC_MAXIM_DSM_A
+#define DEFAULT_LOG_CLASS_NAME "dsm"
+static ssize_t max98504_log_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return maxdsm_log_prepare(buf);
+}
+
+static DEVICE_ATTR(dsm_log, S_IRUGO, max98504_log_show, NULL);
+static struct attribute *max98504_attributes[] = {
+ &dev_attr_dsm_log.attr,
+ NULL
+};
+
+static struct attribute_group max98504_attribute_group = {
+ .attrs = max98504_attributes
+};
+#endif
+
+static int max98504_probe(struct max98504_priv *max98504)
+{
+ struct max98504_pdata *pdata = max98504->pdata;
+ struct max98504_cfg_data *cfg_data = &pdata->cfg_data;
+
+ u8 regval;
+ int ret;
+ unsigned int value;
+
+ msg_maxim("\n");
+
+ max98504_reset(max98504);
+
+ if (!pdata) {
+ pr_err("No platform data\n");
+ return ret;
+ }
+ /* Configure Rx Mode */
+ if (pdata->rx_mode == MODE_RX_PCM) {
+ regval = 0;
+ if (cfg_data->rx_dither_en)
+ regval |= M98504_PCM_DSP_CFG_RX_DITH_EN_MASK;
+ if (cfg_data->rx_flt_mode)
+ regval |= M98504_PCM_DSP_CFG_RX_FLT_MODE_MASK;
+
+ regmap_update_bits(max98504->regmap,
+ MAX98504_REG_25_PCM_DSP_CONFIG,
+ M98504_PCM_DSP_CFG_RX_DITH_EN_MASK|\
+ M98504_PCM_DSP_CFG_RX_FLT_MODE_MASK,
+ regval);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_20_PCM_RX_ENABLES, (u8)cfg_data->rx_ch_en);
+ } else if (pdata->rx_mode == MODE_RX_PDM0 || \
+ pdata->rx_mode == MODE_RX_PDM1) {
+ regmap_write(max98504->regmap,
+ MAX98504_REG_33_PDM_RX_ENABLE, M98504_PDM_RX_EN_MASK);
+ } else {
+ regmap_write(max98504->regmap,
+ MAX98504_REG_20_PCM_RX_ENABLES, 0);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_33_PDM_RX_ENABLE, 0);
+ }
+
+ regmap_write(max98504->regmap,
+ MAX98504_REG_35_SPEAKER_SOURCE_SELECT,
+ (u8) (M98504_SPK_SRC_SEL_MASK & pdata->rx_mode));
+
+ /* Configure Tx Mode */
+ if (pdata->tx_mode == MODE_TX_PCM) {
+ regval = 0;
+ if (cfg_data->tx_dither_en)
+ regval |= M98504_PCM_DSP_CFG_TX_DITH_EN_MASK;
+ if (cfg_data->meas_dc_block_en)
+ regval |= M98504_PCM_DSP_CFG_MEAS_DCBLK_EN_MASK;
+ regmap_update_bits(max98504->regmap,
+ MAX98504_REG_25_PCM_DSP_CONFIG,
+ M98504_PCM_DSP_CFG_TX_DITH_EN_MASK|\
+ M98504_PCM_DSP_CFG_MEAS_DCBLK_EN_MASK, regval);
+
+ regmap_write(max98504->regmap,
+ MAX98504_REG_21_PCM_TX_ENABLES, (u8)cfg_data->tx_ch_en);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_22_PCM_TX_HIZ_CONTROL,
+ (u8)cfg_data->tx_hiz_ch_en);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_23_PCM_TX_CHANNEL_SOURCES,
+ (u8)cfg_data->tx_ch_src);
+ } else {
+ regmap_write(max98504->regmap,
+ MAX98504_REG_30_PDM_TX_ENABLES, (u8)cfg_data->tx_ch_en);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_31_PDM_TX_HIZ_CONTROL,
+ (u8)cfg_data->tx_hiz_ch_en);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_32_PDM_TX_CONTROL,
+ (u8)cfg_data->tx_ch_src);
+ }
+
+ regmap_write(max98504->regmap,
+ MAX98504_REG_36_MEASUREMENT_ENABLES,
+ M98504_MEAS_I_EN_MASK | M98504_MEAS_V_EN_MASK);
+
+ /* Brownout Protection */
+ regmap_write(max98504->regmap,
+ MAX98504_REG_16_PVDD_BROWNOUT_ENABLE, 0x1);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_17_PVDD_BROWNOUT_CONFIG_1, 0x33);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_18_PVDD_BROWNOUT_CONFIG_2, 0x0a);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_19_PVDD_BROWNOUT_CONFIG_3, 0xff);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_1A_PVDD_BROWNOUT_CONFIG_4, 0xff);
+
+ #ifdef USE_MAX98504_IRQ
+ if (gpio_is_valid(pdata->irq)) {
+ regmap_write(max98504->regmap,
+ MAX98504_REG_03_INTERRUPT_ENABLES, 0xff);
+ regmap_write(max98504->regmap,
+ MAX98504_REG_10_GPIO_ENABLE, 0x01);
+ }
+ #endif
+
+ return ret;
+
+err_access:
+ return ret;
+}
+
+int max98504_set_speaker_status(int OnOff)
+{
+ mutex_lock(&max98504_lock);
+
+ if (regmap == NULL) {
+ pr_err("Speaker control is not available.\n");
+ mutex_unlock(&max98504_lock);
+ return 0;
+ }
+
+ pr_debug("%s: onoff=%d\n", __func__, OnOff);
+ if (OnOff) {
+ regmap_update_bits(regmap, MAX98504_REG_34_SPEAKER_ENABLE,
+ M98504_SPK_EN_MASK, M98504_SPK_EN_MASK);
+ regmap_update_bits(regmap, MAX98504_REG_40_GLOBAL_ENABLE,
+ M98504_GLOBAL_EN_MASK, M98504_GLOBAL_EN_MASK);
+ } else {
+ regmap_update_bits(regmap, MAX98504_REG_40_GLOBAL_ENABLE,
+ M98504_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(regmap, MAX98504_REG_34_SPEAKER_ENABLE,
+ M98504_SPK_EN_MASK, 0);
+ }
+
+ mutex_unlock(&max98504_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max98504_set_speaker_status);
+
+bool max98504_get_speaker_status(void)
+{
+ unsigned int ret;
+ mutex_lock(&max98504_lock);
+
+ if (regmap == NULL) {
+ pr_err("Speaker control is not allowed.\n");
+ return 0;
+ }
+ regmap_read(regmap, MAX98504_REG_34_SPEAKER_ENABLE, &ret);
+
+ mutex_unlock(&max98504_lock);
+ return (ret > 0) ? true : false;
+}
+EXPORT_SYMBOL_GPL(max98504_get_speaker_status);
+
+static const struct regmap_config max98504_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98504_REG_CNT,
+ .reg_defaults = max98504_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98504_reg),
+ .volatile_reg = max98504_volatile_register,
+ .readable_reg = max98504_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
+{
+ return (regulator_count_voltages(reg) > 0) ?
+ regulator_set_load(reg, load_uA) : 0;
+}
+
+static int max98504_regulator_config(struct i2c_client *i2c,
+ bool pullup, bool on)
+{
+ struct regulator *max98504_vcc_i2c;
+ int rc;
+ #define VCC_I2C_MIN_UV 1800000
+ #define VCC_I2C_MAX_UV 1800000
+ #define I2C_LOAD_UA 300000
+
+ msg_maxim("pullup=%d\n", pullup);
+
+ if (pullup) {
+ max98504_vcc_i2c = regulator_get(&i2c->dev, "vcc_i2c");
+
+ if (IS_ERR(max98504_vcc_i2c)) {
+ rc = PTR_ERR(max98504_vcc_i2c);
+ pr_err("Regulator get failed rc=%d\n", rc);
+ return rc;
+ }
+
+ if (regulator_count_voltages(max98504_vcc_i2c) > 0) {
+ rc = regulator_set_voltage(max98504_vcc_i2c,
+ VCC_I2C_MIN_UV, VCC_I2C_MAX_UV);
+ if (rc) {
+ pr_err("regulator set_vtg failed rc=%d\n", rc);
+ goto error_set_vtg_i2c;
+ }
+ }
+
+ rc = reg_set_optimum_mode_check(max98504_vcc_i2c, I2C_LOAD_UA);
+ if (rc < 0) {
+ pr_err("Regulator vcc_i2c set_opt failed rc=%d\n", rc);
+ goto error_reg_opt_i2c;
+ }
+
+ rc = regulator_enable(max98504_vcc_i2c);
+ if (rc) {
+ pr_err("Regulator vcc_i2c enable failed rc=%d\n", rc);
+ goto error_reg_en_vcc_i2c;
+ }
+ }
+
+ return 0;
+
+error_reg_en_vcc_i2c:
+ if (pullup)
+ reg_set_optimum_mode_check(max98504_vcc_i2c, 0);
+error_reg_opt_i2c:
+ regulator_disable(max98504_vcc_i2c);
+error_set_vtg_i2c:
+ regulator_put(max98504_vcc_i2c);
+
+ return rc;
+}
+
+#ifdef USE_MAX98504_IRQ
+static irqreturn_t max98504_interrupt(int irq, void *data)
+{
+ struct max98504_priv *max98504 = (struct max98504_priv *) data;
+
+ unsigned int mask;
+ unsigned int flag;
+
+ regmap_read(max98504->regmap, MAX98504_REG_03_INTERRUPT_ENABLES, &mask);
+ regmap_read(max98504->regmap, MAX98504_REG_02_INTERRUPT_FLAGS, &flag);
+
+ pr_debug("max98504_irq! flag=0x%02x mask=0x%02x -> flag=0x%02x\n",
+ flag, mask, flag & mask);
+
+ flag &= mask;
+
+ if (!flag)
+ return IRQ_NONE;
+
+ /* Send work to be scheduled */
+ if (flag & M98504_INT_GENFAIL_EN_MASK)
+ msg_maxim("M98504_INT_GENFAIL_EN_MASK active!");
+
+ if (flag & M98504_INT_AUTHDONE_EN_MASK)
+ msg_maxim("M98504_INT_AUTHDONE_EN_MASK active!");
+
+ if (flag & M98504_INT_VBATBROWN_EN_MASK)
+ msg_maxim("M98504_INT_VBATBROWN_EN_MASK active!");
+
+ if (flag & M98504_INT_WATCHFAIL_EN_MASK)
+ msg_maxim("M98504_INT_WATCHFAIL_EN_MASK active!");
+
+ if (flag & M98504_INT_THERMWARN_END_EN_MASK)
+ msg_maxim("M98504_INT_THERMWARN_END_EN_MASK active!");
+
+ if (flag & M98504_INT_THERMWARN_BGN_EN_MASK)
+ msg_maxim("M98504_INT_THERMWARN_BGN_EN_MASK active!\n");
+
+ if (flag & M98504_INT_THERMSHDN_END_EN_MASK)
+ msg_maxim("M98504_INT_THERMSHDN_END_EN_MASK active!\n");
+
+ if (flag & M98504_INT_THERMSHDN_BGN_FLAG_MASK)
+ msg_maxim("M98504_INT_THERMSHDN_BGN_FLAG_MASK active!\n");
+
+ regmap_write(max98504->regmap,
+ MAX98504_REG_04_INTERRUPT_FLAG_CLEARS, flag&0xff);
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static int max98504_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98504_priv *max98504;
+ struct max98504_pdata *pdata;
+ int ret = 0;
+#ifdef CONFIG_SND_SOC_MAXIM_DSM_A
+ const char *of_class_name = DEFAULT_LOG_CLASS_NAME;
+ const char *class_name_log;
+#endif
+
+ msg_maxim("\n");
+
+ max98504 = kzalloc(sizeof(struct max98504_priv), GFP_KERNEL);
+ if (max98504 == NULL)
+ return -ENOMEM;
+
+ max98504->devtype = id->driver_data;
+ i2c_set_clientdata(i2c, max98504);
+ max98504->control_data = i2c;
+
+ if (i2c->dev.of_node) {
+ max98504->pdata = devm_kzalloc(&i2c->dev,
+ sizeof(struct max98504_pdata), GFP_KERNEL);
+ if (!max98504->pdata) {
+ dev_err(&i2c->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ } else
+ pdata = max98504->pdata;
+
+ #ifdef USE_MAX98504_IRQ
+ pdata->irq = of_get_named_gpio_flags(i2c->dev.of_node,
+ "max98504,irq-gpio", 0, NULL);
+ #endif
+
+ ret = of_property_read_u32(i2c->dev.of_node,
+ "max98504,rx_mode", &pdata->rx_mode);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to read rx_mode.\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(i2c->dev.of_node,
+ "max98504,tx_mode", &pdata->tx_mode);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to read tx_mode.\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(i2c->dev.of_node,
+ "max98504,cfg_data", (u32 *) &pdata->cfg_data,
+ sizeof(struct max98504_cfg_data)/sizeof(u32));
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to read cfg_data.\n");
+ return -EINVAL;
+ }
+ #ifdef CONFIG_SND_SOC_MAXIM_DSM_A
+ ret = of_property_read_string(i2c->dev.of_node,
+ "max98504,log_class", &of_class_name);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to read log_class.\n");
+ of_class_name = DEFAULT_LOG_CLASS_NAME;
+ }
+ #endif
+ msg_maxim("rx_mode:%d, tx_mode:%d, tx_dither_en:%d"\
+ ", rx_dither_en:%d, meas_dc_block_en:%d"\
+ ", rx_flt_mode:%d, rx_ch_en:%d\n",
+ pdata->rx_mode,
+ pdata->tx_mode,
+ pdata->cfg_data.tx_dither_en,
+ pdata->cfg_data.rx_dither_en,
+ pdata->cfg_data.meas_dc_block_en,
+ pdata->cfg_data.rx_flt_mode,
+ pdata->cfg_data.rx_ch_en);
+ msg_maxim("tx_ch_en:%d, tx_hiz_ch_en:%d"\
+ ", tx_ch_src:%d, auth_en:%d, wdog_time_out:%d\n",
+ pdata->cfg_data.tx_ch_en,
+ pdata->cfg_data.tx_hiz_ch_en,
+ pdata->cfg_data.tx_ch_src,
+ pdata->cfg_data.auth_en,
+ pdata->cfg_data.wdog_time_out);
+ } else {
+ max98504->pdata = i2c->dev.platform_data;
+ pdata = max98504->pdata;
+ }
+
+ max98504_regulator_config(i2c,
+ of_property_read_bool(i2c->dev.of_node,
+ "max98504,i2c-pull-up"), 1);
+
+ max98504->regmap = regmap = regmap_init_i2c(i2c, &max98504_regmap);
+
+ if (IS_ERR(max98504->regmap)) {
+ ret = PTR_ERR(max98504->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ goto err_out;
+ }
+
+ max98504_probe(max98504);
+
+#ifdef CONFIG_SND_SOC_MAXIM_DSM_A
+ class_name_log = of_class_name;
+
+ max98504->dev_log_class =
+ class_create(THIS_MODULE, class_name_log);
+ if (max98504->dev_log_class == NULL) {
+ pr_err("%s: class_create fail.\n", __func__);
+ } else {
+ max98504->dev_log = device_create(max98504->dev_log_class, NULL,
+ 1, NULL, "max98504");
+ if (IS_ERR(max98504->dev_log)) {
+ ret = sysfs_create_group(&i2c->dev.kobj,
+ &max98504_attribute_group);
+ if (ret)
+ msg_maxim(\
+ "failed to create sysfs group [%d]", ret);
+ } else {
+ ret = sysfs_create_group(&max98504->dev_log->kobj,
+ &max98504_attribute_group);
+ if (ret)
+ msg_maxim("failed to create sysfs group [%d]",
+ ret);
+ }
+ }
+#endif
+
+#ifdef USE_MAX98504_IRQ
+ if (pdata != NULL && gpio_is_valid(pdata->irq)) {
+ ret = gpio_request(pdata->irq, "max98504_irq_gpio");
+ if (ret) {
+ dev_err(&i2c->dev, "unable to request gpio [%d]\n",
+ pdata->irq);
+ goto err_irq_gpio_req;
+ }
+ ret = gpio_direction_input(pdata->irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "unable to set direction for gpio [%d]\n",
+ pdata->irq);
+ goto err_irq_gpio_req;
+ }
+ i2c->irq = gpio_to_irq(pdata->irq);
+
+ ret = request_threaded_irq(i2c->irq, NULL, max98504_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max98504_interrupt", max98504);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to register interrupt\n");
+
+ } else {
+ dev_err(&i2c->dev, "irq gpio not provided\n");
+ }
+ return 0;
+
+err_irq_gpio_req:
+ if (gpio_is_valid(pdata->irq))
+ gpio_free(pdata->irq);
+ return 0;
+#endif
+
+err_out:
+ if (ret < 0) {
+ if (max98504->regmap)
+ regmap_exit(max98504->regmap);
+ kfree(max98504);
+ }
+
+ return 0;
+}
+
+static int max98504_i2c_remove(struct i2c_client *client)
+{
+ struct max98504_priv *max98504 = dev_get_drvdata(&client->dev);
+
+ if (max98504->regmap)
+ regmap_exit(max98504->regmap);
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static const struct i2c_device_id max98504_i2c_id[] = {
+ { "max98504", MAX98504 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
+
+static struct i2c_driver max98504_i2c_driver = {
+ .driver = {
+ .name = "max98504",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98504_i2c_probe,
+ .remove = max98504_i2c_remove,
+ .id_table = max98504_i2c_id,
+};
+
+static int __init max98504_init(void)
+{
+ return i2c_add_driver(&max98504_i2c_driver);
+}
+module_init(max98504_init);
+
+static void __exit max98504_exit(void)
+{
+ i2c_del_driver(&max98504_i2c_driver);
+}
+module_exit(max98504_exit);
+
+MODULE_DESCRIPTION("SoC MAX98504 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98504a.h b/sound/soc/codecs/max98504a.h
new file mode 100644
index 000000000000..e45a930a2f9c
--- /dev/null
+++ b/sound/soc/codecs/max98504a.h
@@ -0,0 +1,590 @@
+/*
+ * max98504.h -- MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2012 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 _MAX98504_H
+#define _MAX98504_H
+
+#include <linux/version.h>
+
+int max98504_set_speaker_status(int OnOff);
+
+/*
+ * MAX98504 Register Definitions
+ */
+#define MAX98504_REG_01_INTERRUPT_STATUS 0x01
+#define MAX98504_REG_02_INTERRUPT_FLAGS 0x02
+#define MAX98504_REG_03_INTERRUPT_ENABLES 0x03
+#define MAX98504_REG_04_INTERRUPT_FLAG_CLEARS 0x04
+#define MAX98504_REG_10_GPIO_ENABLE 0x10
+#define MAX98504_REG_11_GPIO_CONFIG 0x11
+#define MAX98504_REG_12_WATCHDOG_ENABLE 0x12
+#define MAX98504_REG_13_WATCHDOG_CONFIG 0x13
+#define MAX98504_REG_14_WATCHDOG_CLEAR 0x14
+#define MAX98504_REG_15_CLOCK_MONITOR_ENABLE 0x15
+#define MAX98504_REG_16_PVDD_BROWNOUT_ENABLE 0x16
+#define MAX98504_REG_17_PVDD_BROWNOUT_CONFIG_1 0x17
+#define MAX98504_REG_18_PVDD_BROWNOUT_CONFIG_2 0x18
+#define MAX98504_REG_19_PVDD_BROWNOUT_CONFIG_3 0x19
+#define MAX98504_REG_1A_PVDD_BROWNOUT_CONFIG_4 0x1a
+#define MAX98504_REG_20_PCM_RX_ENABLES 0x20
+#define MAX98504_REG_21_PCM_TX_ENABLES 0x21
+#define MAX98504_REG_22_PCM_TX_HIZ_CONTROL 0x22
+#define MAX98504_REG_23_PCM_TX_CHANNEL_SOURCES 0x23
+#define MAX98504_REG_24_PCM_MODE_CONFIG 0x24
+#define MAX98504_REG_25_PCM_DSP_CONFIG 0x25
+#define MAX98504_REG_26_PCM_CLOCK_SETUP 0x26
+#define MAX98504_REG_27_PCM_SAMPLE_RATE_SETUP 0x27
+#define MAX98504_REG_28_PCM_TO_SPEAKER_MONOMIX 0x28
+#define MAX98504_REG_30_PDM_TX_ENABLES 0x30
+#define MAX98504_REG_31_PDM_TX_HIZ_CONTROL 0x31
+#define MAX98504_REG_32_PDM_TX_CONTROL 0x32
+#define MAX98504_REG_33_PDM_RX_ENABLE 0x33
+#define MAX98504_REG_34_SPEAKER_ENABLE 0x34
+#define MAX98504_REG_35_SPEAKER_SOURCE_SELECT 0x35
+#define MAX98504_REG_36_MEASUREMENT_ENABLES 0x36
+#define MAX98504_REG_37_ANALOGUE_INPUT_GAIN 0x37
+#define MAX98504_REG_38_TEMPERATURE_LIMIT_CONFIG 0x38
+#define MAX98504_REG_39_ANALOGUE_SPARE 0x39
+#define MAX98504_REG_40_GLOBAL_ENABLE 0x40
+#define MAX98504_REG_41_SOFTWARE_RESET 0x41
+#define MAX98504_REG_7FFF_REV_ID 0x7fff
+
+#define MAX98504_REG_CNT (MAX98504_REG_41_SOFTWARE_RESET+1)
+
+/* MAX98504 Register Bit Fields */
+
+/* MAX98504_REG_01_INTERRUPT_STATUS*/
+#define M98504_INT_GENFAIL_STATUS_MASK (1<<7)
+#define M98504_INT_GENFAIL_STATUS_SHIFT 7
+#define M98504_INT_GENFAIL_STATUS_WIDTH 1
+
+#define M98504_INT_AUTHDONE_STATUS_MASK (1<<6)
+#define M98504_INT_AUTHDONE_STATUS_SHIFT 6
+#define M98504_INT_AUTHDONE_STATUS_WIDTH 1
+
+#define M98504_INT_VBATBROWN_STATUS_MASK (1<<5)
+#define M98504_INT_VBATBROWN_STATUS_SHIFT 5
+#define M98504_INT_VBATBROWN_STATUS_WIDTH 1
+
+#define M98504_INT_WATCHFAIL_STATUS_MASK (1<<4)
+#define M98504_INT_WATCHFAIL_STATUS_SHIFT 4
+#define M98504_INT_WATCHFAIL_STATUS_WIDTH 1
+
+#define M98504_INT_THERMWARN_STATUS_MASK (1<<3)
+#define M98504_INT_THERMWARN_STATUS_SHIFT 3
+#define M98504_INT_THERMWARN_STATUS_WIDTH 1
+
+#define M98504_INT_THERMSHDN_STATUS_MASK (1<<1)
+#define M98504_INT_THERMSHDN_STATUS_SHIFT 1
+#define M98504_INT_THERMSHDN_STATUS_WIDTH 1
+
+#define M98504_INT_INTERRUPT_STATUS_MASK (\
+ M98504_INT_GENFAIL_STATUS_MASK|M98504_INT_AUTHDONE_STATUS_MASK\
+ |M98504_INT_VBATBROWN_STATUS_MASK|M98504_INT_WATCHFAIL_STATUS_MASK\
+ |M98504_INT_THERMWARN_STATUS_MASK|M98504_INT_THERMSHDN_STATUS_MASK)
+
+/* MAX98504_REG_02_INTERRUPT_FLAGS*/
+#define M98504_INT_GENFAIL_FLAG_MASK (1<<7)
+#define M98504_INT_GENFAIL_FLAG_SHIFT 7
+#define M98504_INT_GENFAIL_FLAG_WIDTH 1
+
+#define M98504_INT_AUTHDONE_FLAG_MASK (1<<6)
+#define M98504_INT_AUTHDONE_FLAG_SHIFT 6
+#define M98504_INT_AUTHDONE_FLAG_WIDTH 1
+
+#define M98504_INT_VBATBROWN_FLAG_MASK (1<<5)
+#define M98504_INT_VBATBROWN_FLAG_SHIFT 5
+#define M98504_INT_VBATBROWN_FLAG_WIDTH 1
+
+#define M98504_INT_WATCHFAIL_FLAG_MASK (1<<4)
+#define M98504_INT_WATCHFAIL_FLAG_SHIFT 4
+#define M98504_INT_WATCHFAIL_FLAG_WIDTH 1
+
+#define M98504_INT_THERMWARN_END_FLAG_MASK (1<<3)
+#define M98504_INT_THERMWARN_END_FLAG_SHIFT 3
+#define M98504_INT_THERMWARN_END_FLAG_WIDTH 1
+
+#define M98504_INT_THERMWARN_BGN_FLAG_MASK (1<<2)
+#define M98504_INT_THERMWARN_BGN_FLAG_SHIFT 2
+#define M98504_INT_THERMWARN_BGN_FLAG_WIDTH 1
+
+#define M98504_INT_THERMSHDN_END_EN_MASK (1<<1)
+#define M98504_INT_THERMSHDN_END_FLAG_SHIFT 1
+#define M98504_INT_THERMSHDN_END_FLAG_WIDTH 1
+
+#define M98504_INT_THERMSHDN_BGN_FLAG_MASK (1<<0)
+#define M98504_INT_THERMSHDN_BGN_FLAG_SHIFT 0
+#define M98504_INT_THERMSHDN_BGN_FLAG_WIDTH 1
+
+/* MAX98504_REG_03_INTERRUPT_ENABLES*/
+#define M98504_INT_GENFAIL_EN_MASK (1<<7)
+#define M98504_INT_GENFAIL_EN_SHIFT 7
+#define M98504_INT_GENFAIL_EN_WIDTH 1
+
+#define M98504_INT_AUTHDONE_EN_MASK (1<<6)
+#define M98504_INT_AUTHDONE_EN_SHIFT 6
+#define M98504_INT_AUTHDONE_EN_WIDTH 1
+
+#define M98504_INT_VBATBROWN_EN_MASK (1<<5)
+#define M98504_INT_VBATBROWN_EN_SHIFT 5
+#define M98504_INT_VBATBROWN_EN_WIDTH 1
+
+#define M98504_INT_WATCHFAIL_EN_MASK (1<<4)
+#define M98504_INT_WATCHFAIL_EN_SHIFT 4
+#define M98504_INT_WATCHFAIL_EN_WIDTH 1
+
+#define M98504_INT_THERMWARN_END_EN_MASK (1<<3)
+#define M98504_INT_THERMWARN_END_EN_SHIFT 3
+#define M98504_INT_THERMWARN_END_EN_WIDTH 1
+
+#define M98504_INT_THERMWARN_BGN_EN_MASK (1<<2)
+#define M98504_INT_THERMWARN_BGN_EN_SHIFT 2
+#define M98504_INT_THERMWARN_BGN_EN_WIDTH 1
+
+#define M98504_INT_THERMSHDN_END_EN_MASK (1<<1)
+#define M98504_INT_THERMSHDN_END_EN_SHIFT 1
+#define M98504_INT_THERMSHDN_END_EN_WIDTH 1
+
+#define M98504_INT_THERMSHDN_BGN_EN_MASK (1<<0)
+#define M98504_INT_THERMSHDN_BGN_EN_SHIFT 0
+#define M98504_INT_THERMSHDN_BGN_EN_WIDTH 1
+
+/* MAX98504_REG_04_INTERRUPT_FLAG_CLEARS*/
+#define M98504_INT_FLAG_GENFAIL_CLR_MASK (1<<7)
+#define M98504_INT_FLAG_GENFAIL_CLR_SHIFT 7
+#define M98504_INT_FLAG_GENFAIL_CLR_WIDTH 1
+
+#define M98504_INT_FLAG_AUTHDONE_CLR_MASK (1<<6)
+#define M98504_INT_FLAG_AUTHDONE_CLR_SHIFT 6
+#define M98504_INT_FLAG_AUTHDONE_CLR_WIDTH 1
+
+#define M98504_INT_FLAG_VBATBROWN_CLR_MASK (1<<5)
+#define M98504_INT_FLAG_VBATBROWN_CLR_SHIFT 5
+#define M98504_INT_FLAG_VBATBROWN_CLR_WIDTH 1
+
+#define M98504_INT_FLAG_WATCHFAIL_CLR_MASK (1<<4)
+#define M98504_INT_FLAG_WATCHFAIL_CLR_SHIFT 4
+#define M98504_INT_FLAG_WATCHFAIL_CLR_WIDTH 1
+
+#define M98504_INT_FLAG_THERMWARN_END_CLR_MASK (1<<3)
+#define M98504_INT_FLAG_THERMWARN_END_CLR_SHIFT 3
+#define M98504_INT_FLAG_THERMWARN_END_CLR_WIDTH 1
+
+#define M98504_INT_FLAG_THERMWARN_BGN_CLR_MASK (1<<2)
+#define M98504_INT_FLAG_THERMWARN_BGN_CLR_SHIFT 2
+#define M98504_INT_FLAG_THERMWARN_BGN_CLR_WIDTH 1
+
+#define M98504_INT_FLAG_THERMSHDN_END_CLR_MASK (1<<1)
+#define M98504_INT_FLAG_THERMSHDN_END_CLR_SHIFT 1
+#define M98504_INT_FLAG_THERMSHDN_END_CLR_WIDTH 1
+
+#define M98504_INT_FLAG_THERMSHDN_BGN_CLR_MASK (1<<0)
+#define M98504_INT_FLAG_THERMSHDN_BGN_CLR_SHIFT 0
+#define M98504_INT_FLAG_THERMSHDN_BGN_CLR_WIDTH 1
+
+/* MAX98504_REG_10_GPIO_ENABLE*/
+#define M98504_GPIO_ENABLE_MASK (1<<0)
+#define M98504_GPIO_ENALBE_SHIFT 0
+#define M98504_GPIO_ENABLE_WIDTH 1
+
+/* MAX98504_REG_11_GPIO_CONFIG*/
+#define M98504_GPIO_OP_MODE_MASK (1<<0)
+#define M98504_GPIO_OP_MODE_SHIFT 0
+#define M98504_GPIO_OP_MODE_WIDTH 1
+
+/* MAX98504_REG_12_WATCHDOG_ENABLE*/
+#define M98504_WDOG_ENABLE_MASK (1<<0)
+#define M98504_WDOG_ENABLE_SHIFT 0
+#define M98504_WDOG_ENABLE_WIDTH 1
+
+/* MAX98504_REG_13_WATCHDOG_CONFIG*/
+#define M98504_WDOG_CONFIG_MASK (0x3<<0)
+#define M98504_WDOG_CONFIG_SHIFT 0
+#define M98504_WDOG_CONFIG_WIDTH 2
+
+#define M98504_WDOG_CONFIG_100MS 0
+#define M98504_WDOG_CONFIG_500MS 1
+#define M98504_WDOG_CONFIG_1000MS 2
+#define M98504_WDOG_CONFIG_2000MS 3
+
+/* MAX98504_REG_14_WATCHDOG_CLEAR*/
+#define M98504_WDOG_CLEAR_MASK (0xff<<0)
+#define M98504_WDOG_CLEAR_SHIFT 0
+#define M98504_WDOG_CLEAR_WIDTH 8
+
+/* MAX98504_REG_15_CLOCK_MONITOR_ENABLE*/
+#define M98504_CMON_ENA_MASK (1<<0)
+#define M98504_CMON_ENA_SHIFT 0
+#define M98504_CMON_ENA_WIDTH 1
+
+/* MAX98504_REG_16_PVDD_BROWNOUT_ENABLE*/
+#define M98504_PVDD_BROWNOUT_ENA_MASK (1<<0)
+#define M98504_PVDD_BROWNOUT_ENA_SHIFT 0
+#define M98504_PVDD_BROWNOUT_ENA_WIDTH 1
+
+/* MAX98504_REG_17_PVDD_BROWNOUT_CONFIG_1*/
+#define M98504_PVDD_BROWNOUT_CFG1_CODE_MASK (0x1f<<3)
+#define M98504_PVDD_BROWNOUT_CFG1_CODE_SHIFT 3
+#define M98504_PVDD_BROWNOUT_CFG1_CODE_WIDTH 5
+
+#define M98504_PVDD_BROWNOUT_CFG1_MAX_ATTEN_MASK (0x7<<0)
+#define M98504_PVDD_BROWNOUT_CFG1_MAX_ATTEN_SHIFT 0
+#define M98504_PVDD_BROWNOUT_CFG1_MAX_ATTEN_WIDTH 3
+
+/* MAX98504_REG_18_PVDD_BROWNOUT_CONFIG_2*/
+#define M98504_PVDD_BROWNOUT_CFG2_ATTK_HOLD_MASK (0xff<<0)
+#define M98504_PVDD_BROWNOUT_CFG2_ATTK_HOLD_SHIFT 0
+#define M98504_PVDD_BROWNOUT_CFG2_ATTK_HOLD_WIDTH 8
+
+/* MAX98504_REG_19_PVDD_BROWNOUT_CONFIG_3*/
+#define M98504_PVDD_BROWNOUT_CFG3_TIMED_HOLD_MASK (0xff<<0)
+#define M98504_PVDD_BROWNOUT_CFG3_TIMED_HOLD_SHIFT 0
+#define M98504_PVDD_BROWNOUT_CFG3_TIMED_HOLD_WIDTH 8
+
+/* MAX98504_REG_1A_PVDD_BROWNOUT_CONFIG_4*/
+#define M98504_PVDD_BROWNOUT_CFG4_RELEASE_MASK (0xff<<0)
+#define M98504_PVDD_BROWNOUT_CFG4_RELEASE_SHIFT 0
+#define M98504_PVDD_BROWNOUT_CFG4_RELEASE_WIDTH 8
+
+/* MAX98504_REG_20_PCM_RX_ENABLES*/
+#define M98504_PCM_RX_EN_CH7_MASK (1<<7)
+#define M98504_PCM_RX_EN_CH7_SHIFT 7
+#define M98504_PCM_RX_EN_CH7_WIDTH 1
+
+#define M98504_PCM_RX_EN_CH6_MASK (1<<6)
+#define M98504_PCM_RX_EN_CH6_SHIFT 6
+#define M98504_PCM_RX_EN_CH6_WIDTH 1
+
+#define M98504_PCM_RX_EN_CH5_MASK (1<<5)
+#define M98504_PCM_RX_EN_CH5_SHIFT 5
+#define M98504_PCM_RX_EN_CH5_WIDTH 1
+
+#define M98504_PCM_RX_EN_CH4_MASK (1<<4)
+#define M98504_PCM_RX_EN_CH4_SHIFT 4
+#define M98504_PCM_RX_EN_CH4_WIDTH 1
+
+#define M98504_PCM_RX_EN_CH3_MASK (1<<3)
+#define M98504_PCM_RX_EN_CH3_SHIFT 3
+#define M98504_PCM_RX_EN_CH3_WIDTH 1
+
+#define M98504_PCM_RX_EN_CH2_MASK (1<<2)
+#define M98504_PCM_RX_EN_CH2_SHIFT 2
+#define M98504_PCM_RX_EN_CH2_WIDTH 1
+
+#define M98504_PCM_RX_EN_CH1_MASK (1<<1)
+#define M98504_PCM_RX_EN_CH1_SHIFT 1
+#define M98504_PCM_RX_EN_CH1_WIDTH 1
+
+#define M98504_PCM_RX_EN_CH0_MASK (1<<0)
+#define M98504_PCM_RX_EN_CH0_SHIFT 0
+#define M98504_PCM_RX_EN_CH0_WIDTH 1
+
+/* MAX98504_REG_21_PCM_TX_ENABLES*/
+#define M98504_PCM_TX_EN_CH7_MASK (1<<7)
+#define M98504_PCM_TX_EN_CH7_SHIFT 7
+#define M98504_PCM_TX_EN_CH7_WIDTH 1
+
+#define M98504_PCM_TX_EN_CH6_MASK (1<<6)
+#define M98504_PCM_TX_EN_CH6_SHIFT 6
+#define M98504_PCM_TX_EN_CH6_WIDTH 1
+
+#define M98504_PCM_TX_EN_CH5_MASK (1<<5)
+#define M98504_PCM_TX_EN_CH5_SHIFT 5
+#define M98504_PCM_TX_EN_CH5_WIDTH 1
+
+#define M98504_PCM_TX_EN_CH4_MASK (1<<4)
+#define M98504_PCM_TX_EN_CH4_SHIFT 4
+#define M98504_PCM_TX_EN_CH4_WIDTH 1
+
+#define M98504_PCM_TX_EN_CH3_MASK (1<<3)
+#define M98504_PCM_TX_EN_CH3_SHIFT 3
+#define M98504_PCM_TX_EN_CH3_WIDTH 1
+
+#define M98504_PCM_TX_EN_CH2_MASK (1<<2)
+#define M98504_PCM_TX_EN_CH2_SHIFT 2
+#define M98504_PCM_TX_EN_CH2_WIDTH 1
+
+#define M98504_PCM_TX_EN_CH1_MASK (1<<1)
+#define M98504_PCM_TX_EN_CH1_SHIFT 1
+#define M98504_PCM_TX_EN_CH1_WIDTH 1
+
+#define M98504_PCM_TX_EN_CH0_MASK (1<<0)
+#define M98504_PCM_TX_EN_CH0_SHIFT 0
+#define M98504_PCM_TX_EN_CH0_WIDTH 1
+
+/* MAX98504_REG_22_PCM_TX_HIZ_CONTROL*/
+#define M98504_PCM_TX_HIZ_CTRL_CH7_MASK (1<<7)
+#define M98504_PCM_TX_HIZ_CTRL_CH7_SHIFT 7
+#define M98504_PCM_TX_HIZ_CTRL_CH7_WIDTH 1
+
+#define M98504_PCM_TX_HIZ_CTRL_CH6_MASK (1<<6)
+#define M98504_PCM_TX_HIZ_CTRL_CH6_SHIFT 6
+#define M98504_PCM_TX_HIZ_CTRL_CH6_WIDTH 1
+
+#define M98504_PCM_TX_HIZ_CTRL_CH5_MASK (1<<5)
+#define M98504_PCM_TX_HIZ_CTRL_CH5_SHIFT 5
+#define M98504_PCM_TX_HIZ_CTRL_CH5_WIDTH 1
+
+#define M98504_PCM_TX_HIZ_CTRL_CH4_MASK (1<<4)
+#define M98504_PCM_TX_HIZ_CTRL_CH4_SHIFT 4
+#define M98504_PCM_TX_HIZ_CTRL_CH4_WIDTH 1
+
+#define M98504_PCM_TX_HIZ_CTRL_CH3_MASK (1<<3)
+#define M98504_PCM_TX_HIZ_CTRL_CH3_SHIFT 3
+#define M98504_PCM_TX_HIZ_CTRL_CH3_WIDTH 1
+
+#define M98504_PCM_TX_HIZ_CTRL_CH2_MASK (1<<2)
+#define M98504_PCM_TX_HIZ_CTRL_CH2_SHIFT 2
+#define M98504_PCM_TX_HIZ_CTRL_CH2_WIDTH 1
+
+#define M98504_PCM_TX_HIZ_CTRL_CH1_MASK (1<<1)
+#define M98504_PCM_TX_HIZ_CTRL_CH1_SHIFT 1
+#define M98504_PCM_TX_HIZ_CTRL_CH1_WIDTH 1
+
+#define M98504_PCM_TX_HIZ_CTRL_CH0_MASK (1<<0)
+#define M98504_PCM_TX_HIZ_CTRL_CH0_SHIFT 0
+#define M98504_PCM_TX_HIZ_CTRL_CH0_WIDTH 1
+
+/* MAX98504_REG_23_PCM_TX_CHANNEL_SOURCES*/
+#define M98504_PCM_TX_SOURCE_CH7_MASK (1<<7)
+#define M98504_PCM_TX_SOURCE_CH7_SHIFT 7
+#define M98504_PCM_TX_SOURCE_CH7_WIDTH 1
+
+#define M98504_PCM_TX_SOURCE_CH6_MASK (1<<6)
+#define M98504_PCM_TX_SOURCE_CH6_SHIFT 6
+#define M98504_PCM_TX_SOURCE_CH6_WIDTH 1
+
+#define M98504_PCM_TX_SOURCE_CH5_MASK (1<<5)
+#define M98504_PCM_TX_SOURCE_CH5_SHIFT 5
+#define M98504_PCM_TX_SOURCE_CH5_WIDTH 1
+
+#define M98504_PCM_TX_SOURCE_CH4_MASK (1<<4)
+#define M98504_PCM_TX_SOURCE_CH4_SHIFT 4
+#define M98504_PCM_TX_SOURCE_CH4_WIDTH 1
+
+#define M98504_PCM_TX_SOURCE_CH3_MASK (1<<3)
+#define M98504_PCM_TX_SOURCE_CH3_SHIFT 3
+#define M98504_PCM_TX_SOURCE_CH3_WIDTH 1
+
+#define M98504_PCM_TX_SOURCE_CH2_MASK (1<<2)
+#define M98504_PCM_TX_SOURCE_CH2_SHIFT 2
+#define M98504_PCM_TX_SOURCE_CH2_WIDTH 1
+
+#define M98504_PCM_TX_SOURCE_CH1_MASK (1<<1)
+#define M98504_PCM_TX_SOURCE_CH1_SHIFT 1
+#define M98504_PCM_TX_SOURCE_CH1_WIDTH 1
+
+#define M98504_PCM_TX_SOURCE_CH0_MASK (1<<0)
+#define M98504_PCM_TX_SOURCE_CH0_SHIFT 0
+#define M98504_PCM_TX_SOURCE_CH0_WIDTH 1
+
+/* MAX98504_REG_24_PCM_MODE_CONFIG*/
+#define M98504_PCM_MODE_CFG_CH_SIZE_MASK (0x3<<6)
+#define M98504_PCM_MODE_CFG_CH_SIZE_SHIFT 6
+#define M98504_PCM_MODE_CFG_CH_SIZE_WIDTH 2
+
+#define M98504_PCM_MODE_CFG_CH_SIZE_8_MASK \
+ (0<<M98504_PCM_MODE_CFG_CH_SIZE_SHIFT)
+#define M98504_PCM_MODE_CFG_CH_SIZE_16_MASK \
+ (1<<M98504_PCM_MODE_CFG_CH_SIZE_SHIFT)
+#define M98504_PCM_MODE_CFG_CH_SIZE_24_MASK \
+ (2<<M98504_PCM_MODE_CFG_CH_SIZE_SHIFT)
+#define M98504_PCM_MODE_CFG_CH_SIZE_32_MASK \
+ (3<<M98504_PCM_MODE_CFG_CH_SIZE_SHIFT)
+
+#define M98504_PCM_MODE_CFG_FORMAT_MASK (0x7<<3)
+#define M98504_PCM_MODE_CFG_FORMAT_SHIFT 3
+#define M98504_PCM_MODE_CFG_FORMAT_WIDTH 3
+
+#define M98504_PCM_MODE_CFG_FORMAT_I2S_MASK \
+ (0<<M98504_PCM_MODE_CFG_FORMAT_SHIFT)
+#define M98504_PCM_MODE_CFG_FORMAT_LJ_MASK \
+ (1<<M98504_PCM_MODE_CFG_FORMAT_SHIFT)
+#define M98504_PCM_MODE_CFG_FORMAT_RJ_MASK \
+ (2<<M98504_PCM_MODE_CFG_FORMAT_SHIFT)
+#define M98504_PCM_MODE_CFG_FORMAT_TDM_MODE1_MASK \
+ (3<<M98504_PCM_MODE_CFG_FORMAT_SHIFT)
+#define M98504_PCM_MODE_CFG_FORMAT_TDM_MODE2_MASK \
+ (4<<M98504_PCM_MODE_CFG_FORMAT_SHIFT)
+
+#define M98504_PCM_MODE_CFG_BCLKEDGE_MASK (1<<2)
+#define M98504_PCM_MODE_CFG_BCLKEDGE_SHIFT 2
+#define M98504_PCM_MODE_CFG_BCLKEDGE_WIDTH 1
+
+#define M98504_PCM_MODE_CFG_CHSEL_MASK (1<<1)
+#define M98504_PCM_MODE_CFG_CHSEL_SHIFT 1
+#define M98504_PCM_MODE_CFG_CHSEL_WIDTH 1
+
+#define M98504_PCM_MODE_CFG_TX_EXTRA_HIZ_MASK (1<<0)
+#define M98504_PCM_MODE_CFG_TX_EXTRA_HIZ_SHIFT 0
+#define M98504_PCM_MODE_CFG_TX_EXTRA_HIZ_WIDTH 1
+
+/* MAX98504_REG_25_PCM_DSP_CONFIG*/
+#define M98504_PCM_DSP_CFG_TX_DITH_EN_MASK (1<<7)
+#define M98504_PCM_DSP_CFG_TX_DITH_EN_SHIFT 7
+#define M98504_PCM_DSP_CFG_TX_DITH_EN_WIDTH 1
+
+#define M98504_PCM_DSP_CFG_MEAS_DCBLK_EN_MASK (1<<6)
+#define M98504_PCM_DSP_CFG_MEAS_DCBLK_EN_SHIFT 6
+#define M98504_PCM_DSP_CFG_MEAS_DCBLK_EN_WIDTH 1
+
+#define M98504_PCM_DSP_CFG_RX_DITH_EN_MASK (1<<5)
+#define M98504_PCM_DSP_CFG_RX_DITH_EN_SHIFT 5
+#define M98504_PCM_DSP_CFG_RX_DITH_EN_WIDTH 1
+
+#define M98504_PCM_DSP_CFG_RX_FLT_MODE_MASK (1<<4)
+#define M98504_PCM_DSP_CFG_RX_FLT_MODE_SHIFT 4
+#define M98504_PCM_DSP_CFG_RX_FLT_MODE_WIDTH 1
+
+#define M98504_PCM_DSP_CFG_FLT_MASK (\
+ M98504_PCM_DSP_CFG_TX_DITH_EN_MASK|\
+ M98504_PCM_DSP_CFG_MEAS_DCBLK_EN_MASK|\
+ M98504_PCM_DSP_CFG_RX_DITH_EN_MASK|\
+ M98504_PCM_DSP_CFG_RX_FLT_MODE_MASK)
+#define M98504_PCM_DSP_CFG_FLT_SHIFT 4
+#define M98504_PCM_DSP_CFG_FLT_WIDTH 4
+
+
+#define M98504_PCM_DSP_CFG_RX_GAIN_MASK (0xf<<0)
+#define M98504_PCM_DSP_CFG_RX_GAIN_SHIFT 0
+#define M98504_PCM_DSP_CFG_RX_GAIN_WIDTH 4
+
+/* MAX98504_REG_26_PCM_CLOCK_SETUP*/
+#define M98504_PCM_CLK_SETUP_BSEL_MASK (0xf<<0)
+#define M98504_PCM_CLK_SETUP_BSEL_SHIFT 0
+#define M98504_PCM_CLK_SETUP_BSEL_WIDTH 4
+
+#define M98094_PCM_CLK_SETUP_DAI_BSEL64 (1<<2)
+
+/* MAX98504_REG_27_PCM_SAMPLE_RATE_SETUP*/
+#define M98504_PCM_SR_SETUP_SPK_SR_MASK (0xf<<4)
+#define M98504_PCM_SR_SETUP_SPK_SR_SHIFT 4
+#define M98504_PCM_SR_SETUP_SPK_SR_WIDTH 4
+
+#define M98504_PCM_SR_SETUP_MEAS_SR_MASK (0xf<<0)
+#define M98504_PCM_SR_SETUP_MEAS_SR_SHIFT 0
+#define M98504_PCM_SR_SETUP_MEAS_SR_WIDTH 4
+
+/* MAX98504_REG_28_PCM_TO_SPEAKER_MONOMIX*/
+#define M98504_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3<<6)
+#define M98504_PCM_TO_SPK_MONOMIX_CFG_SHIFT 6
+#define M98504_PCM_TO_SPK_MONOMIX_CFG_WIDTH 2
+
+#define M98504_PCM_TO_SPK_MONOMIX_CH1_SRC_MASK (0x7<<3)
+#define M98504_PCM_TO_SPK_MONOMIX_CH1_SRC_SHIFT 3
+#define M98504_PCM_TO_SPK_MONOMIX_CH1_SRC_WIDTH 3
+
+#define M98504_PCM_TO_SPK_MONOMIX_CH0_SRC_MASK (0x7<<0)
+#define M98504_PCM_TO_SPK_MONOMIX_CH0_SRC_SHIFT 0
+#define M98504_PCM_TO_SPK_MONOMIX_CH0_SRC_WIDTH 3
+
+/* MAX98504_REG_30_PDM_TX_ENABLES*/
+#define M98504_PDM_EX_EN_CH1_MASK (1<<1)
+#define M98504_PDM_EX_EN_CH1_SHIFT 1
+#define M98504_PDM_EX_EN_CH1_WIDTH 1
+
+#define M98504_PDM_EX_EN_CH0_MASK (1<<0)
+#define M98504_PDM_EX_EN_CH0_SHIFT 0
+#define M98504_PDM_EX_EN_CH0_WIDTH 1
+
+/* MAX98504_REG_31_PDM_TX_HIZ_CONTROL*/
+#define M98504_PDM_EX_HIZ_CTRL_CH1_MASK (1<<1)
+#define M98504_PDM_EX_HIZ_CTRL_CH1_SHIFT 1
+#define M98504_PDM_EX_HIZ_CTRL_CH1_WIDTH 1
+
+#define M98504_PDM_EX_HIZ_CTRL_CH0_MASK (1<<0)
+#define M98504_PDM_EX_HIZ_CTRL_CH0_SHIFT 0
+#define M98504_PDM_EX_HIZ_CTRL_CH0_WIDTH 1
+
+/* MAX98504_REG_32_PDM_TX_CONTROL*/
+#define M98504_PDM_EX_CTRL_CH1_SRC_MASK (1<<1)
+#define M98504_PDM_EX_CTRL_CH1_SRC_SHIFT 1
+#define M98504_PDM_EX_CTRL_CH1_SRC_WIDTH 1
+
+#define M98504_PDM_EX_CTRL_CH0_SRC_MASK (1<<0)
+#define M98504_PDM_EX_CTRL_CH0_SRC_SHIFT 0
+#define M98504_PDM_EX_CTRL_CH0_SRC_WIDTH 1
+
+/* MAX98504_REG_33_PDM_RX_ENABLE*/
+#define M98504_PDM_RX_EN_MASK (1<<0)
+#define M98504_PDM_RX_EN_SHIFT 0
+#define M98504_PDM_RX_EN_WIDTH 1
+
+/* MAX98504_REG_34_SPEAKER_ENABLE*/
+#define M98504_SPK_EN_MASK (1<<0)
+#define M98504_SPK_EN_SHIFT 0
+#define M98504_SPK_EN_WIDTH 1
+
+/* MAX98504_REG_35_SPEAKER_SOURCE_SELECT*/
+#define M98504_SPK_SRC_SEL_MASK (0x3<<0)
+#define M98504_SPK_SRC_SEL_SHIFT 0
+#define M98504_SPK_SRC_SEL_WIDTH 2
+
+/* MAX98504_REG_36_MEASUREMENT_ENABLES*/
+#define M98504_MEAS_I_EN_MASK (1<<1)
+#define M98504_MEAS_I_EN_SHIFT 1
+#define M98504_MEAS_I_EN_WIDTH 1
+
+#define M98504_MEAS_V_EN_MASK (1<<0)
+#define M98504_MEAS_V_EN_SHIFT 0
+#define M98504_MEAS_V_EN_WIDTH 1
+
+/* MAX98504_REG_37_ANALOGUE_INPUT_GAIN*/
+#define M98504_ANALOG_INPUT_GAIN_MASK (1<<0)
+#define M98504_ANALOG_INPUT_GAIN_SHIFT 0
+#define M98504_ANALOG_INPUT_GAIN_WIDTH 1
+
+/* MAX98504_REG_38_TEMPERATURE_LIMIT_CONFIG*/
+#define M98504_TEMP_LIMIT_CFG_TEMPWARN_SEL_MASK (0x2<<2)
+#define M98504_TEMP_LIMIT_CFG_TEMPWARN_SEL_SHIFT 2
+#define M98504_TEMP_LIMIT_CFG_TEMPWARN_SEL_WIDTH 2
+
+#define M98504_TEMP_LIMIT_CFG_TEMP_SEL_MASK (0x2<<0)
+#define M98504_TEMP_LIMIT_CFG_TEMP_SEL_SHIFT 0
+#define M98504_TEMP_LIMIT_CFG_TEMP_SEL_WIDTH 2
+
+/* MAX98504_REG_39_ANALOGUE_SPARE*/
+#define M98504_ANALOG_SPARE_MASK (0xff<<0)
+#define M98504_ANALOG_SPARE_SHIFT 0
+#define M98504_ANALOG_SPARE_WIDTH 8
+
+/* MAX98504_REG_40_GLOBAL_ENABLE*/
+#define M98504_GLOBAL_EN_MASK (1<<0)
+#define M98504_GLOBAL_EN_SHIFT 0
+#define M98504_GLOBAL_EN_WIDTH 1
+
+/* MAX98504_REG_41_SOFTWARE_RESET*/
+#define M98504_SOFTWARE_RESET_MASK (1<<0)
+#define M98504_SOFTWARE_RESET_SHIFT 0
+#define M98504_SOFTWARE_RESET_WIDTH 1
+
+enum max98504_type {
+ MAX98504,
+};
+
+struct max98504_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+};
+
+struct max98504_priv {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ enum max98504_type devtype;
+ void *control_data;
+ struct max98504_pdata *pdata;
+ unsigned int sysclk;
+ struct max98504_cdata dai[1];
+#ifdef CONFIG_SND_SOC_MAXIM_DSM_A
+ struct class *dev_log_class;
+ struct device *dev_log;
+#endif
+};
+
+#endif