Alsa-devel
Threads by month
- ----- 2024 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
March 2015
- 148 participants
- 317 discussions
[alsa-devel] [PATCH] ALSA: hda - Fix regression of HD-audio controller fallback modes
by Takashi Iwai 08 Mar '15
by Takashi Iwai 08 Mar '15
08 Mar '15
The commit [63e51fd708f5: ALSA: hda - Don't take unresponsive D3
transition too serious] introduced a conditional fallback behavior to
the HD-audio controller depending on the flag set. However, it
introduced a silly bug, too, that the flag was evaluated in a reverse
way. This resulted in a regression of HD-audio controller driver
where it can't go to the fallback mode at communication errors.
Unfortunately (or fortunately?) this didn't come up until recently
because the affected code path is an error handling that happens only
on an unstable hardware chip. Most of recent chips work stably, thus
they didn't hit this problem. Now, we've got a regression report with
a VIA chip, and this seems indeed requiring the fallback to the
polling mode, and finally the bug was revealed.
The fix is a oneliner to remove the wrong logical NOT in the check.
(Lesson learned - be careful about double negation.)
The bug should be backported to stable, but the patch won't be
applicable to 3.13 or earlier because of the code splits. The stable
fix patches for earlier kernels will be posted later manually.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=94021
Fixes: 63e51fd708f5 ('ALSA: hda - Don't take unresponsive D3 transition too serious')
Cc: <stable(a)vger.kernel.org> # v3.14+
Signed-off-by: Takashi Iwai <tiwai(a)suse.de>
---
sound/pci/hda/hda_controller.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index a2ce773bdc62..17c2637d842c 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -1164,7 +1164,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
}
}
- if (!bus->no_response_fallback)
+ if (bus->no_response_fallback)
return -1;
if (!chip->polling_mode && chip->poll_count < 2) {
--
2.3.1
1
0
This patch series attempts to split existing HDA code to create common hda
library. This is done in order to make HDA code available to other users like
upcoming Intel platforms which sport a HDA controller along with an I2S
link. This series is 1st patch series which will try to refactor HDA by
splitting it up and then adding ASoC HDA controller driver for these
platforms.
This series moves code to sound/hda, renames the files mostly. The second
series will do API changes and subsequent ones will be using HDA bus patches
which Takashi posted recently
Comments welcome...
~Vinod
Jeeja KP (5):
ALSA: hda: move hdaudio header files to global include
ALSA: HDA: create HDA common lib
ALSA: hdalib - fix existing checkpatch issues
ALSA: hda: rename sound/hda/hda* as sound/hda/hdalib
ALSA: hdalib: rename common header files as hdalib_*
.../hda/hda_priv.h => include/sound/hda_register.h | 10 +--
.../sound/hdalib_auto_parser.h | 6 +-
.../hda/hda_beep.h => include/sound/hdalib_beep.h | 6 +-
.../hda_codec.h => include/sound/hdalib_codec.h | 23 +++---
.../sound/hdalib_controller.h | 11 +--
.../hda_local.h => include/sound/hdalib_controls.h | 20 ++---
.../sound/hdalib_generic.h | 6 +-
include/sound/{hda_hwdep.h => hdalib_hwdep.h} | 4 +-
.../hda/hda_jack.h => include/sound/hdalib_jack.h | 6 +-
sound/Kconfig | 2 +
sound/Makefile | 2 +-
sound/hda/Kconfig | 85 ++++++++++++++++++++
sound/hda/Makefile | 21 +++++
sound/{pci => }/hda/hda_intel_trace.h | 0
sound/{pci => }/hda/hda_trace.h | 0
.../hda_auto_parser.c => hda/hdalib_auto_parser.c} | 7 +-
sound/{pci/hda/hda_beep.c => hda/hdalib_beep.c} | 8 +-
sound/{pci/hda/hda_codec.c => hda/hdalib_codec.c} | 21 +++--
.../hda_controller.c => hda/hdalib_controller.c} | 10 ++-
sound/{pci/hda/hda_eld.c => hda/hdalib_eld.c} | 4 +-
.../hda/hda_generic.c => hda/hdalib_generic.c} | 13 +--
sound/{pci/hda/hda_hwdep.c => hda/hdalib_hwdep.c} | 6 +-
sound/{pci/hda/hda_jack.c => hda/hdalib_jack.c} | 8 +-
sound/{pci/hda/hda_proc.c => hda/hdalib_proc.c} | 13 +--
sound/{pci/hda/hda_sysfs.c => hda/hdalib_sysfs.c} | 6 +-
sound/pci/hda/Kconfig | 44 ----------
sound/pci/hda/Makefile | 14 +---
sound/pci/hda/hda_i915.c | 3 +-
sound/pci/hda/hda_intel.c | 7 +-
sound/pci/hda/patch_hdmi.c | 10 ++-
sound/pci/hda/patch_realtek.c | 10 ++-
31 files changed, 229 insertions(+), 157 deletions(-)
rename sound/pci/hda/hda_priv.h => include/sound/hda_register.h (98%)
rename sound/pci/hda/hda_auto_parser.h => include/sound/hdalib_auto_parser.h (96%)
rename sound/pci/hda/hda_beep.h => include/sound/hdalib_beep.h (95%)
rename sound/pci/hda/hda_codec.h => include/sound/hdalib_codec.h (97%)
rename sound/pci/hda/hda_controller.h => include/sound/hdalib_controller.h (90%)
rename sound/pci/hda/hda_local.h => include/sound/hdalib_controls.h (98%)
rename sound/pci/hda/hda_generic.h => include/sound/hdalib_generic.h (99%)
rename include/sound/{hda_hwdep.h => hdalib_hwdep.h} (95%)
rename sound/pci/hda/hda_jack.h => include/sound/hdalib_jack.h (96%)
create mode 100644 sound/hda/Kconfig
create mode 100644 sound/hda/Makefile
rename sound/{pci => }/hda/hda_intel_trace.h (100%)
rename sound/{pci => }/hda/hda_trace.h (100%)
rename sound/{pci/hda/hda_auto_parser.c => hda/hdalib_auto_parser.c} (99%)
rename sound/{pci/hda/hda_beep.c => hda/hdalib_beep.c} (98%)
rename sound/{pci/hda/hda_codec.c => hda/hdalib_codec.c} (99%)
rename sound/{pci/hda/hda_controller.c => hda/hdalib_controller.c} (99%)
rename sound/{pci/hda/hda_eld.c => hda/hdalib_eld.c} (99%)
rename sound/{pci/hda/hda_generic.c => hda/hdalib_generic.c} (99%)
rename sound/{pci/hda/hda_hwdep.c => hda/hdalib_hwdep.c} (96%)
rename sound/{pci/hda/hda_jack.c => hda/hdalib_jack.c} (99%)
rename sound/{pci/hda/hda_proc.c => hda/hdalib_proc.c} (99%)
rename sound/{pci/hda/hda_sysfs.c => hda/hdalib_sysfs.c} (99%)
--
1.7.9.5
2
8
[alsa-devel] [PATCH 1/2] snd-waveartist: Introduce Rockwell WaveArtist RWA010 driver
by Ondrej Zary 08 Mar '15
by Ondrej Zary 08 Mar '15
08 Mar '15
This is a new ALSA driver driver for Rockwell WaveArtist RWA010 chips found
on some (rare) ISA sound cards, such as DCS Multimedia S717.
I wasn't able to get the old OSS WaveArtist driver to work with my card but it
was a great source of information about the chip (as the full datasheet is not
available - only a brief one and a design guide).
However, the OSS driver only supports few of the mixer registers so I had to
install the card in Windows and dump mixer registers while changing the mixer
settings. Then tried what the rest of the registers do and the result is a
fully working mixer which even supports more controls than the Windows driver.
Someone with a NetWinder can add support for it to this driver and then remove
the old OSS one.
Signed-off-by: Ondrej Zary <linux(a)rainbow-software.org>
---
include/sound/mpu401.h | 1 +
sound/isa/Kconfig | 11 +
sound/isa/Makefile | 2 +
sound/isa/waveartist.c | 1399 ++++++++++++++++++++++++++++++++++++++++++++++++
sound/isa/waveartist.h | 74 +++
5 files changed, 1487 insertions(+)
create mode 100644 sound/isa/waveartist.c
create mode 100644 sound/isa/waveartist.h
diff --git a/include/sound/mpu401.h b/include/sound/mpu401.h
index e942096..8fceeba 100644
--- a/include/sound/mpu401.h
+++ b/include/sound/mpu401.h
@@ -44,6 +44,7 @@
#define MPU401_HW_INTEL8X0 17 /* Intel8x0 driver */
#define MPU401_HW_PC98II 18 /* Roland PC98II */
#define MPU401_HW_AUREAL 19 /* Aureal Vortex */
+#define MPU401_HW_WAVEARTIST 20 /* Rockwell WaveArtist */
#define MPU401_INFO_INPUT (1 << 0) /* input stream */
#define MPU401_INFO_OUTPUT (1 << 1) /* output stream */
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 0216475..7a3b4a2 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -454,5 +454,16 @@ config SND_MSND_CLASSIC
To compile this driver as a module, choose M here: the module
will be called snd-msnd-classic.
+config SND_WAVEARTIST
+ tristate "Rockwell WaveArtist RWA010"
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_PCM
+ help
+ Say Y here to include support for Rockwell WaveArtist RWA010 chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-waveartist.
+
endif # SND_ISA
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index 9a15f14..23e11e2 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -12,6 +12,7 @@ snd-es18xx-objs := es18xx.o
snd-opl3sa2-objs := opl3sa2.o
snd-sc6000-objs := sc6000.o
snd-sscape-objs := sscape.o
+snd-waveartist-objs := waveartist.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
obj-$(CONFIG_SND_SC6000) += snd-sc6000.o
obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
+obj-$(CONFIG_SND_WAVEARTIST) += snd-waveartist.o
obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \
sb/ wavefront/ wss/
diff --git a/sound/isa/waveartist.c b/sound/isa/waveartist.c
new file mode 100644
index 0000000..e5bda5c
--- /dev/null
+++ b/sound/isa/waveartist.c
@@ -0,0 +1,1399 @@
+/*
+ * Driver for Rockwell WaveArtist RWA010 soundcards
+ *
+ * Copyright (c) 2015 Ondrej Zary
+ *
+ * HW-related parts based on OSS WaveArtist driver by Hannu Savolainen
+ *
+ * ALSA code based on ES18xx driver by Christian Fischbach & Abramo Bagnara
+ * and also by OPL3-SA2 driver by Jaroslav Kysela
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/isa.h>
+#include <linux/pnp.h>
+#include <linux/isapnp.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+#include "waveartist.h"
+
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("Driver for Rockwell WaveArtist RWA010 sound cards");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+#ifdef CONFIG_PNP
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x250-0x3f0 */
+static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x388-0x3f0 */
+static long midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x300-0x3f0 */
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,10,11 */
+static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for WaveArtist soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for WaveArtist soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable WaveArtist soundcard.");
+#ifdef CONFIG_PNP
+module_param_array(isapnp, bool, NULL, 0444);
+MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
+#endif
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for WaveArtist driver.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port # for WaveArtist driver.");
+module_param_array(midi_port, long, NULL, 0444);
+MODULE_PARM_DESC(midi_port, "MIDI port # for WaveArtist driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for WaveArtist driver.");
+module_param_array(dma1, int, NULL, 0444);
+MODULE_PARM_DESC(dma1, "DMA1 # for WaveArtist driver.");
+module_param_array(dma2, int, NULL, 0444);
+MODULE_PARM_DESC(dma2, "DMA2 # for WaveArtist driver.");
+
+#ifdef CONFIG_PNP
+static int isa_registered;
+static int pnp_registered;
+#endif
+
+#define PFX "waveartist: "
+
+#ifdef CONFIG_PNP
+#define WA_DEVICE(pnpid) { \
+ .id = pnpid, \
+ .devs = { {"RSS5000"}, {"RSS5001"}, {"RSS5002"} } \
+}
+/*
+ * RSS5000 = WaveArtist, RSS5001 = SB, RSS5002 = MPU-401, RSS5003 = IDE,
+ * RSS5004 = gameport, RSS5005 = modem, RSS5006 = 3D
+ */
+
+static struct pnp_card_device_id snd_waveartist_pnpids[] = {
+ WA_DEVICE("RSS5000"), /* 16-bit decode */
+ WA_DEVICE("RSS5100"), /* 16-bit decode + modem */
+ WA_DEVICE("RSS5200"), /* 10-bit decode + modem */
+ WA_DEVICE("RSS5300"), /* 10-bit decode */
+ WA_DEVICE("RSS5400"), /* 16-bit decode + IDE */
+ WA_DEVICE("RSS5500"), /* 16-bit decode + modem + IDE */
+ WA_DEVICE("RSS5600"), /* 10-bit decode + IDE */
+ WA_DEVICE("RSS5700"), /* 10-bit decode + modem + IDE */
+ WA_DEVICE("RSS5800"), /* 10-bit decode + modem + 3D */
+ WA_DEVICE("RSS5900"), /* 10-bit decode + 3D */
+ WA_DEVICE("RSS5A00"), /* 16-bit decode + modem + 3D */
+ WA_DEVICE("RSS5B00"), /* 16-bit decode + 3D */
+ { .id = "" } /* end */
+};
+MODULE_DEVICE_TABLE(pnp_card, snd_waveartist_pnpids);
+#endif /* CONFIG_PNP */
+
+struct snd_waveartist {
+#ifdef CONFIG_PNP
+ struct pnp_dev *wa; /* WaveArtist device */
+ struct pnp_dev *sb; /* SB emulation device */
+ struct pnp_dev *mpu; /* MPU-401 device */
+#endif
+ unsigned long port; /* base port */
+ struct resource *res_port; /* base port resource */
+ int irq;
+ int dma_playback;
+ int dma_capture;
+
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ spinlock_t reg_lock;
+ struct snd_hwdep *synth;
+ struct snd_rawmidi *rmidi;
+
+ u16 image[20]; /* mixer registers image */
+};
+
+static inline void wa_outb(struct snd_waveartist *chip, u8 reg, u8 val)
+{
+ outb(val, chip->port + reg);
+}
+
+static inline u8 wa_inb(struct snd_waveartist *chip, u8 reg)
+{
+ return inb(chip->port + reg);
+}
+
+static inline void wa_outw(struct snd_waveartist *chip, u8 reg, u16 val)
+{
+ outw(val, chip->port + reg);
+}
+
+static inline u16 wa_inw(struct snd_waveartist *chip, u8 reg)
+{
+ return inw(chip->port + reg);
+}
+
+static inline void waveartist_set_ctlr(struct snd_waveartist *chip, u8 clear,
+ u8 set)
+{
+ clear = ~clear & wa_inb(chip, CTLR);
+ wa_outb(chip, CTLR, clear | set);
+}
+
+/* acknowledge IRQ */
+static inline void waveartist_iack(struct snd_waveartist *chip)
+{
+ u8 old_ctlr = wa_inb(chip, CTLR) & ~IRQ_ACK;
+
+ wa_outb(chip, CTLR, old_ctlr | IRQ_ACK);
+ wa_outb(chip, CTLR, old_ctlr);
+}
+
+static int waveartist_reset(struct snd_waveartist *chip)
+{
+ unsigned int timeout, res = -1;
+
+ waveartist_set_ctlr(chip, -1, RESET);
+ msleep(200);
+ waveartist_set_ctlr(chip, RESET, 0);
+
+ timeout = 500;
+ do {
+ mdelay(2);
+
+ if (wa_inb(chip, STATR) & CMD_RF) {
+ res = wa_inw(chip, CMDR);
+ if (res == 0x55aa)
+ break;
+ }
+ } while (--timeout);
+
+ if (timeout == 0) {
+ dev_warn(chip->card->dev, "WaveArtist: reset timeout (res=0x%x)\n",
+ res);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Helper function to send and receive words
+ * from WaveArtist. It handles all the handshaking
+ * and can send or receive multiple words.
+ */
+static int waveartist_cmd(struct snd_waveartist *chip,
+ int nr_cmd, u16 *cmd,
+ int nr_resp, u16 *resp)
+{
+ unsigned long flags;
+ unsigned int timed_out = 0, i;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ /*
+ * The chip can hang if we access the STATR register too quickly
+ * after a write. Do a dummy read to slow down.
+ */
+ wa_inb(chip, CTLR);
+
+ if (wa_inb(chip, STATR) & CMD_RF) {
+ /* flush the port */
+ wa_inw(chip, CMDR);
+ udelay(10);
+ }
+
+ for (i = 0; !timed_out && i < nr_cmd; i++) {
+ int count;
+
+ for (count = 5000; count; count--)
+ if (wa_inb(chip, STATR) & CMD_WE)
+ break;
+
+ if (!count)
+ timed_out = 1;
+ else
+ wa_outw(chip, CMDR, cmd[i]);
+ /* Another dummy read */
+ wa_inb(chip, CTLR);
+ }
+
+ for (i = 0; !timed_out && i < nr_resp; i++) {
+ int count;
+
+ for (count = 5000; count; count--)
+ if (wa_inb(chip, STATR) & CMD_RF)
+ break;
+
+ if (!count)
+ timed_out = 1;
+ else
+ resp[i] = wa_inw(chip, CMDR);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+ return timed_out ? 1 : 0;
+}
+
+/* Send one command word */
+static inline int waveartist_cmd1(struct snd_waveartist *chip, u16 cmd)
+{
+ return waveartist_cmd(chip, 1, &cmd, 0, NULL);
+}
+
+/* Send one command, receive one word */
+static inline u16 waveartist_cmd1_r(struct snd_waveartist *chip, u16 cmd)
+{
+ u16 ret;
+
+ waveartist_cmd(chip, 1, &cmd, 1, &ret);
+
+ return ret;
+}
+
+/* Send a double command, receive one word (and throw it away) */
+static inline int waveartist_cmd2(struct snd_waveartist *chip, u16 cmd, u16 arg)
+{
+ u16 vals[2] = { cmd, arg };
+
+ return waveartist_cmd(chip, 2, vals, 1, vals);
+}
+
+/* Send a triple command */
+static inline int waveartist_cmd3(struct snd_waveartist *chip, u16 cmd,
+ u16 arg1, u16 arg2)
+{
+ u16 vals[3] = { cmd, arg1, arg2 };
+
+ return waveartist_cmd(chip, 3, vals, 0, NULL);
+}
+
+static u16 waveartist_getrev(struct snd_waveartist *chip)
+{
+ u16 temp[2];
+ u16 cmd = WACMD_GETREV;
+
+ waveartist_cmd(chip, 1, &cmd, 2, temp);
+
+ return temp[0];
+}
+
+static irqreturn_t snd_waveartist_interrupt(int irq, void *dev_id)
+{
+ u8 status, irqstatus;
+ struct snd_card *card = dev_id;
+ struct snd_waveartist *chip;
+
+ if (card == NULL)
+ return IRQ_NONE;
+
+ chip = card->private_data;
+
+ irqstatus = wa_inb(chip, IRQSTAT);
+ status = wa_inb(chip, STATR);
+
+ if (status & IRQ_REQ) /* clear interrupt */
+ waveartist_iack(chip);
+
+ if (irqstatus & IRQ_PCM) { /* PCM buffer done */
+ if ((status & DMA1) && chip->playback_substream)
+ snd_pcm_period_elapsed(chip->playback_substream);
+ if ((status & DMA0) && chip->capture_substream)
+ snd_pcm_period_elapsed(chip->capture_substream);
+ if (!(status & (DMA0 | DMA1)))
+ dev_warn(chip->card->dev, "Unknown PCM interrupt\n");
+ }
+
+ if (irqstatus & IRQ_SB) /* we do not use SB mode */
+ dev_warn(chip->card->dev, "Unexpected SB interrupt\n");
+
+ if ((irqstatus & IRQ_MPU) && chip->rmidi)
+ snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+static int snd_waveartist_suspend(struct snd_card *card, pm_message_t state)
+{
+ struct snd_waveartist *chip = card->private_data;
+ int i;
+
+ if (!card)
+ return 0;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend_all(chip->pcm);
+
+ /* save mixer registers */
+ for (i = 0; i < ARRAY_SIZE(chip->image); i++)
+ chip->image[i] = waveartist_cmd1_r(chip,
+ WACMD_GET_LEVEL | i << 8);
+
+ return 0;
+}
+
+static int snd_waveartist_resume(struct snd_card *card)
+{
+ struct snd_waveartist *chip;
+ int i;
+
+ if (!card)
+ return 0;
+
+ chip = card->private_data;
+
+ /* restore mixer registers */
+ for (i = 0; i < 10; i += 2)
+ waveartist_cmd3(chip, WACMD_SET_MIXER,
+ chip->image[i], chip->image[i + 1]);
+ for (i = 10; i < ARRAY_SIZE(chip->image); i += 2)
+ waveartist_cmd3(chip, WACMD_SET_LEVEL |
+ ((i - 10) / 2) << 8,
+ chip->image[i], chip->image[i + 1]);
+
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PNP
+static void snd_waveartist_set_irq(struct pnp_dev *pdev, int irq)
+{
+ if (!pdev->active)
+ return;
+ isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
+ isapnp_write_byte(0x70, irq); /* ISAPNP_CFG_IRQ */
+ isapnp_cfg_end();
+}
+
+static int snd_waveartist_pnp(int dev, struct snd_waveartist *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
+{
+ chip->wa = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (!chip->wa)
+ return -EBUSY;
+
+ chip->sb = pnp_request_card_device(card, id->devs[1].id, NULL);
+ if (!chip->sb)
+ return -EBUSY;
+
+ chip->mpu = pnp_request_card_device(card, id->devs[2].id, NULL);
+ if (!chip->mpu)
+ return -EBUSY;
+
+ if (pnp_activate_dev(chip->wa) < 0) {
+ dev_err(chip->card->dev, "WA PnP configure failure\n");
+ return -EBUSY;
+ }
+ if (pnp_activate_dev(chip->sb) < 0) {
+ dev_err(chip->card->dev, "SB PnP configure failure\n");
+ return -EBUSY;
+ }
+ port[dev] = pnp_port_start(chip->wa, 0);
+ dma2[dev] = pnp_dma(chip->wa, 0);
+ fm_port[dev] = pnp_port_start(chip->sb, 1);
+ dma1[dev] = pnp_dma(chip->sb, 0);
+ irq[dev] = pnp_irq(chip->sb, 0);
+
+ /*
+ * The card uses only one IRQ (listed in the resources of SB device)
+ * which needs to be shared by WaveArtist and MPU-401 devices. They
+ * don't have an IRQ resource so it must be forced.
+ */
+ snd_waveartist_set_irq(chip->wa, irq[dev]);
+
+ /* allocate MPU-401 resources */
+ if (pnp_activate_dev(chip->mpu) < 0)
+ dev_err(chip->card->dev, "MPU-401 PnP configure failure: will be disabled\n");
+ else {
+ midi_port[dev] = pnp_port_start(chip->mpu, 0);
+ snd_waveartist_set_irq(chip->mpu, irq[dev]);
+ }
+
+ dev_dbg(chip->card->dev, "PnP WaveArtist: port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
+ port[dev], fm_port[dev], midi_port[dev]);
+ dev_dbg(chip->card->dev, "PnP WaveArtist: dma1=%i, dma2=%i, irq=%i\n",
+ dma1[dev], dma2[dev], irq[dev]);
+
+ return 0;
+}
+#endif /* CONFIG_PNP */
+
+static int snd_waveartist_playback_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int snd_waveartist_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static enum wa_format waveartist_format(snd_pcm_format_t format)
+{
+ if (snd_pcm_format_width(format) == 16)
+ return WA_FMT_S16;
+ if (snd_pcm_format_unsigned(format))
+ return WA_FMT_U8;
+ else
+ return WA_FMT_S8;
+}
+
+static u16 waveartist_rate(struct snd_pcm_runtime *runtime)
+{
+ return (runtime->rate << 16) / 44100;
+}
+
+static int snd_waveartist_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ /* Set rate */
+ if (waveartist_cmd2(chip, WACMD_OUTPUTSPEED, waveartist_rate(runtime)))
+ dev_warn(chip->card->dev, "error setting playback rate %dHz\n",
+ runtime->rate);
+ /* Set channel count */
+ if (waveartist_cmd2(chip, WACMD_OUTPUTCHANNELS, runtime->channels))
+ dev_warn(chip->card->dev, "error setting playback %d channels\n",
+ runtime->channels);
+ /* Set DMA channel */
+ if (waveartist_cmd2(chip, WACMD_OUTPUTDMA,
+ chip->dma_playback > 3 ? WA_DMA_16BIT : WA_DMA_8BIT))
+ dev_warn(chip->card->dev, "error setting playback data path\n");
+ /* Set format */
+ if (waveartist_cmd2(chip, WACMD_OUTPUTFORMAT,
+ waveartist_format(runtime->format)))
+ dev_warn(chip->card->dev, "error setting playback format %d\n",
+ runtime->format);
+ /* Set sample count */
+ if (waveartist_cmd2(chip, WACMD_OUTPUTSIZE, count - 1))
+ dev_warn(chip->card->dev, "error setting playback count %d\n",
+ count);
+ /* Configure DMA controller */
+ snd_dma_program(chip->dma_playback, runtime->dma_addr, size,
+ DMA_MODE_WRITE | DMA_AUTOINIT);
+
+ return 0;
+}
+
+static int snd_waveartist_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ waveartist_cmd1(chip, WACMD_OUTPUTSTART);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ waveartist_cmd1(chip, WACMD_OUTPUTSTOP);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ waveartist_cmd1(chip, WACMD_OUTPUTPAUSE);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ waveartist_cmd1(chip, WACMD_OUTPUTRESUME);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int snd_waveartist_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int snd_waveartist_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+ /* Set rate */
+ if (waveartist_cmd2(chip, WACMD_INPUTSPEED, waveartist_rate(runtime)))
+ dev_warn(chip->card->dev, "error setting capture rate %dHz\n",
+ runtime->rate);
+ /* Set channel count */
+ if (waveartist_cmd2(chip, WACMD_INPUTCHANNELS, runtime->channels))
+ dev_warn(chip->card->dev, "error setting capture %d channels\n",
+ runtime->channels);
+ /* Set DMA channel */
+ if (waveartist_cmd2(chip, WACMD_INPUTDMA,
+ chip->dma_capture > 3 ? WA_DMA_16BIT : WA_DMA_8BIT))
+ dev_warn(chip->card->dev, "error setting capture data path\n");
+ /* Set format */
+ if (waveartist_cmd2(chip, WACMD_INPUTFORMAT,
+ waveartist_format(runtime->format)))
+ dev_warn(chip->card->dev, "error setting capture format %d\n",
+ runtime->format);
+ /* Set sample count */
+ if (waveartist_cmd2(chip, WACMD_INPUTSIZE, count - 1))
+ dev_warn(chip->card->dev, "error setting capture count %d\n",
+ count);
+ /* Configure DMA controller */
+ snd_dma_program(chip->dma_capture, runtime->dma_addr, size,
+ DMA_MODE_READ | DMA_AUTOINIT);
+
+ return 0;
+}
+
+static int snd_waveartist_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ waveartist_cmd1(chip, WACMD_INPUTSTART);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ waveartist_cmd1(chip, WACMD_INPUTSTOP);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ waveartist_cmd1(chip, WACMD_INPUTPAUSE);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ waveartist_cmd1(chip, WACMD_INPUTRESUME);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_waveartist_playback_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+ size_t size = snd_pcm_lib_buffer_bytes(substream);
+ size_t ptr = snd_dma_pointer(chip->dma_playback, size);
+
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_waveartist_capture_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+ size_t size = snd_pcm_lib_buffer_bytes(substream);
+ size_t ptr = snd_dma_pointer(chip->dma_capture, size);
+
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static struct snd_pcm_hardware snd_waveartist_playback = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS |
+ SNDRV_PCM_RATE_8000_44100,
+ .rate_min = 4000,
+ .rate_max = 44100,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+static struct snd_pcm_hardware snd_waveartist_capture = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS |
+ SNDRV_PCM_RATE_8000_44100,
+ .rate_min = 4000,
+ .rate_max = 44100,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+static int snd_waveartist_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+
+ chip->playback_substream = substream;
+ substream->runtime->hw = snd_waveartist_playback;
+
+ snd_pcm_limit_isa_dma_size(chip->dma_playback,
+ &substream->runtime->hw.buffer_bytes_max);
+ snd_pcm_limit_isa_dma_size(chip->dma_playback,
+ &substream->runtime->hw.period_bytes_max);
+
+ return 0;
+}
+
+static int snd_waveartist_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+
+ chip->capture_substream = substream;
+ substream->runtime->hw = snd_waveartist_capture;
+
+ snd_pcm_limit_isa_dma_size(chip->dma_capture,
+ &substream->runtime->hw.buffer_bytes_max);
+ snd_pcm_limit_isa_dma_size(chip->dma_capture,
+ &substream->runtime->hw.period_bytes_max);
+
+ return 0;
+}
+
+static int snd_waveartist_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+
+ chip->playback_substream = NULL;
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+static int snd_waveartist_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_waveartist *chip = snd_pcm_substream_chip(substream);
+
+ chip->capture_substream = NULL;
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+static struct snd_pcm_ops snd_waveartist_playback_ops = {
+ .open = snd_waveartist_playback_open,
+ .close = snd_waveartist_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_waveartist_playback_hw_params,
+ .hw_free = snd_waveartist_pcm_hw_free,
+ .prepare = snd_waveartist_playback_prepare,
+ .trigger = snd_waveartist_playback_trigger,
+ .pointer = snd_waveartist_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_waveartist_capture_ops = {
+ .open = snd_waveartist_capture_open,
+ .close = snd_waveartist_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_waveartist_capture_hw_params,
+ .hw_free = snd_waveartist_pcm_hw_free,
+ .prepare = snd_waveartist_capture_prepare,
+ .trigger = snd_waveartist_capture_trigger,
+ .pointer = snd_waveartist_capture_pointer,
+};
+
+static int snd_waveartist_pcm(struct snd_card *card)
+{
+ struct snd_waveartist *chip = card->private_data;
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(card, "WaveArtist", 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_waveartist_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_waveartist_capture_ops);
+
+ /* global setup */
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ if (chip->dma_playback == chip->dma_capture)
+ pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
+ strcpy(pcm->name, card->shortname);
+ chip->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_isa_data(), 64 * 1024,
+ chip->dma_playback > 3 || chip->dma_capture > 3 ?
+ 128 * 1024 : 64 * 1024);
+
+ return 0;
+}
+
+static void snd_waveartist_free(struct snd_card *card)
+{
+ struct snd_waveartist *chip = card->private_data;
+
+ release_and_free_resource(chip->res_port);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, card);
+ if (chip->dma_playback >= 0) {
+ disable_dma(chip->dma_playback);
+ free_dma(chip->dma_playback);
+ }
+ if (chip->dma_capture >= 0 && chip->dma_capture != chip->dma_playback) {
+ disable_dma(chip->dma_capture);
+ free_dma(chip->dma_capture);
+ }
+}
+
+static int snd_waveartist_dev_free(struct snd_device *device)
+{
+ snd_waveartist_free(device->card);
+
+ return 0;
+}
+
+static int snd_waveartist_init(struct snd_waveartist *chip)
+{
+ if (waveartist_reset(chip))
+ return -ENODEV;
+ dev_dbg(chip->card->dev, "chip rev. 0x%x\n", waveartist_getrev(chip));
+
+ waveartist_cmd1(chip, WACMD_RST_MIXER); /* reset mixer */
+ waveartist_iack(chip); /* clear any pending interrupt */
+ waveartist_set_ctlr(chip, 0, DMA1_IE | DMA0_IE); /* enable DMA IRQs */
+ waveartist_iack(chip); /* clear any pending interrupt */
+
+ return 0;
+}
+
+static int snd_waveartist_new_device(struct snd_card *card,
+ unsigned long port,
+ unsigned long mpu_port,
+ unsigned long fm_port,
+ int irq, int dma1, int dma2)
+{
+ struct snd_waveartist *chip = card->private_data;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_waveartist_dev_free,
+ };
+ int err;
+
+ spin_lock_init(&chip->reg_lock);
+ chip->card = card;
+ chip->port = port;
+ chip->irq = -1;
+ chip->dma_playback = -1;
+ chip->dma_capture = -1;
+
+ chip->res_port = request_region(port, 16, "WaveArtist");
+ if (!chip->res_port) {
+ snd_waveartist_free(card);
+ dev_err(chip->card->dev, "unable to grab ports 0x%lx-0x%lx\n",
+ port, port + 16 - 1);
+ return -EBUSY;
+ }
+
+ if (snd_waveartist_init(chip) < 0) {
+ snd_waveartist_free(card);
+ return -ENODEV;
+ }
+
+ if (request_irq(irq, snd_waveartist_interrupt, 0, "WaveArtist", card)) {
+ snd_waveartist_free(card);
+ dev_err(chip->card->dev, "unable to grab IRQ %d\n", irq);
+ return -EBUSY;
+ }
+ chip->irq = irq;
+
+ if (dma1 == dma2 || dma1 == SNDRV_AUTO_DMA || dma2 == SNDRV_AUTO_DMA) {
+ /* we have only one DMA channel */
+ if (dma1 == SNDRV_AUTO_DMA)
+ dma1 = dma2;
+ if (request_dma(dma1, "WaveArtist")) {
+ snd_waveartist_free(card);
+ dev_err(chip->card->dev, "unable to grab DMA %d\n",
+ dma1);
+ return -EBUSY;
+ }
+ chip->dma_playback = chip->dma_capture = dma1;
+ } else {
+ /*
+ * 2 channels: use 16-bit for playback and 8-bit for capture as
+ * full-duplex works better this way. However, the chip seems to
+ * have some band-width limit so full-duplex at 44kHz/16-bit/2ch
+ * is not possible - some frames are dropped during capture,
+ * resulting in too-fast recording. If capture is done using
+ * lower rate or 8-bit or mono, everything is fine.
+ */
+ if (dma2 > 3)
+ swap(dma1, dma2);
+
+ if (request_dma(dma1, "WaveArtist playback")) {
+ snd_waveartist_free(card);
+ dev_err(chip->card->dev, "unable to grab playback DMA %d\n",
+ dma1);
+ return -EBUSY;
+ }
+ chip->dma_playback = dma1;
+
+ if (dma2 != dma1 && request_dma(dma2, "WaveArtist capture")) {
+ snd_waveartist_free(card);
+ dev_err(chip->card->dev, "unable to grab capture DMA %d\n",
+ dma2);
+ return -EBUSY;
+ }
+ chip->dma_capture = dma2;
+ }
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_waveartist_free(card);
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_waveartist_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
+{
+ return snd_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_waveartist), cardp);
+}
+
+static int snd_waveartist_info_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+ SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+
+ return 0;
+}
+
+static int snd_waveartist_get_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_waveartist *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ u16 val;
+
+ val = waveartist_cmd1_r(chip, WACMD_GET_LEVEL | reg << 8);
+ ucontrol->value.integer.value[0] = (val >> shift) & mask;
+
+ return 0;
+}
+
+static int snd_waveartist_put_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_waveartist *chip = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0xff;
+ int mask = (kcontrol->private_value >> 16) & 0xff;
+ u16 val, old_val, new_val;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ mask <<= shift;
+ val <<= shift;
+
+ old_val = waveartist_cmd1_r(chip, WACMD_GET_LEVEL | reg << 8);
+ new_val = (old_val & ~mask) | (val & mask);
+
+ if (new_val == old_val)
+ return 0;
+
+ /* new_val already contains register number (bits 12..15), bit 11 set */
+ waveartist_cmd3(chip, WACMD_SET_MIXER, new_val, new_val);
+
+ return 1;
+}
+
+static int snd_waveartist_info_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+ SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask ? mask : 0x7fff;
+
+ return 0;
+}
+
+static int snd_waveartist_get_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_waveartist *chip = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x0f;
+ int shift_right = (kcontrol->private_value >> 20) & 0x0f;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ u16 left, right;
+
+ if (mask == 0)
+ mask = 0x7fff;
+
+ left = waveartist_cmd1_r(chip, WACMD_GET_LEVEL | left_reg << 8);
+ if (left_reg != right_reg)
+ right = waveartist_cmd1_r(chip, WACMD_GET_LEVEL |
+ right_reg << 8);
+ else
+ right = left;
+
+ ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+ ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+
+ return 0;
+}
+
+static int snd_waveartist_put_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_waveartist *chip = snd_kcontrol_chip(kcontrol);
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int shift_left = (kcontrol->private_value >> 16) & 0x0f;
+ int shift_right = (kcontrol->private_value >> 20) & 0x0f;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ u16 val1, val2, mask1, mask2;
+ u16 old_left, old_right, new_left, new_right;
+
+ if (mask == 0)
+ mask = 0x7fff;
+
+ val1 = ucontrol->value.integer.value[0] & mask;
+ val2 = ucontrol->value.integer.value[1] & mask;
+ val1 <<= shift_left;
+ val2 <<= shift_right;
+ mask1 = mask << shift_left;
+ mask2 = mask << shift_right;
+
+ old_left = waveartist_cmd1_r(chip, WACMD_GET_LEVEL | left_reg << 8);
+ if (left_reg != right_reg)
+ old_right = waveartist_cmd1_r(chip, WACMD_GET_LEVEL |
+ right_reg << 8);
+ else
+ old_right = old_left;
+
+ new_left = (old_left & ~mask1) | (val1 & mask1);
+ new_right = (old_right & ~mask2) | (val2 & mask2);
+
+ if (new_left == old_left && new_right == old_right)
+ return 0;
+
+ if (left_reg < 10)
+ /* new_* already contain reg. num. (bits 12..15), bit 11 set */
+ waveartist_cmd3(chip, WACMD_SET_MIXER, new_left, new_right);
+ else
+ waveartist_cmd3(chip, WACMD_SET_LEVEL |
+ ((left_reg - 10) / 2) << 8,
+ new_left, new_right);
+
+ return 1;
+}
+
+static int snd_waveartist_info_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const mux_texts[] = {
+ "None", "Mix", "Line", "Phone", "CD", "Mic"
+ };
+
+ return snd_ctl_enum_info(uinfo, 2, ARRAY_SIZE(mux_texts), mux_texts);
+}
+
+static int snd_waveartist_get_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_waveartist *chip = snd_kcontrol_chip(kcontrol);
+ int mux = waveartist_cmd1_r(chip, WACMD_GET_LEVEL | 0x08 << 8);
+
+ ucontrol->value.enumerated.item[0] = mux & 0x07;
+ ucontrol->value.enumerated.item[1] = (mux >> 3) & 0x07;
+
+ return 0;
+}
+
+static int snd_waveartist_put_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_waveartist *chip = snd_kcontrol_chip(kcontrol);
+ u16 old_val, new_val;
+ u16 val1 = ucontrol->value.enumerated.item[0];
+ u16 val2 = ucontrol->value.enumerated.item[1];
+
+ old_val = waveartist_cmd1_r(chip, WACMD_GET_LEVEL | 0x08 << 8);
+ new_val = (old_val & ~0x3f) | (val1 & 0x07) | ((val2 & 0x07) << 3);
+ if (new_val == old_val)
+ return 0;
+
+ /* new_val already contains register number (bits 12..15), bit 11 set */
+ waveartist_cmd3(chip, WACMD_SET_MIXER, new_val, new_val);
+
+ return 1;
+}
+
+#define WAVEARTIST_SINGLE(xname, reg, shift, mask) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_waveartist_info_single, \
+ .get = snd_waveartist_get_single, .put = snd_waveartist_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) \
+}
+
+#define WAVEARTIST_DOUBLE(xname, left_reg, right_reg, shift_left, shift_right, \
+ mask) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_waveartist_info_double, \
+ .get = snd_waveartist_get_double, .put = snd_waveartist_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+ (shift_right << 20) | (mask << 24) \
+}
+
+static struct snd_kcontrol_new snd_waveartist_controls[] = {
+WAVEARTIST_DOUBLE("Master Playback Volume", 2, 6, 1, 1, 0x07),
+WAVEARTIST_DOUBLE("Master Playback Switch", 0, 4, 0, 0, 1),
+WAVEARTIST_DOUBLE("PCM Playback Volume", 10, 11, 0, 0, 0x00),/* 0x00 = 0x7fff */
+WAVEARTIST_DOUBLE("FM Playback Volume", 12, 13, 0, 0, 0x00),/* 0x00 = 0x7fff */
+/* WAVEARTIST_DOUBLE("Wavetable? Playback Volume", 14, 15, 0, 0, 0x00), */
+/* WAVEARTIST_DOUBLE("? Playback Volume", 16, 17, 0, 0, 0x00), */
+/* WAVEARTIST_DOUBLE("? Playback Volume", 18, 19, 0, 0, 0x00), */
+WAVEARTIST_DOUBLE("Digital Playback Switch", 3, 7, 10, 10, 1), /* PCM + FM */
+WAVEARTIST_DOUBLE("CD Playback Volume", 0, 4, 1, 1, 0x1f),
+WAVEARTIST_DOUBLE("CD Playback Switch", 3, 7, 6, 6, 1),
+WAVEARTIST_DOUBLE("Line Playback Volume", 0, 4, 6, 6, 0x1f),
+WAVEARTIST_DOUBLE("Line Playback Switch", 3, 7, 4, 4, 1),
+WAVEARTIST_DOUBLE("Phone Playback Volume", 1, 5, 6, 6, 0x1f),
+WAVEARTIST_DOUBLE("Phone Playback Switch", 3, 7, 5, 5, 1),
+WAVEARTIST_SINGLE("Mono Playback Volume", 8, 5, 0x1f),
+WAVEARTIST_DOUBLE("Mono Playback Switch", 3, 7, 9, 9, 1),
+WAVEARTIST_DOUBLE("Mic Playback Volume", 2, 6, 6, 6, 0x1f),
+WAVEARTIST_DOUBLE("Mic 2 Playback Volume", 1, 5, 1, 1, 0x1f),
+WAVEARTIST_DOUBLE("Mic Playback Switch", 3, 7, 7, 7, 1),
+WAVEARTIST_DOUBLE("Mic 2 Playback Switch", 3, 7, 8, 8, 1),
+WAVEARTIST_DOUBLE("Mic Gain", 2, 6, 4, 4, 0x03),
+WAVEARTIST_DOUBLE("Capture Volume", 3, 7, 0, 0, 0x0f),
+WAVEARTIST_SINGLE("Mono Output Playback Switch", 1, 0, 1),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_waveartist_info_mux,
+ .get = snd_waveartist_get_mux,
+ .put = snd_waveartist_put_mux,
+}
+};
+
+static int snd_waveartist_mixer(struct snd_card *card)
+{
+ struct snd_waveartist *chip = card->private_data;
+ int err;
+ unsigned int idx;
+
+ strcpy(card->mixername, chip->pcm->name);
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_waveartist_controls); idx++) {
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_ctl_new1(&snd_waveartist_controls[idx], chip);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_waveartist_probe(struct snd_card *card, int dev)
+{
+ struct snd_waveartist *chip = card->private_data;
+ struct snd_opl3 *opl3;
+ int err;
+
+ err = snd_waveartist_new_device(card,
+ port[dev], midi_port[dev], fm_port[dev],
+ irq[dev], dma1[dev], dma2[dev]);
+ if (err < 0)
+ return err;
+
+ strcpy(card->driver, "WaveArtist");
+ strcpy(card->shortname, "Rockwell WaveArtist RWA010");
+ sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d",
+ card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
+
+ err = snd_waveartist_pcm(card);
+ if (err < 0)
+ return err;
+ err = snd_waveartist_mixer(card);
+ if (err < 0)
+ return err;
+
+ if (fm_port[dev] >= 0x388 && fm_port[dev] < 0x3f0) {
+ err = snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
+ OPL3_HW_OPL3, 0, &opl3);
+ if (err < 0)
+ return err;
+ err = snd_opl3_timer_new(opl3, 1, 2);
+ if (err < 0)
+ return err;
+ err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth);
+ if (err < 0)
+ return err;
+ }
+ if (midi_port[dev] >= 0x300 && midi_port[dev] < 0x3f0) {
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_WAVEARTIST,
+ midi_port[dev], MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
+ if (err < 0)
+ return err;
+ }
+
+ return snd_card_register(card);
+}
+
+static int snd_waveartist_isa_match(struct device *pdev,
+ unsigned int dev)
+{
+ if (!enable[dev])
+ return 0;
+#ifdef CONFIG_PNP
+ if (isapnp[dev])
+ return 0;
+#endif
+ if (port[dev] == SNDRV_AUTO_PORT) {
+ dev_err(pdev, "specify port\n");
+ return 0;
+ }
+ if (irq[dev] == SNDRV_AUTO_IRQ) {
+ dev_err(pdev, "specify irq\n");
+ return 0;
+ }
+ if (dma1[dev] == SNDRV_AUTO_DMA) {
+ dev_err(pdev, "specify dma1\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int snd_waveartist_isa_probe(struct device *pdev,
+ unsigned int dev)
+{
+ struct snd_card *card;
+ int err;
+
+ err = snd_waveartist_card_new(pdev, dev, &card);
+ if (err < 0)
+ return err;
+ err = snd_waveartist_probe(card, dev);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ dev_set_drvdata(pdev, card);
+
+ return 0;
+}
+
+static int snd_waveartist_isa_remove(struct device *devptr,
+ unsigned int dev)
+{
+ snd_card_free(dev_get_drvdata(devptr));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_waveartist_isa_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ return snd_waveartist_suspend(dev_get_drvdata(dev), state);
+}
+
+static int snd_waveartist_isa_resume(struct device *dev, unsigned int n)
+{
+ return snd_waveartist_resume(dev_get_drvdata(dev));
+}
+#endif
+
+static struct isa_driver waveartist_isa_driver = {
+ .match = snd_waveartist_isa_match,
+ .probe = snd_waveartist_isa_probe,
+ .remove = snd_waveartist_isa_remove,
+#ifdef CONFIG_PM
+ .suspend = snd_waveartist_isa_suspend,
+ .resume = snd_waveartist_isa_resume,
+#endif
+ .driver = { .name = "waveartist" },
+};
+
+#ifdef CONFIG_PNP
+static int snd_waveartist_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
+{
+ static int dev;
+ int err;
+ struct snd_card *card;
+
+ for (; dev < SNDRV_CARDS; dev++) {
+ if (enable[dev] && isapnp[dev])
+ break;
+ }
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+
+ err = snd_waveartist_card_new(&pcard->card->dev, dev, &card);
+ if (err < 0)
+ return err;
+ err = snd_waveartist_pnp(dev, card->private_data, pcard, pid);
+ if (err < 0) {
+ dev_err(&pcard->card->dev, "PnP detection failed\n");
+ snd_card_free(card);
+ return err;
+ }
+ err = snd_waveartist_probe(card, dev);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pnp_set_card_drvdata(pcard, card);
+ dev++;
+
+ return 0;
+}
+
+static void snd_waveartist_pnp_remove(struct pnp_card_link *pcard)
+{
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+ struct snd_waveartist *chip = card->private_data;
+
+ /* disable forced IRQs */
+ snd_waveartist_set_irq(chip->wa, 0);
+ snd_waveartist_set_irq(chip->mpu, 0);
+
+ snd_card_free(pnp_get_card_drvdata(pcard));
+ pnp_set_card_drvdata(pcard, NULL);
+}
+
+#ifdef CONFIG_PM
+static int snd_waveartist_pnp_suspend(struct pnp_card_link *pcard,
+ pm_message_t state)
+{
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+ struct snd_waveartist *chip = card->private_data;
+
+ snd_waveartist_suspend(card, state);
+
+ /* disable forced IRQs to prevent opps */
+ snd_waveartist_set_irq(chip->wa, 0);
+ snd_waveartist_set_irq(chip->mpu, 0);
+
+ return 0;
+}
+static int snd_waveartist_pnp_resume(struct pnp_card_link *pcard)
+{
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+ struct snd_waveartist *chip = card->private_data;
+
+ /* re-enable forced IRQs */
+ snd_waveartist_set_irq(chip->wa, chip->irq);
+ snd_waveartist_set_irq(chip->mpu, chip->irq);
+
+ return snd_waveartist_resume(pnp_get_card_drvdata(pcard));
+}
+#endif
+
+static struct pnp_card_driver waveartist_pnpc_driver = {
+ .flags = PNP_DRIVER_RES_DISABLE,
+ .name = "waveartist",
+ .id_table = snd_waveartist_pnpids,
+ .probe = snd_waveartist_pnp_detect,
+ .remove = snd_waveartist_pnp_remove,
+#ifdef CONFIG_PM
+ .suspend = snd_waveartist_pnp_suspend,
+ .resume = snd_waveartist_pnp_resume,
+#endif
+};
+#endif /* CONFIG_PNP */
+
+static int __init alsa_card_waveartist_init(void)
+{
+ int err;
+
+ err = isa_register_driver(&waveartist_isa_driver, SNDRV_CARDS);
+#ifdef CONFIG_PNP
+ if (!err)
+ isa_registered = 1;
+
+ err = pnp_register_card_driver(&waveartist_pnpc_driver);
+ if (!err)
+ pnp_registered = 1;
+
+ if (isa_registered)
+ err = 0;
+#endif
+ return err;
+}
+
+static void __exit alsa_card_waveartist_exit(void)
+{
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ pnp_unregister_card_driver(&waveartist_pnpc_driver);
+
+ if (isa_registered)
+#endif
+ isa_unregister_driver(&waveartist_isa_driver);
+}
+
+module_init(alsa_card_waveartist_init)
+module_exit(alsa_card_waveartist_exit)
diff --git a/sound/isa/waveartist.h b/sound/isa/waveartist.h
new file mode 100644
index 0000000..72254cf
--- /dev/null
+++ b/sound/isa/waveartist.h
@@ -0,0 +1,74 @@
+/*
+ * Rockwell WaveArtist RWA010 chip register definitions
+ *
+ * Based on OSS WaveArtist driver by Hannu Savolainen
+ */
+
+/* registers */
+#define CMDR 0 /* command (16-bit) */
+#define DATR 2 /* data (for PIO?) (16-bit) */
+#define CTLR 4 /* control */
+#define STATR 5 /* status */
+#define EXPCR1 6 /* expansion control 1 */
+#define EXPCR2 7 /* expansion control 2 */
+#define EXPDAT1 8 /* expansion data 1 (16-bit) */
+#define EXPDAT2 10 /* expansion data 2 (16-bit) */
+#define IRQSTAT 12 /* IRQ status */
+
+/* STATR register bit definitions */
+#define CMD_WE BIT(7) /* CMDR write empty (ready for write) */
+#define CMD_RF BIT(6) /* CMDR read full (ready for read) */
+#define DAT_WE BIT(5) /* DATR write empty (ready for write) */
+#define DAT_RF BIT(4) /* DATR read full (ready for read) */
+#define IRQ_REQ BIT(3) /* IRQ was requested */
+#define DMA1 BIT(2) /* DMA1 IRQ was requested */
+#define DMA0 BIT(1) /* DMA0 IRQ was requested */
+
+/* CTLR register bit definitions */
+#define CMD_WEIE BIT(7) /* CMDR write empty interrupt enable */
+#define CMD_RFIE BIT(6) /* CMDR read full interrupt enable */
+#define DAT_WEIE BIT(5) /* DATR write empty interrupt enable */
+#define DAT_RFIE BIT(4) /* DATR read full interrupt enable */
+#define RESET BIT(3) /* chip reset */
+#define DMA1_IE BIT(2) /* DMA1 interrupt enable */
+#define DMA0_IE BIT(1) /* DMA0 interrupt enable */
+#define IRQ_ACK BIT(0) /* IRQ acknowlege */
+
+/* IRQSTAT register bit definitions */
+#define IRQ_MPU BIT(2) /* MPU-401 */
+#define IRQ_SB BIT(1) /* SB emulation */
+#define IRQ_PCM BIT(0) /* native PCM */
+
+/* commands */
+#define WACMD_GETREV 0x00
+
+#define WACMD_INPUTFORMAT 0x10 /* 0=S8, 1=S16, 2=U8 */
+#define WACMD_INPUTCHANNELS 0x11 /* 1=mono, 2=Stereo */
+#define WACMD_INPUTSPEED 0x12 /* sampling rate */
+#define WACMD_INPUTDMA 0x13 /* 0=8bit, 1=16bit, 2=PIO */
+#define WACMD_INPUTSIZE 0x14 /* samples to interrupt */
+#define WACMD_INPUTSTART 0x15 /* start ADC */
+#define WACMD_INPUTPAUSE 0x16 /* pause ADC */
+#define WACMD_INPUTSTOP 0x17 /* stop ADC */
+#define WACMD_INPUTRESUME 0x18 /* resume ADC */
+#define WACMD_INPUTPIO 0x19 /* PIO ADC */
+
+#define WACMD_OUTPUTFORMAT 0x20 /* 0=S8, 1=S16, 2=U8 */
+#define WACMD_OUTPUTCHANNELS 0x21 /* 1=mono, 2=stereo */
+#define WACMD_OUTPUTSPEED 0x22 /* sampling rate */
+#define WACMD_OUTPUTDMA 0x23 /* 0=8bit, 1=16bit, 2=PIO */
+#define WACMD_OUTPUTSIZE 0x24 /* samples to interrupt */
+#define WACMD_OUTPUTSTART 0x25 /* start DAC */
+#define WACMD_OUTPUTPAUSE 0x26 /* pause DAC */
+#define WACMD_OUTPUTSTOP 0x27 /* stop DAC */
+#define WACMD_OUTPUTRESUME 0x28 /* resume DAC */
+#define WACMD_OUTPUTPIO 0x29 /* PIO DAC */
+
+#define WACMD_GET_LEVEL 0x30 /* read mixer reg */
+#define WACMD_SET_LEVEL 0x31 /* set mixer regs (10..19) */
+#define WACMD_SET_MIXER 0x32 /* set mixer regs (0..9) */
+#define WACMD_RST_MIXER 0x33 /* mixer reset */
+#define WACMD_SET_MONO 0x34 /* set mono mode (|0x000=L, |0x100=R) */
+
+enum wa_format { WA_FMT_S8 = 0, WA_FMT_S16, WA_FMT_U8 };
+enum wa_dma { WA_DMA_8BIT = 0, WA_DMA_16BIT, WA_DMA_PIO };
--
Ondrej Zary
1
1
Hi
A little over a year age I got a ECS KBN-1 which has on-board
HD-audio chip: card0, card1. Determined I needed to make card1
the default and someone was kind enough to send me a simple one
line "fix." So simplein fact, I didn't write it down.
Now I'm installing new software on a new partition and I need
that fix again, please. This time I promise to write it down
in my ECS user manual.
Thanks,
Russ
1
0
[alsa-devel] [PATCH][ASoC]Add DT bindings for generic ASoC AC97 CODEC driver
by Maciej S. Szmigiero 07 Mar '15
by Maciej S. Szmigiero 07 Mar '15
07 Mar '15
Add and document DT bindings for generic ASoC AC97 CODEC driver,
make it selectable in config.
Signed-off-by: Maciej Szmigiero <mail(a)maciej.szmigiero.name>
diff --git a/Documentation/devicetree/bindings/sound/ac97-generic-codec.txt b/Documentation/devicetree/bindings/sound/ac97-generic-codec.txt
new file mode 100644
index 0000000..ce98698
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ac97-generic-codec.txt
@@ -0,0 +1,24 @@
+Generic ASoC AC97 CODEC driver
+
+Allows using codecs supported by generic ALSA AC97 code as codecs in ASoC.
+
+Required properties:
+
+ - compatible : "linux,ac97-codec"
+
+Optional properties:
+
+ - playback-rates : A list of supported playback rates.
+
+ - capture-rates : A list of supported capture rates.
+
+In case one or both of above properties are missing the relevant rate(s) are assumed
+to be 8000, 11025, 22050, 44100, 48000.
+
+Example:
+
+ac97codec: ac97-hifi {
+ compatible = "linux,ac97-codec";
+ playback-rates = <48000 44100 32000 24000 22050 16000 12000 11025 8000>;
+ capture-rates = <48000>;
+};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0bddd92..92d6d39 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -16,7 +16,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_88PM860X if MFD_88PM860X
select SND_SOC_L3
select SND_SOC_AB8500_CODEC if ABX500_CORE
- select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+ select SND_SOC_AC97_CODEC
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X_SPI if SPI_MASTER
select SND_SOC_AD193X_I2C if I2C
@@ -210,8 +210,9 @@ config SND_SOC_AB8500_CODEC
tristate
config SND_SOC_AC97_CODEC
- tristate
+ tristate "Build generic ASoC AC97 CODEC driver"
select SND_AC97_CODEC
+ select SND_SOC_AC97_BUS
config SND_SOC_AD1836
tristate
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index d0ac723..25125bf 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -23,6 +23,12 @@
#include <sound/initval.h>
#include <sound/soc.h>
+struct ac97_private {
+ struct snd_ac97 *ac97;
+ struct snd_pcm_hw_constraint_list playback_rate_constraints;
+ struct snd_pcm_hw_constraint_list capture_rate_constraints;
+};
+
static const struct snd_soc_dapm_widget ac97_widgets[] = {
SND_SOC_DAPM_INPUT("RX"),
SND_SOC_DAPM_OUTPUT("TX"),
@@ -33,22 +39,41 @@ static const struct snd_soc_dapm_route ac97_routes[] = {
{ "TX", NULL, "AC97 Playback" },
};
+static const unsigned int default_rates[] = {
+ 8000, 11025, 22050, 44100, 48000
+};
+
+static int ac97_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ac97_private *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ac97->playback_rate_constraints);
+ else
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ac97->capture_rate_constraints);
+
+ return 0;
+}
+
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct ac97_private *ac97 = snd_soc_codec_get_drvdata(codec);
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
+ return snd_ac97_set_rate(ac97->ac97, reg, substream->runtime->rate);
}
-#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000)
-
static const struct snd_soc_dai_ops ac97_dai_ops = {
+ .startup = ac97_startup,
.prepare = ac97_prepare,
};
@@ -58,20 +83,21 @@ static struct snd_soc_dai_driver ac97_dai = {
.stream_name = "AC97 Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.ops = &ac97_dai_ops,
};
static int ac97_soc_probe(struct snd_soc_codec *codec)
{
- struct snd_ac97 *ac97;
+ struct ac97_private *ac97 = snd_soc_codec_get_drvdata(codec);
+
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
@@ -83,31 +109,28 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
return ret;
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
- ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
+ ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97->ac97);
if (ret < 0)
return ret;
- snd_soc_codec_set_drvdata(codec, ac97);
-
return 0;
}
#ifdef CONFIG_PM
static int ac97_soc_suspend(struct snd_soc_codec *codec)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct ac97_private *ac97 = snd_soc_codec_get_drvdata(codec);
- snd_ac97_suspend(ac97);
+ snd_ac97_suspend(ac97->ac97);
return 0;
}
static int ac97_soc_resume(struct snd_soc_codec *codec)
{
+ struct ac97_private *ac97 = snd_soc_codec_get_drvdata(codec);
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
-
- snd_ac97_resume(ac97);
+ snd_ac97_resume(ac97->ac97);
return 0;
}
@@ -127,8 +150,89 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
.num_dapm_routes = ARRAY_SIZE(ac97_routes),
};
+#ifdef CONFIG_OF
+static int ac97_of_read_rates(struct platform_device *pdev,
+ const char *name,
+ struct snd_pcm_hw_constraint_list *cons)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct property *prop;
+ int len, count, ctr;
+ unsigned int *clist;
+
+ prop = of_find_property(np, name, &len);
+ if (!prop)
+ return 0;
+
+ count = len / sizeof(u32);
+ if (count <= 0)
+ return 0;
+
+ clist = devm_kzalloc(&pdev->dev, count * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!clist)
+ return -ENOMEM;
+
+ for (ctr = 0; ctr < count; ctr++) {
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32_index(np, name, ctr, &val);
+ if (ret)
+ return ret;
+
+ clist[ctr] = val;
+ }
+
+ cons->list = clist;
+ cons->count = ctr;
+
+ return 0;
+}
+#endif
+
static int ac97_probe(struct platform_device *pdev)
{
+ struct ac97_private *ac97 =
+ devm_kzalloc(&pdev->dev, sizeof(struct ac97_private),
+ GFP_KERNEL);
+#ifdef CONFIG_OF
+ struct device_node *np = pdev->dev.of_node;
+#endif
+
+ if (!ac97)
+ return -ENOMEM;
+
+#ifdef CONFIG_OF
+ if (np) {
+ int ret;
+
+ ret = ac97_of_read_rates(pdev, "playback-rates",
+ &ac97->playback_rate_constraints);
+ if (ret)
+ return ret;
+
+ ret = ac97_of_read_rates(pdev, "capture-rates",
+ &ac97->capture_rate_constraints);
+ if (ret)
+ return ret;
+ }
+#endif
+
+ if (!ac97->playback_rate_constraints.list) {
+ ac97->playback_rate_constraints.list = default_rates;
+ ac97->playback_rate_constraints.count =
+ ARRAY_SIZE(default_rates);
+ }
+
+ if (!ac97->capture_rate_constraints.list) {
+ ac97->capture_rate_constraints.list = default_rates;
+ ac97->capture_rate_constraints.count =
+ ARRAY_SIZE(default_rates);
+ }
+
+ platform_set_drvdata(pdev, ac97);
+
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_ac97, &ac97_dai, 1);
}
@@ -139,9 +243,16 @@ static int ac97_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ac97_codec_dt_ids[] = {
+ { .compatible = "linux,ac97-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac97_codec_dt_ids);
+
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
+ .of_match_table = of_match_ptr(ac97_codec_dt_ids),
},
.probe = ac97_probe,
2
5
[alsa-devel] [Patch V7 00/10] ASoC: QCOM: Add support for ipq806x SOC
by Kenneth Westfield 07 Mar '15
by Kenneth Westfield 07 Mar '15
07 Mar '15
From: Kenneth Westfield <kwestfie(a)codeaurora.org>
This patch series adds support for I2S audio playback on the Storm board, which
contains a Qualcomm Technologies ipq806x SOC and a Maxim max98357a DAC/amp.
The ipq806x SOC has audio-related hardware blocks in its low-power audio
subsystem (or LPASS). One of the relevant blocks in the LPASS is its low-power
audio interface (or LPAIF). This contains an MI2S port, which is what these
drivers are configured to use. The LPAIF also contains a DMA engine that is
dedicated to moving audio samples into the transmit FIFO of the MI2S port.
One bus from the MI2S port of the SOC is connected to the DAC/amp for stereo
playback. This bus is configured so that the SOC is bus master and consists of
DATA, LRCLK, and BCLK. The DAC/amp does not need MCLK to operate. In addition,
a single GPIO pin from the SOC is connected to the same DAC/amp, which gives
enable/disable control over the DAC/amp.
The specific drivers added are:
* a CPU DAI driver for controlling the MI2S port
* a platform driver for controlling the LPAIF DMA engine
* a machine driver that instantiates a dai-link for playback
The LPASS also contains clocks that need to be controlled. Those drivers have
been submitted as several separate patch series:
* [PATCH v3 0/8] qcom audio clock control drivers
http://lkml.org/lkml/2015/1/19/656
* [PATCH] clk: qcom: Properly change rates for ahbix clock
https://lkml.org/lkml/2015/2/25/706
* [PATCH] clk: qcom: Fix ipq806x LCC frequency tables
https://lkml.org/lkml/2015/2/26/774
Even though the ipq806x LPASS does not contain an audio DSP, other SOCs do have
one. For those SOCs, the audio DSP typically controls the hardware blocks in
the LPASS. Hence, different CPU DAI driver(s) would need to be used in order to
facilitate audio with the DSP. As such, the LPASS DT contains an adsp subnode,
which is disabled for this SOC. The same subnode should be enabled and
populated for other SOCs that do contain an audio DSP. Not using the audio DSP
would require different CPU DAI driver(s), in addition to possible bootloader
and/or firmware changes.
Corresponding additions to the device tree for the ipq806x SOC and its
documentation has also been added. Also, as this is a new directory, the
MAINTAINERS file has been updated as well.
= Changes since V6
[Patch V6 00/10] ASoC: QCOM: Add support for ipq806x SOC
http://mailman.alsa-project.org/pipermail/alsa-devel/2015-February/088218.h…
* Added REGMAP_MMIO selection for the CPU and platform drivers
* Modified the AHBIX frequency to match the corresponding LCC fix
* Tweaked the logging in the CPU driver probe.
= Changes since V5
[Patch V5 00/12] ASoC: QCOM: Add support for ipq806x SOC
http://mailman.alsa-project.org/pipermail/alsa-devel/2015-February/087832.h…
* Correctly use Storm as the build target label and DT binding label.
* Added audio DSP sub-node to the LPASS device tree, disabled for this SOC.
* Added logic to CPU DAI driver to fail the probe() if a DSP is present.
* Use the standard naming convention for the DAI link properties.
* General code cleanup.
= Changes since V4
[Patch V4 00/10] ASoC: QCOM: Add support for ipq806x SOC
http://mailman.alsa-project.org/pipermail/alsa-devel/2015-February/087499.h…
* Replaced simple-card with a machine driver to resolve the system clock
configuration, rather than having the CPU DAI driver do it.
* Added header files to avoid indirect header dependencies and implicit
forward declarations.
* Tweaked the ISR to match the conventions of the surrounding code.
* Removed the usage of the low-power memory as it is not needed.
* Removed the use of the DRV_NAME constant.
* Added explicit dependency on gpiolib for the codec driver.
* Moved the MODULE_DEVICE_TABLE macro inside the CONFIG_OF conditional.
* Modified the documentation to reflect the changes.
* General code cleanup.
= Changes since V3
[Patch V3 00/10] ASoC: QCOM: Add support for ipq806x SOC
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-December/085694.h…
* Placed the content of the inline functions into the callbacks.
* Replaced use of readl/writel register access functions with regmap access
functions. Notable exception is the ISR, which uses ioread32/iowrite32.
* Rearranged the sequencing of the hardware block enables to fit within the
ASoC framework callbacks, while remaining functional.
REQ 1: The hardware requires the enable sequence to be:
LPAIF-DMA[enable],then LPAIF-MI2S[enable], then DAC-GPIO[enable]
REQ 2: The hardware requires the disable sequence to be:
DAC-GPIO[disable], then LPAIF-MI2S[disable]
* Corrected the implementation of the pointer callback.
* Utilize the LPM to buffer audio samples, rather than memory external to
LPASS.
* Corrected the interrupt clearing in the ISR.
* Implemented a default system clock (defined by the simple-card DT node), and
optional LPASS DT node modifiers that can alter the system clock in order to
expand the range of available bit clock frequencies.
* Addressed all of the remaining issues raised by Mark Brown.
* General code cleanup.
= Changes since V2
[Patch v2 00/11] ASoC: QCOM: Add support for ipq806x SOC
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-December/085186.h…
* Removed the PCM platform driver from the DTS platform and tied it to the CPU
DAI driver.
* Changed I2S pinctrl to use generic naming convention and moved control to
CPU DAI driver. It should be controlled now by soc-core's pinctrl_pm_*
functionality.
* Added stub DAPM support in codec driver. As the DAC GPIO needs to be
enabled last when starting playback, and disabled first when stopping
playback, it seems as though the trigger function may be the place for this.
Suggestions are welcome for a better place to put this.
* Removed machine driver and tied DAI drivers to simple-audio-card.
* Packaged the build files and Maxim codec files together in one change.
* Removed QCOM as vendor from Maxim code and documentation.
* Separated the SOC and board definitions into the correct DTS files.
* Update device tree documentation to reflect changes.
* General code cleanup.
= Changes since V1
[PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-November/084322.h…
* Remove the native LPAIF driver, and move its functionality to the CPU DAI
driver.
* Add a codec driver to manage the pins going to the external DAC (previously
managed by the machine driver).
* Use devm_* and dev_* where possible.
* ISR only handles relevant DMA channel now.
* Update device tree documentation to reflect changes.
* General code cleanup.
Kenneth Westfield (10):
MAINTAINERS: Add QCOM audio ASoC maintainer
ASoC: qcom: Document LPASS CPU bindings
ASoC: qcom: Document Storm bindings
ASoC: qcom: add LPASS header files
ASoC: qcom: Add LPASS CPU DAI driver
ASoC: qcom: Add LPASS platform driver
ASoC: qcom: Add Storm machine driver
ASoC: qcom: Add ability to build QCOM drivers
ASoC: Allow for building QCOM drivers
ARM: dts: Model IPQ LPASS audio hardware
.../devicetree/bindings/sound/qcom,lpass-cpu.txt | 49 ++
Documentation/devicetree/bindings/sound/storm.txt | 23 +
MAINTAINERS | 7 +
arch/arm/boot/dts/qcom-ipq8064.dtsi | 26 +
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/qcom/Kconfig | 25 +
sound/soc/qcom/Makefile | 11 +
sound/soc/qcom/lpass-cpu.c | 510 ++++++++++++++++++++
sound/soc/qcom/lpass-lpaif-ipq806x.h | 172 +++++++
sound/soc/qcom/lpass-platform.c | 526 +++++++++++++++++++++
sound/soc/qcom/lpass.h | 51 ++
sound/soc/qcom/storm.c | 162 +++++++
13 files changed, 1564 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt
create mode 100644 Documentation/devicetree/bindings/sound/storm.txt
create mode 100644 sound/soc/qcom/Kconfig
create mode 100644 sound/soc/qcom/Makefile
create mode 100644 sound/soc/qcom/lpass-cpu.c
create mode 100644 sound/soc/qcom/lpass-lpaif-ipq806x.h
create mode 100644 sound/soc/qcom/lpass-platform.c
create mode 100644 sound/soc/qcom/lpass.h
create mode 100644 sound/soc/qcom/storm.c
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
3
21
07 Mar '15
The jz4780 and jz4740 have very similar i2s blocks.
The slight difference is in Rx/Tx fifos.
And the bitclocks for input/output are different.
This patch adds jz4780 support to the driver
Signed-off-by: Zubair Lutfullah Kakakhel <Zubair.Kakakhel(a)imgtec.com>
---
Patch based on 4.0-rc2
Tested on the MIPS Creator CI20.
---
.../bindings/sound/ingenic,jz4740-i2s.txt | 2 +-
sound/soc/jz4740/jz4740-i2s.c | 63 ++++++++++++++++++----
2 files changed, 54 insertions(+), 11 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt b/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt
index b414333..b623d50 100644
--- a/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt
@@ -1,7 +1,7 @@
Ingenic JZ4740 I2S controller
Required properties:
-- compatible : "ingenic,jz4740-i2s"
+- compatible : "ingenic,jz4740-i2s" or "ingenic,jz4780-i2s"
- reg : I2S registers location and length
- clocks : AIC and I2S PLL clock specifiers.
- clock-names: "aic" and "i2s"
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 07f7781..630ea96 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -58,6 +58,12 @@
#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
+#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24
+#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16
+#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_MASK \
+ (0xf << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET)
+#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_MASK \
+ (0x1f << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET)
#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
@@ -79,6 +85,7 @@
#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
+#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13)
#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
#define JZ_AIC_I2S_FMT_MSB BIT(0)
@@ -87,6 +94,13 @@
#define JZ_AIC_CLK_DIV_MASK 0xf
#define I2SDIV_DV_SHIFT 8
#define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT)
+#define I2SDIV_IDV_SHIFT 8
+#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT)
+
+enum jz47xx_i2s_version {
+ JZ_I2S_JZ4740,
+ JZ_I2S_JZ4780,
+};
struct jz4740_i2s {
struct resource *mem;
@@ -98,6 +112,8 @@ struct jz4740_i2s {
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct snd_dmaengine_dai_dma_data capture_dma_data;
+
+ enum jz47xx_i2s_version version;
};
static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
@@ -267,13 +283,22 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
else
ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
+
+ div_reg &= ~I2SDIV_DV_MASK;
+ div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
} else {
ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
+
+ if (i2s->version >= JZ_I2S_JZ4780) {
+ div_reg &= ~I2SDIV_IDV_MASK;
+ div_reg |= (div - 1) << I2SDIV_IDV_SHIFT;
+ } else {
+ div_reg &= ~I2SDIV_DV_MASK;
+ div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
+ }
}
- div_reg &= ~I2SDIV_DV_MASK;
- div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg);
@@ -369,11 +394,19 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
&i2s->capture_dma_data);
- conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
- (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
- JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
- JZ_AIC_CONF_I2S |
- JZ_AIC_CONF_INTERNAL_CODEC;
+ if (i2s->version >= JZ_I2S_JZ4780) {
+ conf = (7 << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
+ (8 << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
+ JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
+ JZ_AIC_CONF_I2S |
+ JZ_AIC_CONF_INTERNAL_CODEC;
+ } else {
+ conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
+ (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
+ JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
+ JZ_AIC_CONF_I2S |
+ JZ_AIC_CONF_INTERNAL_CODEC;
+ }
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
@@ -428,7 +461,8 @@ static const struct snd_soc_component_driver jz4740_i2s_component = {
#ifdef CONFIG_OF
static const struct of_device_id jz4740_of_matches[] = {
- { .compatible = "ingenic,jz4740-i2s" },
+ { .compatible = "ingenic,jz4740-i2s", .data = (void *)JZ_I2S_JZ4740 },
+ { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 },
{ /* sentinel */ }
};
#endif
@@ -438,11 +472,16 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev)
struct jz4740_i2s *i2s;
struct resource *mem;
int ret;
+ const struct of_device_id *match;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
+ match = of_match_device(jz4740_of_matches, &pdev->dev);
+ if (match)
+ i2s->version = (enum jz47xx_i2s_version)match->data;
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2s->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(i2s->base))
@@ -465,8 +504,12 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
- SND_DMAENGINE_PCM_FLAG_COMPAT);
+ if (i2s->version >= JZ_I2S_JZ4780)
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+ else
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
}
static struct platform_driver jz4740_i2s_driver = {
--
1.9.1
2
1
[alsa-devel] [PATCH 1/1] ASoC: Intel: remove conflicts when load/unload multiple firmware images
by han.luï¼ intel.com 06 Mar '15
by han.luï¼ intel.com 06 Mar '15
06 Mar '15
From: "Lu, Han" <han.lu(a)intel.com>
Details:
1. Unload all modules on fw_list of dsp when suspend, and reload all
modules on fw_list when resume.
2. A DSP expects only one scratch, but hsw_parse_fw_image() allocates
scratch blocks for each firmware image it parses. Move the allocate function
sst_block_alloc_scratch() out of hsw_parse_fw_image() to make sure a scratch
be allocated only after all firmware images be parsed.
Signed-off-by: Lu, Han <han.lu(a)intel.com>
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c
index c42ffae..402b728 100644
--- a/sound/soc/intel/sst-haswell-dsp.c
+++ b/sound/soc/intel/sst-haswell-dsp.c
@@ -207,9 +207,6 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
module = (void *)module + sizeof(*module) + module->mod_size;
}
- /* allocate scratch mem regions */
- sst_block_alloc_scratch(dsp);
-
return 0;
}
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
index 394af56..863a9ca 100644
--- a/sound/soc/intel/sst-haswell-ipc.c
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -1732,6 +1732,7 @@ static void sst_hsw_drop_all(struct sst_hsw *hsw)
int sst_hsw_dsp_load(struct sst_hsw *hsw)
{
struct sst_dsp *dsp = hsw->dsp;
+ struct sst_fw *sst_fw, *t;
int ret;
dev_dbg(hsw->dev, "loading audio DSP....");
@@ -1748,12 +1749,17 @@ int sst_hsw_dsp_load(struct sst_hsw *hsw)
return ret;
}
- ret = sst_fw_reload(hsw->sst_fw);
- if (ret < 0) {
- dev_err(hsw->dev, "error: SST FW reload failed\n");
- sst_dsp_dma_put_channel(dsp);
- return -ENOMEM;
+ list_for_each_entry_safe_reverse(sst_fw, t, &dsp->fw_list, list) {
+ ret = sst_fw_reload(sst_fw);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: SST FW reload failed\n");
+ sst_dsp_dma_put_channel(dsp);
+ return -ENOMEM;
+ }
}
+ ret = sst_block_alloc_scratch(hsw->dsp);
+ if (ret < 0)
+ return -EINVAL;
sst_dsp_dma_put_channel(dsp);
return 0;
@@ -1809,12 +1815,17 @@ int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
{
- sst_fw_unload(hsw->sst_fw);
- sst_block_free_scratch(hsw->dsp);
+ struct sst_fw *sst_fw, *t;
+ struct sst_dsp *dsp = hsw->dsp;
+
+ list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
+ sst_fw_unload(sst_fw);
+ }
+ sst_block_free_scratch(dsp);
hsw->boot_complete = false;
- sst_dsp_sleep(hsw->dsp);
+ sst_dsp_sleep(dsp);
return 0;
}
@@ -1943,6 +1954,11 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
goto fw_err;
}
+ /* allocate scratch mem regions */
+ ret = sst_block_alloc_scratch(hsw->dsp);
+ if (ret < 0)
+ goto boot_err;
+
/* wait for DSP boot completion */
sst_dsp_boot(hsw->dsp);
ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
--
1.9.1
2
1
[alsa-devel] Right interface for cellphone modem audio (was Re: [PATCHv2 0/2] N900 Modem Speech Support)
by Pavel Machek 06 Mar '15
by Pavel Machek 06 Mar '15
06 Mar '15
Hi!
> >>Userland access goes via /dev/cmt_speech. The API is implemented in
> >>libcmtspeechdata, which is used by ofono and the freesmartphone.org project.
> >Yes, the ABI is "tested" for some years, but it is not documented, and
> >it is very wrong ABI.
> >
> >I'm not sure what they do with the "read()". I was assuming it is
> >meant for passing voice data, but it can return at most 4 bytes,
> >AFAICT.
> >
> >We already have perfectly good ABI for passing voice data around. It
> >is called "ALSA". libcmtspeech will then become unneccessary, and the
> >daemon routing voice data will be as simple as "read sample from
>
> I'm no longer involved with cmt_speech (with this driver nor modems in
> general), but let me clarify some bits about the design.
Thanks a lot for your insights; high level design decisions are quite
hard to understand from C code.
> First, the team that designed the driver and the stack above had a lot of
> folks working also with ALSA (and the ALSA drivers have been merged to
> mainline long ago) and we considered ALSA on multiple occasions as the
> interface for this as well.
>
> Our take was that ALSA is not the right interface for cmt_speech. The
> cmt_speech interface in the modem is _not_ a PCM interface as modelled by
> ALSA. Specifically:
>
> - the interface is lossy in both directions
> - data is sent in packets, not a stream of samples (could be other things
> than PCM samples), with timing and meta-data
> - timing of uplink is of utmost importance
I see that you may not have data available in "downlink" scenario, but
how is it lossy in "uplink" scenario? Phone should always try to fill
the uplink, no? (Or do you detect silence and not transmit in this
case?) (Actually, I guess applications should be ready for "data not
ready" case even on "normal" hardware due to differing clocks.)
Packets vs. stream of samples... does userland need to know about the
packets? Could we simply hide it from the userland? As userland daemon
is (supposed to be) realtime, do we really need extra set of
timestamps? What other metadata are there?
Uplink timing... As the daemon is realtime, can it just send the data
at the right time? Also normally uplink would be filled, no?
> Some definite similarities:
> - the mmap interface to manage the PCM buffers (that is on purpose
> similar to that of ALSA)
>
> The interface was designed so that the audio mixer (e.g. Pulseaudio) is run
> with a soft real-time SCHED_FIFO/RR user-space thread that has full control
> over _when_ voice _packets_ are sent, and can receive packets with meta-data
> (see libcmtspeechdata interface, cmtspeech.h), and can detect and handle
> gaps in the received packets.
Well, packets are of fixed size, right? So the userland can simply
supply the right size in the common case. As for sending at the right
time... well... if the userspace is already real-time, that should be
easy.
Now, there's a difference in the downlink. Maybe ALSA people have an
idea what to do in this case? Perhaps we can just provide artificial
"zero" data?
> This is very different from modems that offer an actual PCM voice link for
> example over I2S to the application processor (there are lots of these on
> the market). When you walk out of coverage during a call with these modems,
> you'll still get samples over I2S, but not so with cmt_speech, so ALSA is
> not the right interface.
Yes, understood.
> Now, I'm not saying the interface is perfect, but just to give a bit of
> background, why a custom char-device interface was chosen.
Thanks and best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
2
1
changes since v1:
- addressed mark brown comments regarding
open coding functions.
- changed the mixer namings
- added some dapm widgets
Signed-off-by: Anish Kumar <yesanishhere(a)gmail.com>
---
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/max98925.c | 737 +++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/max98925.h | 833 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1576 insertions(+)
create mode 100644 sound/soc/codecs/max98925.c
create mode 100644 sound/soc/codecs/max98925.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0bddd92..a7f44e3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -68,6 +68,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_LM49453 if I2C
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98090 if I2C
+ select SND_SOC_MAX98925 if I2C
select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX98357A if GPIOLIB
select SND_SOC_MAX9850 if I2C
@@ -455,6 +456,9 @@ config SND_SOC_MAX98088
config SND_SOC_MAX98090
tristate
+config SND_SOC_MAX98925
+ tristate
+
config SND_SOC_MAX98095
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7acb6c1..1658d19 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -63,6 +63,7 @@ snd-soc-lm49453-objs := lm49453.o
snd-soc-max9768-objs := max9768.o
snd-soc-max98088-objs := max98088.o
snd-soc-max98090-objs := max98090.o
+snd-soc-max98925-objs := max98925.o
snd-soc-max98095-objs := max98095.o
snd-soc-max98357a-objs := max98357a.o
snd-soc-max9850-objs := max9850.o
@@ -247,6 +248,7 @@ obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
+obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
new file mode 100644
index 0000000..7831bc9
--- /dev/null
+++ b/sound/soc/codecs/max98925.c
@@ -0,0 +1,737 @@
+/*
+ * max98925.c -- ALSA SoC Stereo MAX98925 driver
+ * Copyright 2013-15 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98925.h"
+
+static const char *const dai_text[] = {
+ "Left", "Right", "LeftRight", "LeftRightDiv2",
+};
+
+static const char *const hpf_text[] = {
+ "Disable", "DC Block", "100Hz", "200Hz", "400Hz", "800Hz",
+};
+
+static struct reg_default max98925_reg[] = {
+ { 0x0B, 0x00 }, /* IRQ Enable0 */
+ { 0x0C, 0x00 }, /* IRQ Enable1 */
+ { 0x0D, 0x00 }, /* IRQ Enable2 */
+ { 0x0E, 0x00 }, /* IRQ Clear0 */
+ { 0x0F, 0x00 }, /* IRQ Clear1 */
+ { 0x10, 0x00 }, /* IRQ Clear2 */
+ { 0x11, 0xC0 }, /* Map0 */
+ { 0x12, 0x00 }, /* Map1 */
+ { 0x13, 0x00 }, /* Map2 */
+ { 0x14, 0xF0 }, /* Map3 */
+ { 0x15, 0x00 }, /* Map4 */
+ { 0x16, 0xAB }, /* Map5 */
+ { 0x17, 0x89 }, /* Map6 */
+ { 0x18, 0x00 }, /* Map7 */
+ { 0x19, 0x00 }, /* Map8 */
+ { 0x1A, 0x06 }, /* DAI Clock Mode 1 */
+ { 0x1B, 0xC0 }, /* DAI Clock Mode 2 */
+ { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */
+ { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */
+ { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */
+ { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
+ { 0x20, 0x50 }, /* Format */
+ { 0x21, 0x00 }, /* TDM Slot Select */
+ { 0x22, 0x00 }, /* DOUT Configuration VMON */
+ { 0x23, 0x00 }, /* DOUT Configuration IMON */
+ { 0x24, 0x00 }, /* DOUT Configuration VBAT */
+ { 0x25, 0x00 }, /* DOUT Configuration VBST */
+ { 0x26, 0x00 }, /* DOUT Configuration FLAG */
+ { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
+ { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
+ { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
+ { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
+ { 0x2B, 0x02 }, /* DOUT Drive Strength */
+ { 0x2C, 0x90 }, /* Filters */
+ { 0x2D, 0x00 }, /* Gain */
+ { 0x2E, 0x02 }, /* Gain Ramping */
+ { 0x2F, 0x00 }, /* Speaker Amplifier */
+ { 0x30, 0x0A }, /* Threshold */
+ { 0x31, 0x00 }, /* ALC Attack */
+ { 0x32, 0x80 }, /* ALC Atten and Release */
+ { 0x33, 0x00 }, /* ALC Infinite Hold Release */
+ { 0x34, 0x92 }, /* ALC Configuration */
+ { 0x35, 0x01 }, /* Boost Converter */
+ { 0x36, 0x00 }, /* Block Enable */
+ { 0x37, 0x00 }, /* Configuration */
+ { 0x38, 0x00 }, /* Global Enable */
+ { 0x3A, 0x00 }, /* Boost Limiter */
+};
+
+static const struct soc_enum max98925_enum[] = {
+ SOC_ENUM_SINGLE(MAX98925_GAIN, 5, 4, dai_text),
+ SOC_ENUM_SINGLE(MAX98925_FILTERS, 0, 6, hpf_text),
+};
+
+static const struct snd_kcontrol_new max98925_dai_sel =
+ SOC_DAPM_ENUM("Route", max98925_enum[0]);
+
+static const struct snd_kcontrol_new max98925_hpf_sel =
+ SOC_DAPM_ENUM("Route", max98925_enum[1]);
+
+static int max98925_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_BLOCK_ENABLE,
+ M98925_BST_EN_MASK |
+ M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK,
+ M98925_BST_EN_MASK |
+ M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_BLOCK_ENABLE, M98925_BST_EN_MASK |
+ M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, 0);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98925_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("DAI_IN_MUX MUX", SND_SOC_NOPM, 0, 0,
+ &max98925_dai_sel),
+ SND_SOC_DAPM_MUX("Rc Filter Mux", SND_SOC_NOPM, 0, 0,
+ &max98925_hpf_sel),
+ SND_SOC_DAPM_DAC_E("Amp Enable", NULL, MAX98925_BLOCK_ENABLE,
+ M98925_SPK_EN_SHIFT, 0, max98925_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98925_GLOBAL_ENABLE,
+ M98925_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98925_audio_map[] = {
+ {"DAI_IN_MUX MUX", "Left", "DAI_OUT"},
+ {"DAI_IN_MUX MUX", "Right", "DAI_OUT"},
+ {"DAI_IN_MUX MUX", "LeftRight", "DAI_OUT"},
+ {"DAI_IN_MUX MUX", "LeftRightDiv2", "DAI_OUT"},
+ {"Rc Filter Mux", "Disable", "DAI_IN_MUX MUX"},
+ {"Rc Filter Mux", "DC Block", "DAI_IN_MUX MUX"},
+ {"Rc Filter Mux", "100Hz", "DAI_IN_MUX MUX"},
+ {"Rc Filter Mux", "200Hz", "DAI_IN_MUX MUX"},
+ {"Rc Filter Mux", "400Hz", "DAI_IN_MUX MUX"},
+ {"Rc Filter Mux", "800Hz", "DAI_IN_MUX MUX"},
+ {"Amp Enable", NULL, "Rc Filter Mux"},
+ {"BE_OUT", NULL, "Amp Enable"},
+ {"BE_OUT", NULL, "Global Enable"},
+};
+
+static bool max98925_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98925_VBAT_DATA:
+ case MAX98925_VBST_DATA:
+ case MAX98925_LIVE_STATUS0:
+ case MAX98925_LIVE_STATUS1:
+ case MAX98925_LIVE_STATUS2:
+ case MAX98925_STATE0:
+ case MAX98925_STATE1:
+ case MAX98925_STATE2:
+ case MAX98925_FLAG0:
+ case MAX98925_FLAG1:
+ case MAX98925_FLAG2:
+ case MAX98925_REV_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98925_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98925_IRQ_CLEAR0:
+ case MAX98925_IRQ_CLEAR1:
+ case MAX98925_IRQ_CLEAR2:
+ case MAX98925_ALC_HOLD_RLS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int max98925_spk_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98925->spk_gain;
+ dev_dbg(codec->dev, "%s: spk_gain setting returned %d\n", __func__,
+ (int) ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int max98925_spk_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ if (sel < ((1 << M98925_SPK_GAIN_WIDTH) - 1)) {
+ regmap_update_bits(max98925->regmap, MAX98925_GAIN,
+ M98925_SPK_GAIN_MASK, sel << M98925_SPK_GAIN_SHIFT);
+ max98925->spk_gain = sel;
+
+ dev_dbg(codec->dev, "%s: spk_gain set to %d\n", __func__, sel);
+ } else {
+ dev_dbg(codec->dev, "%s: valid speaker gain settings: %d to %d\n",
+ __func__, 0,
+ ((1 << M98925_SPK_GAIN_WIDTH) - 1));
+ }
+ return 0;
+}
+
+static const char * const max98925_boost_voltage_text[] = {
+ "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
+ "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
+};
+
+static const struct soc_enum max98925_boost_voltage_enum =
+ SOC_ENUM_SINGLE(MAX98925_CONFIGURATION, M98925_BST_VOUT_SHIFT,
+ ARRAY_SIZE(max98925_boost_voltage_text),
+ max98925_boost_voltage_text);
+
+static int max98925_boost_voltage_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ int data;
+
+ regmap_read(max98925->regmap, MAX98925_CONFIGURATION, &data);
+ ucontrol->value.integer.value[0] =
+ (data & M98925_BST_VOUT_MASK) >> M98925_BST_VOUT_SHIFT;
+ return 0;
+}
+
+static int max98925_boost_voltage_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ regmap_update_bits(max98925->regmap, MAX98925_CONFIGURATION,
+ M98925_BST_VOUT_MASK, sel << M98925_BST_VOUT_SHIFT);
+ return 0;
+}
+
+DECLARE_TLV_DB_SCALE(max98925_spk_tlv, -600, 100, 0);
+
+static const struct snd_kcontrol_new max98925_snd_controls[] = {
+ SOC_SINGLE_EXT_TLV("Speaker Gain", MAX98925_GAIN,
+ M98925_SPK_GAIN_SHIFT, (1<<M98925_SPK_GAIN_WIDTH)-1, 0,
+ max98925_spk_gain_get, max98925_spk_gain_put, max98925_spk_tlv),
+ SOC_SINGLE("Speaker Ramp", MAX98925_GAIN_RAMPING,
+ M98925_SPK_RMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Speaker ZCD", MAX98925_GAIN_RAMPING,
+ M98925_SPK_ZCD_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Enable", MAX98925_THRESHOLD,
+ M98925_ALC_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Threshold", MAX98925_THRESHOLD, M98925_ALC_TH_SHIFT,
+ (1<<M98925_ALC_TH_WIDTH)-1, 0),
+ SOC_ENUM_EXT("Boost Output Voltage", max98925_boost_voltage_enum,
+ max98925_boost_voltage_get, max98925_boost_voltage_put),
+};
+
+/* codec sample rate and n/m dividers parameter table */
+static const struct {
+ int rate;
+ int sr;
+ int divisors[3][2];
+} rate_table[] = {
+ {
+ .rate = 8000,
+ .sr = 0,
+ .divisors = { {1, 375}, {5, 1764}, {1, 384} }
+ },
+ {
+ .rate = 11025,
+ .sr = 1,
+ .divisors = { {147, 40000}, {1, 256}, {147, 40960} }
+ },
+ {
+ .rate = 12000,
+ .sr = 2,
+ .divisors = { {1, 250}, {5, 1176}, {1, 256} }
+ },
+ {
+ .rate = 16000,
+ .sr = 3,
+ .divisors = { {2, 375}, {5, 882}, {1, 192} }
+ },
+ {
+ .rate = 22050,
+ .sr = 4,
+ .divisors = { {147, 20000}, {1, 128}, {147, 20480} }
+ },
+ {
+ .rate = 24000,
+ .sr = 5,
+ .divisors = { {1, 125}, {5, 588}, {1, 128} }
+ },
+ {
+ .rate = 32000,
+ .sr = 6,
+ .divisors = { {4, 375}, {5, 441}, {1, 96} }
+ },
+ {
+ .rate = 44100,
+ .sr = 7,
+ .divisors = { {147, 10000}, {1, 64}, {147, 10240} }
+ },
+ {
+ .rate = 48000,
+ .sr = 8,
+ .divisors = { {2, 125}, {5, 294}, {1, 64} }
+ },
+};
+
+static inline int max98925_rate_value(int rate,
+ int clock, int *value, int *n, int *m)
+{
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ *n = rate_table[i].divisors[clock][0];
+ *m = rate_table[i].divisors[clock][1];
+ ret = 0;
+ break;
+ }
+ }
+ pr_debug("%s: sample rate is %d, returning %d\n",
+ __func__, rate_table[i].rate, *value);
+ return ret;
+}
+
+static void max98925_set_sense_data(struct max98925_priv *max98925)
+{
+ /* set VMON slots */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_VMON,
+ M98925_DAI_VMON_EN_MASK, M98925_DAI_VMON_EN_MASK);
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_VMON,
+ M98925_DAI_VMON_SLOT_MASK, max98925->v_slot);
+ /* set IMON slots */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_IMON,
+ M98925_DAI_IMON_EN_MASK, M98925_DAI_IMON_EN_MASK);
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_IMON,
+ M98925_DAI_IMON_SLOT_MASK, max98925->i_slot);
+}
+
+static void max98925_set_slave(struct max98925_priv *max98925)
+{
+ /* set DAI to slave mode */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_MAS_MASK, 0);
+ max98925_set_sense_data(max98925);
+}
+
+static void max98925_set_master(struct max98925_priv *max98925)
+{
+ /* set left channel DAI to master mode, right channel always slave */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK);
+}
+
+static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ unsigned int invert = 0;
+
+ dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ max98925_set_slave(max98925);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ max98925_set_master(max98925);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert = M98925_DAI_WCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = M98925_DAI_BCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert = M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98925->regmap, MAX98925_FORMAT,
+ M98925_DAI_BCI_MASK | M98925_DAI_BCI_MASK, invert);
+ return 0;
+}
+
+static int max98925_set_clock(struct max98925_priv *max98925,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int dai_sr = 0, clock, mdll, n, m;
+ int rate = params_rate(params);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98925->ch_size;
+
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (max98925->sysclk) {
+ case 6000000:
+ clock = 0;
+ mdll = M98925_MDLL_MULT_MCLKx16;
+ break;
+ case 11289600:
+ clock = 1;
+ mdll = M98925_MDLL_MULT_MCLKx8;
+ break;
+ case 12000000:
+ clock = 0;
+ mdll = M98925_MDLL_MULT_MCLKx8;
+ break;
+ case 12288000:
+ clock = 2;
+ mdll = M98925_MDLL_MULT_MCLKx8;
+ break;
+ default:
+ dev_info(max98925->codec->dev, "unsupported sysclk %d\n",
+ max98925->sysclk);
+ return -EINVAL;
+ }
+
+ if (max98925_rate_value(rate, clock, &dai_sr, &n, &m))
+ return -EINVAL;
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_SR_MASK, dai_sr << M98925_DAI_SR_SHIFT);
+ /* set DAI m divider */
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_M_MSBS, m >> 8);
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_M_LSBS, m & 0xFF);
+ /* set DAI n divider */
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_N_MSBS, n >> 8);
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_N_LSBS, n & 0xFF);
+ /* set MDLL */
+ regmap_update_bits(max98925->regmap, MAX98925_DAI_CLK_MODE1,
+ M98925_MDLL_MULT_MASK, mdll << M98925_MDLL_MULT_SHIFT);
+ return 0;
+}
+
+static int max98925_dai_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 max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_FORMAT,
+ M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_16);
+ max98925->ch_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_FORMAT,
+ M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_32);
+ max98925->ch_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_FORMAT,
+ M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_32);
+ max98925->ch_size = 32;
+ break;
+ default:
+ pr_err("%s: format unsupported %d",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: format supported %d",
+ __func__, params_format(params));
+ return max98925_set_clock(max98925, params);
+}
+
+static int max98925_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case 0:
+ /* use MCLK for Left channel, right channel always BCLK */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE1,
+ M98925_DAI_CLK_SOURCE_MASK, 0);
+ break;
+ case 1:
+ /* configure dai clock source to BCLK instead of MCLK */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE1,
+ M98925_DAI_CLK_SOURCE_MASK,
+ M98925_DAI_CLK_SOURCE_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ max98925->sysclk = freq;
+ return 0;
+}
+
+static int max98925_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct max98925_priv *max98925 =
+ snd_soc_codec_get_drvdata(codec_dai->codec);
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ dev_dbg(codec->dev, "%s: mute %d\n", __func__, mute);
+ if (mute) {
+ regmap_update_bits(max98925->regmap, MAX98925_GAIN,
+ M98925_SPK_GAIN_MASK, 0x00);
+
+ regmap_update_bits(max98925->regmap,
+ MAX98925_GLOBAL_ENABLE,
+ M98925_EN_MASK, 0x0);
+ }
+ return 0;
+}
+
+#define MAX98925_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops max98925_dai_ops = {
+ .set_sysclk = max98925_dai_set_sysclk,
+ .set_fmt = max98925_dai_set_fmt,
+ .hw_params = max98925_dai_hw_params,
+ .digital_mute = max98925_dai_digital_mute,
+};
+
+static struct snd_soc_dai_driver max98925_dai[] = {
+ {
+ .name = "max98925-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98925_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98925_FORMATS,
+ },
+ .ops = &max98925_dai_ops,
+ }
+};
+
+static int max98925_probe(struct snd_soc_codec *codec)
+{
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ int reg = 0;
+
+ dev_info(codec->dev, "build number %s\n", MAX98925_REVISION);
+
+ max98925->codec = codec;
+ codec->control_data = max98925->regmap;
+ ret = regmap_read(max98925->regmap,
+ MAX98925_REV_VERSION, ®);
+ if ((ret < 0) ||
+ ((reg != MAX98925_VERSION) &&
+ (reg != MAX98925_VERSION1))) {
+ dev_err(codec->dev,
+ "device initialization error (%d 0x%02X)\n",
+ ret, reg);
+ goto err_access;
+ }
+ regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00);
+ /* It's not the default but we need to set DAI_DLY */
+ regmap_write(max98925->regmap,
+ MAX98925_FORMAT, M98925_DAI_DLY_MASK);
+ regmap_write(max98925->regmap, MAX98925_TDM_SLOT_SELECT, 0xC8);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG1, 0xFF);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG2, 0xFF);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG3, 0xFF);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG4, 0xF0);
+ regmap_write(max98925->regmap, MAX98925_FILTERS, 0xD8);
+ regmap_write(max98925->regmap, MAX98925_ALC_CONFIGURATION, 0xF8);
+ regmap_write(max98925->regmap, MAX98925_CONFIGURATION, 0xF0);
+
+ /* Disable ALC muting */
+ regmap_write(max98925->regmap, MAX98925_BOOST_LIMITER, 0xF8);
+ dev_info(codec->dev, "device version 0x%02X\n", reg);
+err_access:
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98925 = {
+ .probe = max98925_probe,
+ .controls = max98925_snd_controls,
+ .num_controls = ARRAY_SIZE(max98925_snd_controls),
+ .dapm_routes = max98925_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98925_audio_map),
+ .dapm_widgets = max98925_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets),
+};
+
+static struct regmap_config max98925_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98925_REV_VERSION,
+ .reg_defaults = max98925_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98925_reg),
+ .volatile_reg = max98925_volatile_register,
+ .readable_reg = max98925_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98925_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ u32 value;
+ struct max98925_priv *max98925;
+
+ max98925 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98925), GFP_KERNEL);
+ if (!max98925)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98925);
+ max98925->regmap = devm_regmap_init_i2c(i2c, &max98925_regmap);
+ if (IS_ERR(max98925->regmap)) {
+ ret = PTR_ERR(max98925->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ goto err_out;
+ }
+
+ if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
+ if (value > M98925_DAI_VMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "vmon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98925->v_slot = value;
+ }
+ if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) {
+ if (value > M98925_DAI_IMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "imon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98925->i_slot = value;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925,
+ max98925_dai, ARRAY_SIZE(max98925_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev,
+ "Failed to register codec: %d\n", ret);
+err_out:
+ return ret;
+}
+
+static int max98925_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 max98925_i2c_id[] = {
+ { "max98925", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98925_i2c_id);
+
+static struct i2c_driver max98925_i2c_driver = {
+ .driver = {
+ .name = "max98925",
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ },
+ .probe = max98925_i2c_probe,
+ .remove = max98925_i2c_remove,
+ .id_table = max98925_i2c_id,
+};
+
+module_i2c_driver(max98925_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98925 driver");
+MODULE_AUTHOR("Ralph Birt <rdbirt(a)gmail.com>, Anish kumar <anish.kumar(a)maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h
new file mode 100644
index 0000000..d7ac021
--- /dev/null
+++ b/sound/soc/codecs/max98925.h
@@ -0,0 +1,833 @@
+/*
+ * max98925.h -- MAX98925 ALSA SoC Audio driver
+ *
+ * Copyright 2013-2015 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 _MAX98925_H
+#define _MAX98925_H
+
+#define MAX98925_VERSION 0x51
+#define MAX98925_VERSION1 0x80
+#define MAX98925_REVISION "0.00.0333"
+#define MAX98925_VBAT_DATA 0x00
+#define MAX98925_VBST_DATA 0x01
+#define MAX98925_LIVE_STATUS0 0x02
+#define MAX98925_LIVE_STATUS1 0x03
+#define MAX98925_LIVE_STATUS2 0x04
+#define MAX98925_STATE0 0x05
+#define MAX98925_STATE1 0x06
+#define MAX98925_STATE2 0x07
+#define MAX98925_FLAG0 0x08
+#define MAX98925_FLAG1 0x09
+#define MAX98925_FLAG2 0x0A
+#define MAX98925_IRQ_ENABLE0 0x0B
+#define MAX98925_IRQ_ENABLE1 0x0C
+#define MAX98925_IRQ_ENABLE2 0x0D
+#define MAX98925_IRQ_CLEAR0 0x0E
+#define MAX98925_IRQ_CLEAR1 0x0F
+#define MAX98925_IRQ_CLEAR2 0x10
+#define MAX98925_MAP0 0x11
+#define MAX98925_MAP1 0x12
+#define MAX98925_MAP2 0x13
+#define MAX98925_MAP3 0x14
+#define MAX98925_MAP4 0x15
+#define MAX98925_MAP5 0x16
+#define MAX98925_MAP6 0x17
+#define MAX98925_MAP7 0x18
+#define MAX98925_MAP8 0x19
+#define MAX98925_DAI_CLK_MODE1 0x1A
+#define MAX98925_DAI_CLK_MODE2 0x1B
+#define MAX98925_DAI_CLK_DIV_M_MSBS 0x1C
+#define MAX98925_DAI_CLK_DIV_M_LSBS 0x1D
+#define MAX98925_DAI_CLK_DIV_N_MSBS 0x1E
+#define MAX98925_DAI_CLK_DIV_N_LSBS 0x1F
+#define MAX98925_FORMAT 0x20
+#define MAX98925_TDM_SLOT_SELECT 0x21
+#define MAX98925_DOUT_CFG_VMON 0x22
+#define MAX98925_DOUT_CFG_IMON 0x23
+#define MAX98925_DOUT_CFG_VBAT 0x24
+#define MAX98925_DOUT_CFG_VBST 0x25
+#define MAX98925_DOUT_CFG_FLAG 0x26
+#define MAX98925_DOUT_HIZ_CFG1 0x27
+#define MAX98925_DOUT_HIZ_CFG2 0x28
+#define MAX98925_DOUT_HIZ_CFG3 0x29
+#define MAX98925_DOUT_HIZ_CFG4 0x2A
+#define MAX98925_DOUT_DRV_STRENGTH 0x2B
+#define MAX98925_FILTERS 0x2C
+#define MAX98925_GAIN 0x2D
+#define MAX98925_GAIN_RAMPING 0x2E
+#define MAX98925_SPK_AMP 0x2F
+#define MAX98925_THRESHOLD 0x30
+#define MAX98925_ALC_ATTACK 0x31
+#define MAX98925_ALC_ATTEN_RLS 0x32
+#define MAX98925_ALC_HOLD_RLS 0x33
+#define MAX98925_ALC_CONFIGURATION 0x34
+#define MAX98925_BOOST_CONVERTER 0x35
+#define MAX98925_BLOCK_ENABLE 0x36
+#define MAX98925_CONFIGURATION 0x37
+#define MAX98925_GLOBAL_ENABLE 0x38
+#define MAX98925_BOOST_LIMITER 0x3A
+#define MAX98925_REV_VERSION 0xFF
+
+#define MAX98925_REG_CNT (MAX98925_R03A_BOOST_LIMITER+1)
+
+/* MAX98925 Register Bit Fields */
+
+/* MAX98925_R002_LIVE_STATUS0 */
+#define M98925_THERMWARN_STATUS_MASK (1<<3)
+#define M98925_THERMWARN_STATUS_SHIFT 3
+#define M98925_THERMWARN_STATUS_WIDTH 1
+#define M98925_THERMSHDN_STATUS_MASK (1<<1)
+#define M98925_THERMSHDN_STATUS_SHIFT 1
+#define M98925_THERMSHDN_STATUS_WIDTH 1
+
+/* MAX98925_R003_LIVE_STATUS1 */
+#define M98925_SPKCURNT_STATUS_MASK (1<<5)
+#define M98925_SPKCURNT_STATUS_SHIFT 5
+#define M98925_SPKCURNT_STATUS_WIDTH 1
+#define M98925_WATCHFAIL_STATUS_MASK (1<<4)
+#define M98925_WATCHFAIL_STATUS_SHIFT 4
+#define M98925_WATCHFAIL_STATUS_WIDTH 1
+#define M98925_ALCINFH_STATUS_MASK (1<<3)
+#define M98925_ALCINFH_STATUS_SHIFT 3
+#define M98925_ALCINFH_STATUS_WIDTH 1
+#define M98925_ALCACT_STATUS_MASK (1<<2)
+#define M98925_ALCACT_STATUS_SHIFT 2
+#define M98925_ALCACT_STATUS_WIDTH 1
+#define M98925_ALCMUT_STATUS_MASK (1<<1)
+#define M98925_ALCMUT_STATUS_SHIFT 1
+#define M98925_ALCMUT_STATUS_WIDTH 1
+#define M98925_ACLP_STATUS_MASK (1<<0)
+#define M98925_ACLP_STATUS_SHIFT 0
+#define M98925_ACLP_STATUS_WIDTH 1
+
+/* MAX98925_R004_LIVE_STATUS2 */
+#define M98925_SLOTOVRN_STATUS_MASK (1<<6)
+#define M98925_SLOTOVRN_STATUS_SHIFT 6
+#define M98925_SLOTOVRN_STATUS_WIDTH 1
+#define M98925_INVALSLOT_STATUS_MASK (1<<5)
+#define M98925_INVALSLOT_STATUS_SHIFT 5
+#define M98925_INVALSLOT_STATUS_WIDTH 1
+#define M98925_SLOTCNFLT_STATUS_MASK (1<<4)
+#define M98925_SLOTCNFLT_STATUS_SHIFT 4
+#define M98925_SLOTCNFLT_STATUS_WIDTH 1
+#define M98925_VBSTOVFL_STATUS_MASK (1<<3)
+#define M98925_VBSTOVFL_STATUS_SHIFT 3
+#define M98925_VBSTOVFL_STATUS_WIDTH 1
+#define M98925_VBATOVFL_STATUS_MASK (1<<2)
+#define M98925_VBATOVFL_STATUS_SHIFT 2
+#define M98925_VBATOVFL_STATUS_WIDTH 1
+#define M98925_IMONOVFL_STATUS_MASK (1<<1)
+#define M98925_IMONOVFL_STATUS_SHIFT 1
+#define M98925_IMONOVFL_STATUS_WIDTH 1
+#define M98925_VMONOVFL_STATUS_MASK (1<<0)
+#define M98925_VMONOVFL_STATUS_SHIFT 0
+#define M98925_VMONOVFL_STATUS_WIDTH 1
+
+/* MAX98925_R005_STATE0 */
+#define M98925_THERMWARN_END_STATE_MASK (1<<3)
+#define M98925_THERMWARN_END_STATE_SHIFT 3
+#define M98925_THERMWARN_END_STATE_WIDTH 1
+#define M98925_THERMWARN_BGN_STATE_MASK (1<<2)
+#define M98925_THERMWARN_BGN_STATE_SHIFT 1
+#define M98925_THERMWARN_BGN_STATE_WIDTH 1
+#define M98925_THERMSHDN_END_STATE_MASK (1<<1)
+#define M98925_THERMSHDN_END_STATE_SHIFT 1
+#define M98925_THERMSHDN_END_STATE_WIDTH 1
+#define M98925_THERMSHDN_BGN_STATE_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_STATE_SHIFT 0
+#define M98925_THERMSHDN_BGN_STATE_WIDTH 1
+
+/* MAX98925_R006_STATE1 */
+#define M98925_SPRCURNT_STATE_MASK (1<<5)
+#define M98925_SPRCURNT_STATE_SHIFT 5
+#define M98925_SPRCURNT_STATE_WIDTH 1
+#define M98925_WATCHFAIL_STATE_MASK (1<<4)
+#define M98925_WATCHFAIL_STATE_SHIFT 4
+#define M98925_WATCHFAIL_STATE_WIDTH 1
+#define M98925_ALCINFH_STATE_MASK (1<<3)
+#define M98925_ALCINFH_STATE_SHIFT 3
+#define M98925_ALCINFH_STATE_WIDTH 1
+#define M98925_ALCACT_STATE_MASK (1<<2)
+#define M98925_ALCACT_STATE_SHIFT 2
+#define M98925_ALCACT_STATE_WIDTH 1
+#define M98925_ALCMUT_STATE_MASK (1<<1)
+#define M98925_ALCMUT_STATE_SHIFT 1
+#define M98925_ALCMUT_STATE_WIDTH 1
+#define M98925_ALCP_STATE_MASK (1<<0)
+#define M98925_ALCP_STATE_SHIFT 0
+#define M98925_ALCP_STATE_WIDTH 1
+
+/* MAX98925_R007_STATE2 */
+#define M98925_SLOTOVRN_STATE_MASK (1<<6)
+#define M98925_SLOTOVRN_STATE_SHIFT 6
+#define M98925_SLOTOVRN_STATE_WIDTH 1
+#define M98925_INVALSLOT_STATE_MASK (1<<5)
+#define M98925_INVALSLOT_STATE_SHIFT 5
+#define M98925_INVALSLOT_STATE_WIDTH 1
+#define M98925_SLOTCNFLT_STATE_MASK (1<<4)
+#define M98925_SLOTCNFLT_STATE_SHIFT 4
+#define M98925_SLOTCNFLT_STATE_WIDTH 1
+#define M98925_VBSTOVFL_STATE_MASK (1<<3)
+#define M98925_VBSTOVFL_STATE_SHIFT 3
+#define M98925_VBSTOVFL_STATE_WIDTH 1
+#define M98925_VBATOVFL_STATE_MASK (1<<2)
+#define M98925_VBATOVFL_STATE_SHIFT 2
+#define M98925_VBATOVFL_STATE_WIDTH 1
+#define M98925_IMONOVFL_STATE_MASK (1<<1)
+#define M98925_IMONOVFL_STATE_SHIFT 1
+#define M98925_IMONOVFL_STATE_WIDTH 1
+#define M98925_VMONOVFL_STATE_MASK (1<<0)
+#define M98925_VMONOVFL_STATE_SHIFT 0
+#define M98925_VMONOVFL_STATE_WIDTH 1
+
+/* MAX98925_R008_FLAG0 */
+#define M98925_THERMWARN_END_FLAG_MASK (1<<3)
+#define M98925_THERMWARN_END_FLAG_SHIFT 3
+#define M98925_THERMWARN_END_FLAG_WIDTH 1
+#define M98925_THERMWARN_BGN_FLAG_MASK (1<<2)
+#define M98925_THERMWARN_BGN_FLAG_SHIFT 2
+#define M98925_THERMWARN_BGN_FLAG_WIDTH 1
+#define M98925_THERMSHDN_END_FLAG_MASK (1<<1)
+#define M98925_THERMSHDN_END_FLAG_SHIFT 1
+#define M98925_THERMSHDN_END_FLAG_WIDTH 1
+#define M98925_THERMSHDN_BGN_FLAG_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_FLAG_SHIFT 0
+#define M98925_THERMSHDN_BGN_FLAG_WIDTH 1
+
+/* MAX98925_R009_FLAG1 */
+#define M98925_SPKCURNT_FLAG_MASK (1<<5)
+#define M98925_SPKCURNT_FLAG_SHIFT 5
+#define M98925_SPKCURNT_FLAG_WIDTH 1
+#define M98925_WATCHFAIL_FLAG_MASK (1<<4)
+#define M98925_WATCHFAIL_FLAG_SHIFT 4
+#define M98925_WATCHFAIL_FLAG_WIDTH 1
+#define M98925_ALCINFH_FLAG_MASK (1<<3)
+#define M98925_ALCINFH_FLAG_SHIFT 3
+#define M98925_ALCINFH_FLAG_WIDTH 1
+#define M98925_ALCACT_FLAG_MASK (1<<2)
+#define M98925_ALCACT_FLAG_SHIFT 2
+#define M98925_ALCACT_FLAG_WIDTH 1
+#define M98925_ALCMUT_FLAG_MASK (1<<1)
+#define M98925_ALCMUT_FLAG_SHIFT 1
+#define M98925_ALCMUT_FLAG_WIDTH 1
+#define M98925_ALCP_FLAG_MASK (1<<0)
+#define M98925_ALCP_FLAG_SHIFT 0
+#define M98925_ALCP_FLAG_WIDTH 1
+
+/* MAX98925_R00A_FLAG2 */
+#define M98925_SLOTOVRN_FLAG_MASK (1<<6)
+#define M98925_SLOTOVRN_FLAG_SHIFT 6
+#define M98925_SLOTOVRN_FLAG_WIDTH 1
+#define M98925_INVALSLOT_FLAG_MASK (1<<5)
+#define M98925_INVALSLOT_FLAG_SHIFT 5
+#define M98925_INVALSLOT_FLAG_WIDTH 1
+#define M98925_SLOTCNFLT_FLAG_MASK (1<<4)
+#define M98925_SLOTCNFLT_FLAG_SHIFT 4
+#define M98925_SLOTCNFLT_FLAG_WIDTH 1
+#define M98925_VBSTOVFL_FLAG_MASK (1<<3)
+#define M98925_VBSTOVFL_FLAG_SHIFT 3
+#define M98925_VBSTOVFL_FLAG_WIDTH 1
+#define M98925_VBATOVFL_FLAG_MASK (1<<2)
+#define M98925_VBATOVFL_FLAG_SHIFT 2
+#define M98925_VBATOVFL_FLAG_WIDTH 1
+#define M98925_IMONOVFL_FLAG_MASK (1<<1)
+#define M98925_IMONOVFL_FLAG_SHIFT 1
+#define M98925_IMONOVFL_FLAG_WIDTH 1
+#define M98925_VMONOVFL_FLAG_MASK (1<<0)
+#define M98925_VMONOVFL_FLAG_SHIFT 0
+#define M98925_VMONOVFL_FLAG_WIDTH 1
+
+/* MAX98925_R00B_IRQ_ENABLE0 */
+#define M98925_THERMWARN_END_EN_MASK (1<<3)
+#define M98925_THERMWARN_END_EN_SHIFT 3
+#define M98925_THERMWARN_END_EN_WIDTH 1
+#define M98925_THERMWARN_BGN_EN_MASK (1<<2)
+#define M98925_THERMWARN_BGN_EN_SHIFT 2
+#define M98925_THERMWARN_BGN_EN_WIDTH 1
+#define M98925_THERMSHDN_END_EN_MASK (1<<1)
+#define M98925_THERMSHDN_END_EN_SHIFT 1
+#define M98925_THERMSHDN_END_EN_WIDTH 1
+#define M98925_THERMSHDN_BGN_EN_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_EN_SHIFT 0
+#define M98925_THERMSHDN_BGN_EN_WIDTH 1
+
+/* MAX98925_R00C_IRQ_ENABLE1 */
+#define M98925_SPKCURNT_EN_MASK (1<<5)
+#define M98925_SPKCURNT_EN_SHIFT 5
+#define M98925_SPKCURNT_EN_WIDTH 1
+#define M98925_WATCHFAIL_EN_MASK (1<<4)
+#define M98925_WATCHFAIL_EN_SHIFT 4
+#define M98925_WATCHFAIL_EN_WIDTH 1
+#define M98925_ALCINFH_EN_MASK (1<<3)
+#define M98925_ALCINFH_EN_SHIFT 3
+#define M98925_ALCINFH_EN_WIDTH 1
+#define M98925_ALCACT_EN_MASK (1<<2)
+#define M98925_ALCACT_EN_SHIFT 2
+#define M98925_ALCACT_EN_WIDTH 1
+#define M98925_ALCMUT_EN_MASK (1<<1)
+#define M98925_ALCMUT_EN_SHIFT 1
+#define M98925_ALCMUT_EN_WIDTH 1
+#define M98925_ALCP_EN_MASK (1<<0)
+#define M98925_ALCP_EN_SHIFT 0
+#define M98925_ALCP_EN_WIDTH 1
+
+/* MAX98925_R00D_IRQ_ENABLE2 */
+#define M98925_SLOTOVRN_EN_MASK (1<<6)
+#define M98925_SLOTOVRN_EN_SHIFT 6
+#define M98925_SLOTOVRN_EN_WIDTH 1
+#define M98925_INVALSLOT_EN_MASK (1<<5)
+#define M98925_INVALSLOT_EN_SHIFT 5
+#define M98925_INVALSLOT_EN_WIDTH 1
+#define M98925_SLOTCNFLT_EN_MASK (1<<4)
+#define M98925_SLOTCNFLT_EN_SHIFT 4
+#define M98925_SLOTCNFLT_EN_WIDTH 1
+#define M98925_VBSTOVFL_EN_MASK (1<<3)
+#define M98925_VBSTOVFL_EN_SHIFT 3
+#define M98925_VBSTOVFL_EN_WIDTH 1
+#define M98925_VBATOVFL_EN_MASK (1<<2)
+#define M98925_VBATOVFL_EN_SHIFT 2
+#define M98925_VBATOVFL_EN_WIDTH 1
+#define M98925_IMONOVFL_EN_MASK (1<<1)
+#define M98925_IMONOVFL_EN_SHIFT 1
+#define M98925_IMONOVFL_EN_WIDTH 1
+#define M98925_VMONOVFL_EN_MASK (1<<0)
+#define M98925_VMONOVFL_EN_SHIFT 0
+#define M98925_VMONOVFL_EN_WIDTH 1
+
+/* MAX98925_R00E_IRQ_CLEAR0 */
+#define M98925_THERMWARN_END_CLR_MASK (1<<3)
+#define M98925_THERMWARN_END_CLR_SHIFT 3
+#define M98925_THERMWARN_END_CLR_WIDTH 1
+#define M98925_THERMWARN_BGN_CLR_MASK (1<<2)
+#define M98925_THERMWARN_BGN_CLR_SHIFT 2
+#define M98925_THERMWARN_BGN_CLR_WIDTH 1
+#define M98925_THERMSHDN_END_CLR_MASK (1<<1)
+#define M98925_THERMSHDN_END_CLR_SHIFT 1
+#define M98925_THERMSHDN_END_CLR_WIDTH 1
+#define M98925_THERMSHDN_BGN_CLR_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_CLR_SHIFT 0
+#define M98925_THERMSHDN_BGN_CLR_WIDTH 1
+
+/* MAX98925_R00F_IRQ_CLEAR1 */
+#define M98925_SPKCURNT_CLR_MASK (1<<5)
+#define M98925_SPKCURNT_CLR_SHIFT 5
+#define M98925_SPKCURNT_CLR_WIDTH 1
+#define M98925_WATCHFAIL_CLR_MASK (1<<4)
+#define M98925_WATCHFAIL_CLR_SHIFT 4
+#define M98925_WATCHFAIL_CLR_WIDTH 1
+#define M98925_ALCINFH_CLR_MASK (1<<3)
+#define M98925_ALCINFH_CLR_SHIFT 3
+#define M98925_ALCINFH_CLR_WIDTH 1
+#define M98925_ALCACT_CLR_MASK (1<<2)
+#define M98925_ALCACT_CLR_SHIFT 2
+#define M98925_ALCACT_CLR_WIDTH 1
+#define M98925_ALCMUT_CLR_MASK (1<<1)
+#define M98925_ALCMUT_CLR_SHIFT 1
+#define M98925_ALCMUT_CLR_WIDTH 1
+#define M98925_ALCP_CLR_MASK (1<<0)
+#define M98925_ALCP_CLR_SHIFT 0
+#define M98925_ALCP_CLR_WIDTH 1
+
+/* MAX98925_R010_IRQ_CLEAR2 */
+#define M98925_SLOTOVRN_CLR_MASK (1<<6)
+#define M98925_SLOTOVRN_CLR_SHIFT 6
+#define M98925_SLOTOVRN_CLR_WIDTH 1
+#define M98925_INVALSLOT_CLR_MASK (1<<5)
+#define M98925_INVALSLOT_CLR_SHIFT 5
+#define M98925_INVALSLOT_CLR_WIDTH 1
+#define M98925_SLOTCNFLT_CLR_MASK (1<<4)
+#define M98925_SLOTCNFLT_CLR_SHIFT 4
+#define M98925_SLOTCNFLT_CLR_WIDTH 1
+#define M98925_VBSTOVFL_CLR_MASK (1<<3)
+#define M98925_VBSTOVFL_CLR_SHIFT 3
+#define M98925_VBSTOVFL_CLR_WIDTH 1
+#define M98925_VBATOVFL_CLR_MASK (1<<2)
+#define M98925_VBATOVFL_CLR_SHIFT 2
+#define M98925_VBATOVFL_CLR_WIDTH 1
+#define M98925_IMONOVFL_CLR_MASK (1<<1)
+#define M98925_IMONOVFL_CLR_SHIFT 1
+#define M98925_IMONOVFL_CLR_WIDTH 1
+#define M98925_VMONOVFL_CLR_MASK (1<<0)
+#define M98925_VMONOVFL_CLR_SHIFT 0
+#define M98925_VMONOVFL_CLR_WIDTH 1
+
+/* MAX98925_R011_MAP0 */
+#define M98925_ER_THERMWARN_EN_MASK (1<<7)
+#define M98925_ER_THERMWARN_EN_SHIFT 7
+#define M98925_ER_THERMWARN_EN_WIDTH 1
+#define M98925_ER_THERMWARN_MAP_MASK (0x07<<4)
+#define M98925_ER_THERMWARN_MAP_SHIFT 4
+#define M98925_ER_THERMWARN_MAP_WIDTH 3
+
+/* MAX98925_R012_MAP1 */
+#define M98925_ER_ALCMUT_EN_MASK (1<<7)
+#define M98925_ER_ALCMUT_EN_SHIFT 7
+#define M98925_ER_ALCMUT_EN_WIDTH 1
+#define M98925_ER_ALCMUT_MAP_MASK (0x07<<4)
+#define M98925_ER_ALCMUT_MAP_SHIFT 4
+#define M98925_ER_ALCMUT_MAP_WIDTH 3
+#define M98925_ER_ALCP_EN_MASK (1<<3)
+#define M98925_ER_ALCP_EN_SHIFT 3
+#define M98925_ER_ALCP_EN_WIDTH 1
+#define M98925_ER_ALCP_MAP_MASK (0x07<<0)
+#define M98925_ER_ALCP_MAP_SHIFT 0
+#define M98925_ER_ALCP_MAP_WIDTH 3
+
+/* MAX98925_R013_MAP2 */
+#define M98925_ER_ALCINFH_EN_MASK (1<<7)
+#define M98925_ER_ALCINFH_EN_SHIFT 7
+#define M98925_ER_ALCINFH_EN_WIDTH 1
+#define M98925_ER_ALCINFH_MAP_MASK (0x07<<4)
+#define M98925_ER_ALCINFH_MAP_SHIFT 4
+#define M98925_ER_ALCINFH_MAP_WIDTH 3
+#define M98925_ER_ALCACT_EN_MASK (1<<3)
+#define M98925_ER_ALCACT_EN_SHIFT 3
+#define M98925_ER_ALCACT_EN_WIDTH 1
+#define M98925_ER_ALCACT_MAP_MASK (0x07<<0)
+#define M98925_ER_ALCACT_MAP_SHIFT 0
+#define M98925_ER_ALCACT_MAP_WIDTH 3
+
+/* MAX98925_R014_MAP3 */
+#define M98925_ER_SPKCURNT_EN_MASK (1<<7)
+#define M98925_ER_SPKCURNT_EN_SHIFT 7
+#define M98925_ER_SPKCURNT_EN_WIDTH 1
+#define M98925_ER_SPKCURNT_MAP_MASK (0x07<<4)
+#define M98925_ER_SPKCURNT_MAP_SHIFT 4
+#define M98925_ER_SPKCURNT_MAP_WIDTH 3
+
+/* MAX98925_R015_MAP4 */
+/* RESERVED */
+
+/* MAX98925_R016_MAP5 */
+#define M98925_ER_IMONOVFL_EN_MASK (1<<7)
+#define M98925_ER_IMONOVFL_EN_SHIFT 7
+#define M98925_ER_IMONOVFL_EN_WIDTH 1
+#define M98925_ER_IMONOVFL_MAP_MASK (0x07<<4)
+#define M98925_ER_IMONOVFL_MAP_SHIFT 4
+#define M98925_ER_IMONOVFL_MAP_WIDTH 3
+#define M98925_ER_VMONOVFL_EN_MASK (1<<3)
+#define M98925_ER_VMONOVFL_EN_SHIFT 3
+#define M98925_ER_VMONOVFL_EN_WIDTH 1
+#define M98925_ER_VMONOVFL_MAP_MASK (0x07<<0)
+#define M98925_ER_VMONOVFL_MAP_SHIFT 0
+#define M98925_ER_VMONOVFL_MAP_WIDTH 3
+
+/* MAX98925_R017_MAP6 */
+#define M98925_ER_VBSTOVFL_EN_MASK (1<<7)
+#define M98925_ER_VBSTOVFL_EN_SHIFT 7
+#define M98925_ER_VBSTOVFL_EN_WIDTH 1
+#define M98925_ER_VBSTOVFL_MAP_MASK (0x07<<4)
+#define M98925_ER_VBSTOVFL_MAP_SHIFT 4
+#define M98925_ER_VBSTOVFL_MAP_WIDTH 3
+#define M98925_ER_VBATOVFL_EN_MASK (1<<3)
+#define M98925_ER_VBATOVFL_EN_SHIFT 3
+#define M98925_ER_VBATOVFL_EN_WIDTH 1
+#define M98925_ER_VBATOVFL_MAP_MASK (0x07<<0)
+#define M98925_ER_VBATOVFL_MAP_SHIFT 0
+#define M98925_ER_VBATOVFL_MAP_WIDTH 3
+
+/* MAX98925_R018_MAP7 */
+#define M98925_ER_INVALSLOT_EN_MASK (1<<7)
+#define M98925_ER_INVALSLOT_EN_SHIFT 7
+#define M98925_ER_INVALSLOT_EN_WIDTH 1
+#define M98925_ER_INVALSLOT_MAP_MASK (0x07<<4)
+#define M98925_ER_INVALSLOT_MAP_SHIFT 4
+#define M98925_ER_INVALSLOT_MAP_WIDTH 3
+#define M98925_ER_SLOTCNFLT_EN_MASK (1<<3)
+#define M98925_ER_SLOTCNFLT_EN_SHIFT 3
+#define M98925_ER_SLOTCNFLT_EN_WIDTH 1
+#define M98925_ER_SLOTCNFLT_MAP_MASK (0x07<<0)
+#define M98925_ER_SLOTCNFLT_MAP_SHIFT 0
+#define M98925_ER_SLOTCNFLT_MAP_WIDTH 3
+
+/* MAX98925_R019_MAP8 */
+#define M98925_ER_SLOTOVRN_EN_MASK (1<<3)
+#define M98925_ER_SLOTOVRN_EN_SHIFT 3
+#define M98925_ER_SLOTOVRN_EN_WIDTH 1
+#define M98925_ER_SLOTOVRN_MAP_MASK (0x07<<0)
+#define M98925_ER_SLOTOVRN_MAP_SHIFT 0
+#define M98925_ER_SLOTOVRN_MAP_WIDTH 3
+
+/* MAX98925_R01A_DAI_CLK_MODE1 */
+#define M98925_DAI_CLK_SOURCE_MASK (1<<6)
+#define M98925_DAI_CLK_SOURCE_SHIFT 6
+#define M98925_DAI_CLK_SOURCE_WIDTH 1
+#define M98925_MDLL_MULT_MASK (0x0F<<0)
+#define M98925_MDLL_MULT_SHIFT 0
+#define M98925_MDLL_MULT_WIDTH 4
+
+#define M98925_MDLL_MULT_MCLKx8 6
+#define M98925_MDLL_MULT_MCLKx16 8
+
+/* MAX98925_R01B_DAI_CLK_MODE2 */
+#define M98925_DAI_SR_MASK (0x0F<<4)
+#define M98925_DAI_SR_SHIFT 4
+#define M98925_DAI_SR_WIDTH 4
+#define M98925_DAI_MAS_MASK (1<<3)
+#define M98925_DAI_MAS_SHIFT 3
+#define M98925_DAI_MAS_WIDTH 1
+#define M98925_DAI_BSEL_MASK (0x07<<0)
+#define M98925_DAI_BSEL_SHIFT 0
+#define M98925_DAI_BSEL_WIDTH 3
+
+#define M98925_DAI_BSEL_32 (0 << M98925_DAI_BSEL_SHIFT)
+#define M98925_DAI_BSEL_48 (1 << M98925_DAI_BSEL_SHIFT)
+#define M98925_DAI_BSEL_64 (2 << M98925_DAI_BSEL_SHIFT)
+#define M98925_DAI_BSEL_256 (6 << M98925_DAI_BSEL_SHIFT)
+
+/* MAX98925_R01C_DAI_CLK_DIV_M_MSBS */
+#define M98925_DAI_M_MSBS_MASK (0xFF<<0)
+#define M98925_DAI_M_MSBS_SHIFT 0
+#define M98925_DAI_M_MSBS_WIDTH 8
+
+/* MAX98925_R01D_DAI_CLK_DIV_M_LSBS */
+#define M98925_DAI_M_LSBS_MASK (0xFF<<0)
+#define M98925_DAI_M_LSBS_SHIFT 0
+#define M98925_DAI_M_LSBS_WIDTH 8
+
+/* MAX98925_R01E_DAI_CLK_DIV_N_MSBS */
+#define M98925_DAI_N_MSBS_MASK (0x7F<<0)
+#define M98925_DAI_N_MSBS_SHIFT 0
+#define M98925_DAI_N_MSBS_WIDTH 7
+
+/* MAX98925_R01F_DAI_CLK_DIV_N_LSBS */
+#define M98925_DAI_N_LSBS_MASK (0xFF<<0)
+#define M98925_DAI_N_LSBS_SHIFT 0
+#define M98925_DAI_N_LSBS_WIDTH 8
+
+/* MAX98925_R020_FORMAT */
+#define M98925_DAI_CHANSZ_MASK (0x03<<6)
+#define M98925_DAI_CHANSZ_SHIFT 6
+#define M98925_DAI_CHANSZ_WIDTH 2
+#define M98925_DAI_EXTBCLK_HIZ_MASK (1<<4)
+#define M98925_DAI_EXTBCLK_HIZ_SHIFT 4
+#define M98925_DAI_EXTBCLK_HIZ_WIDTH 1
+#define M98925_DAI_WCI_MASK (1<<3)
+#define M98925_DAI_WCI_SHIFT 3
+#define M98925_DAI_WCI_WIDTH 1
+#define M98925_DAI_BCI_MASK (1<<2)
+#define M98925_DAI_BCI_SHIFT 2
+#define M98925_DAI_BCI_WIDTH 1
+#define M98925_DAI_DLY_MASK (1<<1)
+#define M98925_DAI_DLY_SHIFT 1
+#define M98925_DAI_DLY_WIDTH 1
+#define M98925_DAI_TDM_MASK (1<<0)
+#define M98925_DAI_TDM_SHIFT 0
+#define M98925_DAI_TDM_WIDTH 1
+
+#define M98925_DAI_CHANSZ_16 (1 << M98925_DAI_CHANSZ_SHIFT)
+#define M98925_DAI_CHANSZ_24 (2 << M98925_DAI_CHANSZ_SHIFT)
+#define M98925_DAI_CHANSZ_32 (3 << M98925_DAI_CHANSZ_SHIFT)
+
+/* MAX98925_R021_TDM_SLOT_SELECT */
+#define M98925_DAI_DO_EN_MASK (1<<7)
+#define M98925_DAI_DO_EN_SHIFT 7
+#define M98925_DAI_DO_EN_WIDTH 1
+#define M98925_DAI_DIN_EN_MASK (1<<6)
+#define M98925_DAI_DIN_EN_SHIFT 6
+#define M98925_DAI_DIN_EN_WIDTH 1
+#define M98925_DAI_INR_SOURCE_MASK (0x07<<3)
+#define M98925_DAI_INR_SOURCE_SHIFT 3
+#define M98925_DAI_INR_SOURCE_WIDTH 3
+#define M98925_DAI_INL_SOURCE_MASK (0x07<<0)
+#define M98925_DAI_INL_SOURCE_SHIFT 0
+#define M98925_DAI_INL_SOURCE_WIDTH 3
+
+/* MAX98925_R022_DOUT_CFG_VMON */
+#define M98925_DAI_VMON_EN_MASK (1<<5)
+#define M98925_DAI_VMON_EN_SHIFT 5
+#define M98925_DAI_VMON_EN_WIDTH 1
+#define M98925_DAI_VMON_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_VMON_SLOT_SHIFT 0
+#define M98925_DAI_VMON_SLOT_WIDTH 5
+
+#define M98925_DAI_VMON_SLOT_00_01 (0 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_01_02 (1 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_02_03 (2 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_03_04 (3 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_04_05 (4 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_05_06 (5 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_06_07 (6 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_07_08 (7 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_08_09 (8 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_09_0A (9 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0A_0B (10 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0B_0C (11 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0C_0D (12 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0D_0E (13 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0E_0F (14 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0F_10 (15 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_10_11 (16 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_11_12 (17 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_12_13 (18 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_13_14 (19 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_14_15 (20 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_15_16 (21 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_16_17 (22 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_17_18 (23 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_18_19 (24 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_19_1A (25 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1A_1B (26 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1B_1C (27 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1C_1D (28 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1D_1E (29 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1E_1F (30 << M98925_DAI_VMON_SLOT_SHIFT)
+
+/* MAX98925_R023_DOUT_CFG_IMON */
+#define M98925_DAI_IMON_EN_MASK (1<<5)
+#define M98925_DAI_IMON_EN_SHIFT 5
+#define M98925_DAI_IMON_EN_WIDTH 1
+#define M98925_DAI_IMON_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_IMON_SLOT_SHIFT 0
+#define M98925_DAI_IMON_SLOT_WIDTH 5
+
+#define M98925_DAI_IMON_SLOT_00_01 (0 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_01_02 (1 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_02_03 (2 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_03_04 (3 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_04_05 (4 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_05_06 (5 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_06_07 (6 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_07_08 (7 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_08_09 (8 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_09_0A (9 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0A_0B (10 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0B_0C (11 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0C_0D (12 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0D_0E (13 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0E_0F (14 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0F_10 (15 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_10_11 (16 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_11_12 (17 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_12_13 (18 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_13_14 (19 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_14_15 (20 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_15_16 (21 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_16_17 (22 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_17_18 (23 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_18_19 (24 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_19_1A (25 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1A_1B (26 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1B_1C (27 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1C_1D (28 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1D_1E (29 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1E_1F (30 << M98925_DAI_IMON_SLOT_SHIFT)
+
+/* MAX98925_R024_DOUT_CFG_VBAT */
+#define M98925_DAI_VBAT_EN_MASK (1<<5)
+#define M98925_DAI_VBAT_EN_SHIFT 5
+#define M98925_DAI_VBAT_EN_WIDTH 1
+#define M98925_DAI_VBAT_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_VBAT_SLOT_SHIFT 0
+#define M98925_DAI_VBAT_SLOT_WIDTH 5
+
+/* MAX98925_R025_DOUT_CFG_VBST */
+#define M98925_DAI_VBST_EN_MASK (1<<5)
+#define M98925_DAI_VBST_EN_SHIFT 5
+#define M98925_DAI_VBST_EN_WIDTH 1
+#define M98925_DAI_VBST_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_VBST_SLOT_SHIFT 0
+#define M98925_DAI_VBST_SLOT_WIDTH 5
+
+/* MAX98925_R026_DOUT_CFG_FLAG */
+#define M98925_DAI_FLAG_EN_MASK (1<<5)
+#define M98925_DAI_FLAG_EN_SHIFT 5
+#define M98925_DAI_FLAG_EN_WIDTH 1
+#define M98925_DAI_FLAG_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_FLAG_SLOT_SHIFT 0
+#define M98925_DAI_FLAG_SLOT_WIDTH 5
+
+/* MAX98925_R027_DOUT_HIZ_CFG1 */
+#define M98925_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG1_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG1_WIDTH 8
+
+/* MAX98925_R028_DOUT_HIZ_CFG2 */
+#define M98925_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG2_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG2_WIDTH 8
+
+/* MAX98925_R029_DOUT_HIZ_CFG3 */
+#define M98925_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG3_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG3_WIDTH 8
+
+/* MAX98925_R02A_DOUT_HIZ_CFG4 */
+#define M98925_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG4_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG4_WIDTH 8
+
+/* MAX98925_R02B_DOUT_DRV_STRENGTH */
+#define M98925_DAI_OUT_DRIVE_MASK (0x03<<0)
+#define M98925_DAI_OUT_DRIVE_SHIFT 0
+#define M98925_DAI_OUT_DRIVE_WIDTH 2
+
+/* MAX98925_R02C_FILTERS */
+#define M98925_ADC_DITHER_EN_MASK (1<<7)
+#define M98925_ADC_DITHER_EN_SHIFT 7
+#define M98925_ADC_DITHER_EN_WIDTH 1
+#define M98925_IV_DCB_EN_MASK (1<<6)
+#define M98925_IV_DCB_EN_SHIFT 6
+#define M98925_IV_DCB_EN_WIDTH 1
+#define M98925_DAC_DITHER_EN_MASK (1<<4)
+#define M98925_DAC_DITHER_EN_SHIFT 4
+#define M98925_DAC_DITHER_EN_WIDTH 1
+#define M98925_DAC_FILTER_MODE_MASK (1<<3)
+#define M98925_DAC_FILTER_MODE_SHIFT 3
+#define M98925_DAC_FILTER_MODE_WIDTH 1
+#define M98925_DAC_HPF_MASK (0x07<<0)
+#define M98925_DAC_HPF_SHIFT 0
+#define M98925_DAC_HPF_WIDTH 3
+#define M98925_DAC_HPF_DISABLE (0 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_DC_BLOCK (1 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_100 (2 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_200 (3 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_400 (4 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_800 (5 << M98925_DAC_HPF_SHIFT)
+
+/* MAX98925_R02D_GAIN */
+#define M98925_DAC_IN_SEL_MASK (0x03<<5)
+#define M98925_DAC_IN_SEL_SHIFT 5
+#define M98925_DAC_IN_SEL_WIDTH 2
+#define M98925_SPK_GAIN_MASK (0x1F<<0)
+#define M98925_SPK_GAIN_SHIFT 0
+#define M98925_SPK_GAIN_WIDTH 5
+
+#define M98925_DAC_IN_SEL_LEFT_DAI (0 << M98925_DAC_IN_SEL_SHIFT)
+#define M98925_DAC_IN_SEL_RIGHT_DAI (1 << M98925_DAC_IN_SEL_SHIFT)
+#define M98925_DAC_IN_SEL_SUMMED_DAI (2 << M98925_DAC_IN_SEL_SHIFT)
+#define M98925_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << M98925_DAC_IN_SEL_SHIFT)
+
+/* MAX98925_R02E_GAIN_RAMPING */
+#define M98925_SPK_RMP_EN_MASK (1<<1)
+#define M98925_SPK_RMP_EN_SHIFT 1
+#define M98925_SPK_RMP_EN_WIDTH 1
+#define M98925_SPK_ZCD_EN_MASK (1<<0)
+#define M98925_SPK_ZCD_EN_SHIFT 0
+#define M98925_SPK_ZCD_EN_WIDTH 1
+
+/* MAX98925_R02F_SPK_AMP */
+#define M98925_SPK_MODE_MASK (1<<0)
+#define M98925_SPK_MODE_SHIFT 0
+#define M98925_SPK_MODE_WIDTH 1
+
+/* MAX98925_R030_THRESHOLD */
+#define M98925_ALC_EN_MASK (1<<5)
+#define M98925_ALC_EN_SHIFT 5
+#define M98925_ALC_EN_WIDTH 1
+#define M98925_ALC_TH_MASK (0x1F<<0)
+#define M98925_ALC_TH_SHIFT 0
+#define M98925_ALC_TH_WIDTH 5
+
+/* MAX98925_R031_ALC_ATTACK */
+#define M98925_ALC_ATK_STEP_MASK (0x0F<<4)
+#define M98925_ALC_ATK_STEP_SHIFT 4
+#define M98925_ALC_ATK_STEP_WIDTH 4
+#define M98925_ALC_ATK_RATE_MASK (0x7<<0)
+#define M98925_ALC_ATK_RATE_SHIFT 0
+#define M98925_ALC_ATK_RATE_WIDTH 3
+
+/* MAX98925_R032_ALC_ATTEN_RLS */
+#define M98925_ALC_MAX_ATTEN_MASK (0x0F<<4)
+#define M98925_ALC_MAX_ATTEN_SHIFT 4
+#define M98925_ALC_MAX_ATTEN_WIDTH 4
+#define M98925_ALC_RLS_RATE_MASK (0x7<<0)
+#define M98925_ALC_RLS_RATE_SHIFT 0
+#define M98925_ALC_RLS_RATE_WIDTH 3
+
+/* MAX98925_R033_ALC_HOLD_RLS */
+#define M98925_ALC_RLS_TGR_MASK (1<<0)
+#define M98925_ALC_RLS_TGR_SHIFT 0
+#define M98925_ALC_RLS_TGR_WIDTH 1
+
+/* MAX98925_R034_ALC_CONFIGURATION */
+#define M98925_ALC_MUTE_EN_MASK (1<<7)
+#define M98925_ALC_MUTE_EN_SHIFT 7
+#define M98925_ALC_MUTE_EN_WIDTH 1
+#define M98925_ALC_MUTE_DLY_MASK (0x07<<4)
+#define M98925_ALC_MUTE_DLY_SHIFT 4
+#define M98925_ALC_MUTE_DLY_WIDTH 3
+#define M98925_ALC_RLS_DBT_MASK (0x07<<0)
+#define M98925_ALC_RLS_DBT_SHIFT 0
+#define M98925_ALC_RLS_DBT_WIDTH 3
+
+/* MAX98925_R035_BOOST_CONVERTER */
+#define M98925_BST_SYNC_MASK (1<<7)
+#define M98925_BST_SYNC_SHIFT 7
+#define M98925_BST_SYNC_WIDTH 1
+#define M98925_BST_PHASE_MASK (0x03<<4)
+#define M98925_BST_PHASE_SHIFT 4
+#define M98925_BST_PHASE_WIDTH 2
+#define M98925_BST_SKIP_MODE_MASK (0x03<<0)
+#define M98925_BST_SKIP_MODE_SHIFT 0
+#define M98925_BST_SKIP_MODE_WIDTH 2
+
+/* MAX98925_R036_BLOCK_ENABLE */
+#define M98925_BST_EN_MASK (1<<7)
+#define M98925_BST_EN_SHIFT 7
+#define M98925_BST_EN_WIDTH 1
+#define M98925_WATCH_EN_MASK (1<<6)
+#define M98925_WATCH_EN_SHIFT 6
+#define M98925_WATCH_EN_WIDTH 1
+#define M98925_CLKMON_EN_MASK (1<<5)
+#define M98925_CLKMON_EN_SHIFT 5
+#define M98925_CLKMON_EN_WIDTH 1
+#define M98925_SPK_EN_MASK (1<<4)
+#define M98925_SPK_EN_SHIFT 4
+#define M98925_SPK_EN_WIDTH 1
+#define M98925_ADC_VBST_EN_MASK (1<<3)
+#define M98925_ADC_VBST_EN_SHIFT 3
+#define M98925_ADC_VBST_EN_WIDTH 1
+#define M98925_ADC_VBAT_EN_MASK (1<<2)
+#define M98925_ADC_VBAT_EN_SHIFT 2
+#define M98925_ADC_VBAT_EN_WIDTH 1
+#define M98925_ADC_IMON_EN_MASK (1<<1)
+#define M98925_ADC_IMON_EN_SHIFT 1
+#define M98925_ADC_IMON_EN_WIDTH 1
+#define M98925_ADC_VMON_EN_MASK (1<<0)
+#define M98925_ADC_VMON_EN_SHIFT 0
+#define M98925_ADC_VMON_EN_WIDTH 1
+
+/* MAX98925_R037_CONFIGURATION */
+#define M98925_BST_VOUT_MASK (0x0F<<4)
+#define M98925_BST_VOUT_SHIFT 4
+#define M98925_BST_VOUT_WIDTH 4
+#define M98925_THERMWARN_LEVEL_MASK (0x03<<2)
+#define M98925_THERMWARN_LEVEL_SHIFT 2
+#define M98925_THERMWARN_LEVEL_WIDTH 2
+#define M98925_WATCH_TIME_MASK (0x03<<0)
+#define M98925_WATCH_TIME_SHIFT 0
+#define M98925_WATCH_TIME_WIDTH 2
+
+/* MAX98925_R038_GLOBAL_ENABLE */
+#define M98925_EN_MASK (1<<7)
+#define M98925_EN_SHIFT 7
+#define M98925_EN_WIDTH 1
+
+/* MAX98925_R03A_BOOST_LIMITER */
+#define M98925_BST_ILIM_MASK (0x1F<<3)
+#define M98925_BST_ILIM_SHIFT 3
+#define M98925_BST_ILIM_WIDTH 5
+
+/* MAX98925_R0FF_VERSION */
+#define M98925_REV_ID_MASK (0xFF<<0)
+#define M98925_REV_ID_SHIFT 0
+#define M98925_REV_ID_WIDTH 8
+
+struct max98925_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct max98925_pdata *pdata;
+ unsigned int sysclk;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spk_gain;
+ unsigned int ch_size;
+};
+#endif
--
1.9.3
2
1