summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/aoa/codecs/tas.c2
-rw-r--r--sound/core/control.c64
-rw-r--r--sound/core/init.c2
-rw-r--r--sound/core/pcm_lib.c14
-rw-r--r--sound/firewire/Kconfig11
-rw-r--r--sound/firewire/Makefile2
-rw-r--r--sound/firewire/isight.c755
-rw-r--r--sound/firewire/iso-resources.c5
-rw-r--r--sound/firewire/packets-buffer.c2
-rw-r--r--sound/i2c/other/Makefile2
-rw-r--r--sound/i2c/other/tea575x-tuner.c153
-rw-r--r--sound/oss/Kconfig4
-rw-r--r--sound/oss/Makefile1
-rw-r--r--sound/oss/ac97_codec.c1203
-rw-r--r--sound/oss/au1550_ac97.c2147
-rw-r--r--sound/pci/Kconfig27
-rw-r--r--sound/pci/Makefile1
-rw-r--r--sound/pci/asihpi/asihpi.c328
-rw-r--r--sound/pci/asihpi/hpi6000.c39
-rw-r--r--sound/pci/asihpi/hpi6205.c95
-rw-r--r--sound/pci/asihpi/hpi_internal.h19
-rw-r--r--sound/pci/asihpi/hpicmn.c10
-rw-r--r--sound/pci/asihpi/hpicmn.h2
-rw-r--r--sound/pci/asihpi/hpifunc.c27
-rw-r--r--sound/pci/asihpi/hpimsgx.c31
-rw-r--r--sound/pci/asihpi/hpioctl.c63
-rw-r--r--sound/pci/au88x0/au8810.h2
-rw-r--r--sound/pci/au88x0/au8820.h2
-rw-r--r--sound/pci/au88x0/au8830.h2
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c13
-rw-r--r--sound/pci/emu10k1/emufx.c5
-rw-r--r--sound/pci/emu10k1/emumixer.c10
-rw-r--r--sound/pci/es1968.c78
-rw-r--r--sound/pci/fm801.c371
-rw-r--r--sound/pci/intel8x0m.c4
-rw-r--r--sound/pci/lola/Makefile4
-rw-r--r--sound/pci/lola/lola.c791
-rw-r--r--sound/pci/lola/lola.h527
-rw-r--r--sound/pci/lola/lola_clock.c323
-rw-r--r--sound/pci/lola/lola_mixer.c839
-rw-r--r--sound/pci/lola/lola_pcm.c706
-rw-r--r--sound/pci/lola/lola_proc.c222
-rw-r--r--sound/ppc/tumbler.c2
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c2
-rw-r--r--sound/soc/au1x/db1200.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c13
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c166
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c42
-rw-r--r--sound/soc/blackfin/bf5xx-ad193x.c56
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c45
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c42
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c23
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c172
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c159
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h16
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c42
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c23
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c110
-rw-r--r--sound/soc/codecs/88pm860x-codec.c2
-rw-r--r--sound/soc/codecs/Kconfig20
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ad193x.c23
-rw-r--r--sound/soc/codecs/ad1980.c2
-rw-r--r--sound/soc/codecs/ad73311.c2
-rw-r--r--sound/soc/codecs/ak4535.c19
-rw-r--r--sound/soc/codecs/ak4641.c664
-rw-r--r--sound/soc/codecs/ak4641.h47
-rw-r--r--sound/soc/codecs/ak4671.c18
-rw-r--r--sound/soc/codecs/cx20442.c18
-rw-r--r--sound/soc/codecs/dmic.c26
-rw-r--r--sound/soc/codecs/jz4740.c18
-rw-r--r--sound/soc/codecs/max98088.c87
-rw-r--r--sound/soc/codecs/max98088.h13
-rw-r--r--sound/soc/codecs/max98095.c2396
-rw-r--r--sound/soc/codecs/max98095.h299
-rw-r--r--sound/soc/codecs/sn95031.c17
-rw-r--r--sound/soc/codecs/spdif_transciever.c8
-rw-r--r--sound/soc/codecs/ssm2602.c472
-rw-r--r--sound/soc/codecs/ssm2602.h6
-rw-r--r--sound/soc/codecs/tlv320aic23.c19
-rw-r--r--sound/soc/codecs/tlv320aic3x.c3
-rw-r--r--sound/soc/codecs/tlv320dac33.c17
-rw-r--r--sound/soc/codecs/tlv320dac33.h2
-rw-r--r--sound/soc/codecs/tpa6130a2.c4
-rw-r--r--sound/soc/codecs/tpa6130a2.h2
-rw-r--r--sound/soc/codecs/twl6040.c6
-rw-r--r--sound/soc/codecs/uda134x.c2
-rw-r--r--sound/soc/codecs/wm1250-ev1.c108
-rw-r--r--sound/soc/codecs/wm8711.c18
-rw-r--r--sound/soc/codecs/wm8728.c18
-rw-r--r--sound/soc/codecs/wm8731.c22
-rw-r--r--sound/soc/codecs/wm8903.c46
-rw-r--r--sound/soc/codecs/wm8915.c2931
-rw-r--r--sound/soc/codecs/wm8915.h3717
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c1051
-rw-r--r--sound/soc/codecs/wm8962.c63
-rw-r--r--sound/soc/codecs/wm8993.c3
-rw-r--r--sound/soc/codecs/wm8994.c395
-rw-r--r--sound/soc/codecs/wm8994.h97
-rw-r--r--sound/soc/codecs/wm8995.c4
-rw-r--r--sound/soc/codecs/wm9705.c18
-rw-r--r--sound/soc/codecs/wm9712.c18
-rw-r--r--sound/soc/codecs/wm9713.c19
-rw-r--r--sound/soc/codecs/wm_hubs.c24
-rw-r--r--sound/soc/davinci/davinci-mcasp.c21
-rw-r--r--sound/soc/imx/imx-ssi.c6
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c2
-rw-r--r--sound/soc/jz4740/qi_lb60.c46
-rw-r--r--sound/soc/mid-x86/sst_platform.c10
-rw-r--r--sound/soc/omap/omap-mcbsp.c6
-rw-r--r--sound/soc/omap/omap-mcbsp.h2
-rw-r--r--sound/soc/omap/omap-pcm.c7
-rw-r--r--sound/soc/omap/omap-pcm.h2
-rw-r--r--sound/soc/omap/rx51.c2
-rw-r--r--sound/soc/pxa/Kconfig9
-rw-r--r--sound/soc/pxa/Makefile2
-rw-r--r--sound/soc/pxa/corgi.c2
-rw-r--r--sound/soc/pxa/hx4700.c255
-rw-r--r--sound/soc/pxa/poodle.c2
-rw-r--r--sound/soc/pxa/spitz.c41
-rw-r--r--sound/soc/samsung/Kconfig15
-rw-r--r--sound/soc/samsung/Makefile4
-rw-r--r--sound/soc/samsung/goni_wm8994.c9
-rw-r--r--sound/soc/samsung/neo1973_wm8753.c1
-rw-r--r--sound/soc/samsung/smdk_wm8580pcm.c206
-rw-r--r--sound/soc/samsung/speyside.c332
-rw-r--r--sound/soc/sh/fsi.c188
-rw-r--r--sound/soc/soc-cache.c612
-rw-r--r--sound/soc/soc-core.c193
-rw-r--r--sound/soc/soc-dapm.c611
-rw-r--r--sound/soc/soc-jack.c2
-rw-r--r--sound/soc/soc-utils.c53
-rw-r--r--sound/soc/tegra/Kconfig38
-rw-r--r--sound/soc/tegra/Makefile14
-rw-r--r--sound/soc/tegra/harmony.c394
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c9
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h2
-rw-r--r--sound/soc/tegra/tegra_i2s.c2
-rw-r--r--sound/soc/tegra/tegra_wm8903.c475
-rw-r--r--sound/soc/tegra/trimslice.c228
-rw-r--r--sound/usb/6fire/control.c105
-rw-r--r--sound/usb/6fire/control.h17
-rw-r--r--sound/usb/6fire/firmware.c73
-rw-r--r--sound/usb/6fire/pcm.c97
-rw-r--r--sound/usb/Kconfig10
-rw-r--r--sound/usb/clock.c11
-rw-r--r--sound/usb/debug.h2
-rw-r--r--sound/usb/format.c1
-rw-r--r--sound/usb/mixer.c10
-rw-r--r--sound/usb/mixer_quirks.c12
-rw-r--r--sound/usb/quirks-table.h47
-rw-r--r--sound/usb/quirks.c1
152 files changed, 20055 insertions, 6731 deletions
diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c
index 58804c7acfcf..fd2188c3df2b 100644
--- a/sound/aoa/codecs/tas.c
+++ b/sound/aoa/codecs/tas.c
@@ -170,7 +170,7 @@ static void tas_set_volume(struct tas *tas)
/* analysing the volume and mixer tables shows
* that they are similar enough when we shift
* the mixer table down by 4 bits. The error
- * is minuscule, in just one item the error
+ * is miniscule, in just one item the error
* is 1, at a value of 0x07f17b (mixer table
* value is 0x07f17a) */
tmp = tas_gaintable[left];
diff --git a/sound/core/control.c b/sound/core/control.c
index a08ad57c49b6..5d98194bcad5 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -366,6 +366,70 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
EXPORT_SYMBOL(snd_ctl_add);
/**
+ * snd_ctl_replace - replace the control instance of the card
+ * @card: the card instance
+ * @kcontrol: the control instance to replace
+ * @add_on_replace: add the control if not already added
+ *
+ * Replaces the given control. If the given control does not exist
+ * and the add_on_replace flag is set, the control is added. If the
+ * control exists, it is destroyed first.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ *
+ * It frees automatically the control which cannot be added or replaced.
+ */
+int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
+ bool add_on_replace)
+{
+ struct snd_ctl_elem_id id;
+ unsigned int idx;
+ struct snd_kcontrol *old;
+ int ret;
+
+ if (!kcontrol)
+ return -EINVAL;
+ if (snd_BUG_ON(!card || !kcontrol->info)) {
+ ret = -EINVAL;
+ goto error;
+ }
+ id = kcontrol->id;
+ down_write(&card->controls_rwsem);
+ old = snd_ctl_find_id(card, &id);
+ if (!old) {
+ if (add_on_replace)
+ goto add;
+ up_write(&card->controls_rwsem);
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = snd_ctl_remove(card, old);
+ if (ret < 0) {
+ up_write(&card->controls_rwsem);
+ goto error;
+ }
+add:
+ if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
+ up_write(&card->controls_rwsem);
+ ret = -ENOMEM;
+ goto error;
+ }
+ list_add_tail(&kcontrol->list, &card->controls);
+ card->controls_count += kcontrol->count;
+ kcontrol->id.numid = card->last_numid + 1;
+ card->last_numid += kcontrol->count;
+ up_write(&card->controls_rwsem);
+ for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+ return 0;
+
+error:
+ snd_ctl_free_one(kcontrol);
+ return ret;
+}
+EXPORT_SYMBOL(snd_ctl_replace);
+
+/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
* @kcontrol: the control instance to remove
diff --git a/sound/core/init.c b/sound/core/init.c
index a0080aa45ae9..30ecad41403c 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -514,7 +514,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
id = card->id;
if (*id == '\0')
- strcpy(id, "default");
+ strcpy(id, "Default");
while (1) {
if (loops-- == 0) {
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 64449cb8f873..abfeff1611ce 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -189,6 +189,7 @@ static void xrun(struct snd_pcm_substream *substream)
#define XRUN_LOG_CNT 10
struct hwptr_log_entry {
+ unsigned int in_interrupt;
unsigned long jiffies;
snd_pcm_uframes_t pos;
snd_pcm_uframes_t period_size;
@@ -204,7 +205,7 @@ struct snd_pcm_hwptr_log {
};
static void xrun_log(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t pos)
+ snd_pcm_uframes_t pos, int in_interrupt)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
@@ -220,6 +221,7 @@ static void xrun_log(struct snd_pcm_substream *substream,
return;
}
entry = &log->entries[log->idx];
+ entry->in_interrupt = in_interrupt;
entry->jiffies = jiffies;
entry->pos = pos;
entry->period_size = runtime->period_size;
@@ -246,9 +248,11 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
entry = &log->entries[idx];
if (entry->period_size == 0)
break;
- snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
+ snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
"hwptr=%ld/%ld\n",
- name, entry->jiffies, (unsigned long)entry->pos,
+ name, entry->in_interrupt ? "[Q] " : "",
+ entry->jiffies,
+ (unsigned long)entry->pos,
(unsigned long)entry->period_size,
(unsigned long)entry->buffer_size,
(unsigned long)entry->old_hw_ptr,
@@ -262,7 +266,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos) do { } while (0)
+#define xrun_log(substream, pos, in_interrupt) do { } while (0)
#define xrun_log_show(substream) do { } while (0)
#endif
@@ -326,7 +330,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
pos -= pos % runtime->min_align;
if (xrun_debug(substream, XRUN_DEBUG_LOG))
- xrun_log(substream, pos);
+ xrun_log(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
if (in_interrupt) {
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index e486f48660fb..26071489970b 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -22,4 +22,15 @@ config SND_FIREWIRE_SPEAKERS
To compile this driver as a module, choose M here: the module
will be called snd-firewire-speakers.
+config SND_ISIGHT
+ tristate "Apple iSight microphone"
+ select SND_PCM
+ select SND_FIREWIRE_LIB
+ help
+ Say Y here to include support for the front and rear microphones
+ of the Apple iSight web camera.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-isight.
+
endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index e5b1634d9ad4..d71ed8935f76 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,6 +1,8 @@
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp.o
snd-firewire-speakers-objs := speakers.o
+snd-isight-objs := isight.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
+obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
new file mode 100644
index 000000000000..86ee16ca365e
--- /dev/null
+++ b/sound/firewire/isight.c
@@ -0,0 +1,755 @@
+/*
+ * Apple iSight audio driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "packets-buffer.h"
+
+#define OUI_APPLE 0x000a27
+#define MODEL_APPLE_ISIGHT 0x000008
+#define SW_ISIGHT_AUDIO 0x000010
+
+#define REG_AUDIO_ENABLE 0x000
+#define AUDIO_ENABLE 0x80000000
+#define REG_DEF_AUDIO_GAIN 0x204
+#define REG_GAIN_RAW_START 0x210
+#define REG_GAIN_RAW_END 0x214
+#define REG_GAIN_DB_START 0x218
+#define REG_GAIN_DB_END 0x21c
+#define REG_SAMPLE_RATE_INQUIRY 0x280
+#define REG_ISO_TX_CONFIG 0x300
+#define SPEED_SHIFT 16
+#define REG_SAMPLE_RATE 0x400
+#define RATE_48000 0x80000000
+#define REG_GAIN 0x500
+#define REG_MUTE 0x504
+
+#define MAX_FRAMES_PER_PACKET 475
+
+#define QUEUE_LENGTH 20
+
+struct isight {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct fw_device *device;
+ u64 audio_base;
+ struct fw_address_handler iris_handler;
+ struct snd_pcm_substream *pcm;
+ struct mutex mutex;
+ struct iso_packets_buffer buffer;
+ struct fw_iso_resources resources;
+ struct fw_iso_context *context;
+ bool pcm_active;
+ bool pcm_running;
+ bool first_packet;
+ int packet_index;
+ u32 total_samples;
+ unsigned int buffer_pointer;
+ unsigned int period_counter;
+ s32 gain_min, gain_max;
+ unsigned int gain_tlv[4];
+};
+
+struct audio_payload {
+ __be32 sample_count;
+ __be32 signature;
+ __be32 sample_total;
+ __be32 reserved;
+ __be16 samples[2 * MAX_FRAMES_PER_PACKET];
+};
+
+MODULE_DESCRIPTION("iSight audio driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+static struct fw_iso_packet audio_packet = {
+ .payload_length = sizeof(struct audio_payload),
+ .interrupt = 1,
+ .header_length = 4,
+};
+
+static void isight_update_pointers(struct isight *isight, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime = isight->pcm->runtime;
+ unsigned int ptr;
+
+ smp_wmb(); /* update buffer data before buffer pointer */
+
+ ptr = isight->buffer_pointer;
+ ptr += count;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ ACCESS_ONCE(isight->buffer_pointer) = ptr;
+
+ isight->period_counter += count;
+ if (isight->period_counter >= runtime->period_size) {
+ isight->period_counter -= runtime->period_size;
+ snd_pcm_period_elapsed(isight->pcm);
+ }
+}
+
+static void isight_samples(struct isight *isight,
+ const __be16 *samples, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime;
+ unsigned int count1;
+
+ if (!ACCESS_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ if (isight->buffer_pointer + count <= runtime->buffer_size) {
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count1 * 4);
+ samples += count1 * 2;
+ memcpy(runtime->dma_area, samples, (count - count1) * 4);
+ }
+
+ isight_update_pointers(isight, count);
+}
+
+static void isight_pcm_abort(struct isight *isight)
+{
+ unsigned long flags;
+
+ if (ACCESS_ONCE(isight->pcm_active)) {
+ snd_pcm_stream_lock_irqsave(isight->pcm, flags);
+ if (snd_pcm_running(isight->pcm))
+ snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(isight->pcm, flags);
+ }
+}
+
+static void isight_dropped_samples(struct isight *isight, unsigned int total)
+{
+ struct snd_pcm_runtime *runtime;
+ u32 dropped;
+ unsigned int count1;
+
+ if (!ACCESS_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ dropped = total - isight->total_samples;
+ if (dropped < runtime->buffer_size) {
+ if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, dropped * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, count1 * 4);
+ memset(runtime->dma_area, 0, (dropped - count1) * 4);
+ }
+ isight_update_pointers(isight, dropped);
+ } else {
+ isight_pcm_abort(isight);
+ }
+}
+
+static void isight_packet(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
+{
+ struct isight *isight = data;
+ const struct audio_payload *payload;
+ unsigned int index, length, count, total;
+ int err;
+
+ if (isight->packet_index < 0)
+ return;
+ index = isight->packet_index;
+ payload = isight->buffer.packets[index].buffer;
+ length = be32_to_cpup(header) >> 16;
+
+ if (likely(length >= 16 &&
+ payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
+ count = be32_to_cpu(payload->sample_count);
+ if (likely(count <= (length - 16) / 4)) {
+ total = be32_to_cpu(payload->sample_total);
+ if (unlikely(total != isight->total_samples)) {
+ if (!isight->first_packet)
+ isight_dropped_samples(isight, total);
+ isight->first_packet = false;
+ isight->total_samples = total;
+ }
+
+ isight_samples(isight, payload->samples, count);
+ isight->total_samples += count;
+ }
+ }
+
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[index].offset);
+ if (err < 0) {
+ dev_err(&isight->unit->device, "queueing error: %d\n", err);
+ isight_pcm_abort(isight);
+ isight->packet_index = -1;
+ return;
+ }
+
+ if (++index >= QUEUE_LENGTH)
+ index = 0;
+ isight->packet_index = index;
+}
+
+static int isight_connect(struct isight *isight)
+{
+ int ch, err, rcode, errors = 0;
+ __be32 value;
+
+retry_after_bus_reset:
+ ch = fw_iso_resources_allocate(&isight->resources,
+ sizeof(struct audio_payload),
+ isight->device->max_speed);
+ if (ch < 0) {
+ err = ch;
+ goto error;
+ }
+
+ value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
+ for (;;) {
+ rcode = fw_run_transaction(
+ isight->device->card,
+ TCODE_WRITE_QUADLET_REQUEST,
+ isight->device->node_id,
+ isight->resources.generation,
+ isight->device->max_speed,
+ isight->audio_base + REG_ISO_TX_CONFIG,
+ &value, 4);
+ if (rcode == RCODE_COMPLETE) {
+ return 0;
+ } else if (rcode == RCODE_GENERATION) {
+ fw_iso_resources_free(&isight->resources);
+ goto retry_after_bus_reset;
+ } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+ err = -EIO;
+ goto err_resources;
+ }
+ msleep(5);
+ }
+
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+error:
+ return err;
+}
+
+static int isight_open(struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 4 * 1024 * 1024,
+ .period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct isight *isight = substream->private_data;
+
+ substream->runtime->hw = hardware;
+
+ return iso_packets_buffer_init(&isight->buffer, isight->unit,
+ QUEUE_LENGTH,
+ sizeof(struct audio_payload),
+ DMA_FROM_DEVICE);
+}
+
+static int isight_close(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ iso_packets_buffer_destroy(&isight->buffer, isight->unit);
+
+ return 0;
+}
+
+static int isight_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct isight *isight = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ ACCESS_ONCE(isight->pcm_active) = true;
+
+ return 0;
+}
+
+static int reg_read(struct isight *isight, int offset, __be32 *value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
+ isight->audio_base + offset, value, 4);
+}
+
+static int reg_write(struct isight *isight, int offset, __be32 value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + offset, &value, 4);
+}
+
+static void isight_stop_streaming(struct isight *isight)
+{
+ if (!isight->context)
+ return;
+
+ fw_iso_context_stop(isight->context);
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+}
+
+static int isight_hw_free(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ ACCESS_ONCE(isight->pcm_active) = false;
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int isight_start_streaming(struct isight *isight)
+{
+ unsigned int i;
+ int err;
+
+ if (isight->context) {
+ if (isight->packet_index < 0)
+ isight_stop_streaming(isight);
+ else
+ return 0;
+ }
+
+ err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));
+ if (err < 0)
+ goto error;
+
+ err = isight_connect(isight);
+ if (err < 0)
+ goto error;
+
+ err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));
+ if (err < 0)
+ goto err_resources;
+
+ isight->context = fw_iso_context_create(isight->device->card,
+ FW_ISO_CONTEXT_RECEIVE,
+ isight->resources.channel,
+ isight->device->max_speed,
+ 4, isight_packet, isight);
+ if (IS_ERR(isight->context)) {
+ err = PTR_ERR(isight->context);
+ isight->context = NULL;
+ goto err_resources;
+ }
+
+ for (i = 0; i < QUEUE_LENGTH; ++i) {
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[i].offset);
+ if (err < 0)
+ goto err_context;
+ }
+
+ isight->first_packet = true;
+ isight->packet_index = 0;
+
+ err = fw_iso_context_start(isight->context, -1, 0,
+ FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
+ if (err < 0)
+ goto err_context;
+
+ return 0;
+
+err_context:
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+error:
+ return err;
+}
+
+static int isight_prepare(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+ int err;
+
+ isight->buffer_pointer = 0;
+ isight->period_counter = 0;
+
+ mutex_lock(&isight->mutex);
+ err = isight_start_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return err;
+}
+
+static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct isight *isight = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ACCESS_ONCE(isight->pcm_running) = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ACCESS_ONCE(isight->pcm_running) = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ return ACCESS_ONCE(isight->buffer_pointer);
+}
+
+static int isight_create_pcm(struct isight *isight)
+{
+ static struct snd_pcm_ops ops = {
+ .open = isight_open,
+ .close = isight_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = isight_hw_params,
+ .hw_free = isight_hw_free,
+ .prepare = isight_prepare,
+ .trigger = isight_trigger,
+ .pointer = isight_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = isight;
+ strcpy(pcm->name, "iSight");
+ isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ isight->pcm->ops = &ops;
+
+ return 0;
+}
+
+static int isight_gain_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct isight *isight = ctl->private_data;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = isight->gain_min;
+ info->value.integer.max = isight->gain_max;
+
+ return 0;
+}
+
+static int isight_gain_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 gain;
+ int err;
+
+ err = reg_read(isight, REG_GAIN, &gain);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = (s32)be32_to_cpu(gain);
+
+ return 0;
+}
+
+static int isight_gain_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ if (value->value.integer.value[0] < isight->gain_min ||
+ value->value.integer.value[0] > isight->gain_max)
+ return -EINVAL;
+
+ return reg_write(isight, REG_GAIN,
+ cpu_to_be32(value->value.integer.value[0]));
+}
+
+static int isight_mute_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 mute;
+ int err;
+
+ err = reg_read(isight, REG_MUTE, &mute);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = !mute;
+
+ return 0;
+}
+
+static int isight_mute_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ return reg_write(isight, REG_MUTE,
+ (__force __be32)!value->value.integer.value[0]);
+}
+
+static int isight_create_mixer(struct isight *isight)
+{
+ static const struct snd_kcontrol_new gain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = isight_gain_info,
+ .get = isight_gain_get,
+ .put = isight_gain_put,
+ };
+ static const struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = isight_mute_get,
+ .put = isight_mute_put,
+ };
+ __be32 value;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ err = reg_read(isight, REG_GAIN_RAW_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_min = be32_to_cpu(value);
+
+ err = reg_read(isight, REG_GAIN_RAW_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_max = be32_to_cpu(value);
+
+ isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
+ isight->gain_tlv[1] = 2 * sizeof(unsigned int);
+
+ err = reg_read(isight, REG_GAIN_DB_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100;
+
+ err = reg_read(isight, REG_GAIN_DB_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100;
+
+ ctl = snd_ctl_new1(&gain_control, isight);
+ if (ctl)
+ ctl->tlv.p = isight->gain_tlv;
+ err = snd_ctl_add(isight->card, ctl);
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void isight_card_free(struct snd_card *card)
+{
+ struct isight *isight = card->private_data;
+
+ fw_iso_resources_destroy(&isight->resources);
+ fw_unit_put(isight->unit);
+ fw_device_put(isight->device);
+ mutex_destroy(&isight->mutex);
+}
+
+static u64 get_unit_base(struct fw_unit *unit)
+{
+ struct fw_csr_iterator i;
+ int key, value;
+
+ fw_csr_iterator_init(&i, unit->directory);
+ while (fw_csr_iterator_next(&i, &key, &value))
+ if (key == CSR_OFFSET)
+ return CSR_REGISTER_BASE + value * 4;
+ return 0;
+}
+
+static int isight_probe(struct device *unit_dev)
+{
+ struct fw_unit *unit = fw_unit(unit_dev);
+ struct fw_device *fw_dev = fw_parent_device(unit);
+ struct snd_card *card;
+ struct isight *isight;
+ int err;
+
+ err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card);
+ if (err < 0)
+ return err;
+ snd_card_set_dev(card, unit_dev);
+
+ isight = card->private_data;
+ isight->card = card;
+ mutex_init(&isight->mutex);
+ isight->unit = fw_unit_get(unit);
+ isight->device = fw_device_get(fw_dev);
+ isight->audio_base = get_unit_base(unit);
+ if (!isight->audio_base) {
+ dev_err(&unit->device, "audio unit base not found\n");
+ err = -ENXIO;
+ goto err_unit;
+ }
+ fw_iso_resources_init(&isight->resources, unit);
+
+ card->private_free = isight_card_free;
+
+ strcpy(card->driver, "iSight");
+ strcpy(card->shortname, "Apple iSight");
+ snprintf(card->longname, sizeof(card->longname),
+ "Apple iSight (GUID %08x%08x) at %s, S%d",
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&unit->device), 100 << fw_dev->max_speed);
+ strcpy(card->mixername, "iSight");
+
+ err = isight_create_pcm(isight);
+ if (err < 0)
+ goto error;
+
+ err = isight_create_mixer(isight);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(unit_dev, isight);
+
+ return 0;
+
+err_unit:
+ fw_unit_put(isight->unit);
+ fw_device_put(isight->device);
+ mutex_destroy(&isight->mutex);
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static int isight_remove(struct device *dev)
+{
+ struct isight *isight = dev_get_drvdata(dev);
+
+ isight_pcm_abort(isight);
+
+ snd_card_disconnect(isight->card);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ snd_card_free_when_closed(isight->card);
+
+ return 0;
+}
+
+static void isight_bus_reset(struct fw_unit *unit)
+{
+ struct isight *isight = dev_get_drvdata(&unit->device);
+
+ if (fw_iso_resources_update(&isight->resources) < 0) {
+ isight_pcm_abort(isight);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+ }
+}
+
+static const struct ieee1394_device_id isight_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .specifier_id = OUI_APPLE,
+ .version = SW_ISIGHT_AUDIO,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, isight_id_table);
+
+static struct fw_driver isight_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ .probe = isight_probe,
+ .remove = isight_remove,
+ },
+ .update = isight_bus_reset,
+ .id_table = isight_id_table,
+};
+
+static int __init alsa_isight_init(void)
+{
+ return driver_register(&isight_driver.driver);
+}
+
+static void __exit alsa_isight_exit(void)
+{
+ driver_unregister(&isight_driver.driver);
+}
+
+module_init(alsa_isight_init);
+module_exit(alsa_isight_exit);
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c
index 775dbd5f3445..9d4a6714f9ec 100644
--- a/sound/firewire/iso-resources.c
+++ b/sound/firewire/iso-resources.c
@@ -36,6 +36,7 @@ int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
return 0;
}
+EXPORT_SYMBOL(fw_iso_resources_init);
/**
* fw_iso_resources_destroy - destroy a resource manager
@@ -48,6 +49,7 @@ void fw_iso_resources_destroy(struct fw_iso_resources *r)
mutex_destroy(&r->mutex);
fw_unit_put(r->unit);
}
+EXPORT_SYMBOL(fw_iso_resources_destroy);
static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
{
@@ -152,6 +154,7 @@ retry_after_bus_reset:
return channel;
}
+EXPORT_SYMBOL(fw_iso_resources_allocate);
/**
* fw_iso_resources_update - update resource allocations after a bus reset
@@ -203,6 +206,7 @@ int fw_iso_resources_update(struct fw_iso_resources *r)
return channel;
}
+EXPORT_SYMBOL(fw_iso_resources_update);
/**
* fw_iso_resources_free - frees allocated resources
@@ -230,3 +234,4 @@ void fw_iso_resources_free(struct fw_iso_resources *r)
mutex_unlock(&r->mutex);
}
+EXPORT_SYMBOL(fw_iso_resources_free);
diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c
index 1e20e60ba6a6..3c61ca2e6152 100644
--- a/sound/firewire/packets-buffer.c
+++ b/sound/firewire/packets-buffer.c
@@ -60,6 +60,7 @@ err_packets:
error:
return err;
}
+EXPORT_SYMBOL(iso_packets_buffer_init);
/**
* iso_packets_buffer_destroy - frees packet buffer resources
@@ -72,3 +73,4 @@ void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
kfree(b->packets);
}
+EXPORT_SYMBOL(iso_packets_buffer_destroy);
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 2dad40f3f622..c95d8f1aae87 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -14,4 +14,4 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
-obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
+obj-$(CONFIG_SND_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index ee538f1ae846..4831800239d3 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -37,8 +37,8 @@ static int radio_nr = -1;
module_param(radio_nr, int, 0);
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-#define FREQ_LO (87 * 16000)
-#define FREQ_HI (108 * 16000)
+#define FREQ_LO (50UL * 16000)
+#define FREQ_HI (150UL * 16000)
/*
* definitions
@@ -77,27 +77,95 @@ static struct v4l2_queryctrl radio_qctrl[] = {
* lowlevel part
*/
+static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
+{
+ u16 l;
+ u8 data;
+
+ tea->ops->set_direction(tea, 1);
+ udelay(16);
+
+ for (l = 25; l > 0; l--) {
+ data = (val >> 24) & TEA575X_DATA;
+ val <<= 1; /* shift data */
+ tea->ops->set_pins(tea, data | TEA575X_WREN);
+ udelay(2);
+ tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
+ udelay(2);
+ tea->ops->set_pins(tea, data | TEA575X_WREN);
+ udelay(2);
+ }
+
+ if (!tea->mute)
+ tea->ops->set_pins(tea, 0);
+}
+
+static unsigned int snd_tea575x_read(struct snd_tea575x *tea)
+{
+ u16 l, rdata;
+ u32 data = 0;
+
+ tea->ops->set_direction(tea, 0);
+ tea->ops->set_pins(tea, 0);
+ udelay(16);
+
+ for (l = 24; l--;) {
+ tea->ops->set_pins(tea, TEA575X_CLK);
+ udelay(2);
+ if (!l)
+ tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
+ tea->ops->set_pins(tea, 0);
+ udelay(2);
+ data <<= 1; /* shift data */
+ rdata = tea->ops->get_pins(tea);
+ if (!l)
+ tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1;
+ if (rdata & TEA575X_DATA)
+ data++;
+ udelay(2);
+ }
+
+ if (tea->mute)
+ tea->ops->set_pins(tea, TEA575X_WREN);
+
+ return data;
+}
+
+static void snd_tea575x_get_freq(struct snd_tea575x *tea)
+{
+ unsigned long freq;
+
+ freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
+ /* freq *= 12.5 */
+ freq *= 125;
+ freq /= 10;
+ /* crystal fixup */
+ if (tea->tea5759)
+ freq += TEA575X_FMIF;
+ else
+ freq -= TEA575X_FMIF;
+
+ tea->freq = freq * 16; /* from kHz */
+}
+
static void snd_tea575x_set_freq(struct snd_tea575x *tea)
{
unsigned long freq;
- freq = tea->freq / 16; /* to kHz */
- if (freq > 108000)
- freq = 108000;
- if (freq < 87000)
- freq = 87000;
+ freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
+ freq /= 16; /* to kHz */
/* crystal fixup */
if (tea->tea5759)
- freq -= tea->freq_fixup;
+ freq -= TEA575X_FMIF;
else
- freq += tea->freq_fixup;
+ freq += TEA575X_FMIF;
/* freq /= 12.5 */
freq *= 10;
freq /= 125;
tea->val &= ~TEA575X_BIT_FREQ_MASK;
tea->val |= freq & TEA575X_BIT_FREQ_MASK;
- tea->ops->write(tea, tea->val);
+ snd_tea575x_write(tea, tea->val);
}
/*
@@ -109,29 +177,34 @@ static int vidioc_querycap(struct file *file, void *priv,
{
struct snd_tea575x *tea = video_drvdata(file);
- strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
- strlcpy(v->card, "Maestro Radio", sizeof(v->card));
- sprintf(v->bus_info, "PCI");
+ strlcpy(v->card, tea->card, sizeof(v->card));
+ strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+ strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
v->version = RADIO_VERSION;
- v->capabilities = V4L2_CAP_TUNER;
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct snd_tea575x *tea = video_drvdata(file);
+
if (v->index > 0)
return -EINVAL;
+ snd_tea575x_read(tea);
+
strcpy(v->name, "FM");
v->type = V4L2_TUNER_RADIO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
v->rangelow = FREQ_LO;
v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
+ v->signal = tea->tuned ? 0xffff : 0;
+
return 0;
}
@@ -148,7 +221,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct snd_tea575x *tea = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
+ snd_tea575x_get_freq(tea);
f->frequency = tea->freq;
return 0;
}
@@ -158,6 +234,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct snd_tea575x *tea = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
return -EINVAL;
@@ -209,10 +288,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- ctrl->value = tea->mute;
- return 0;
- }
+ ctrl->value = tea->mute;
+ return 0;
}
return -EINVAL;
}
@@ -224,11 +301,11 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- tea->ops->mute(tea, ctrl->value);
+ if (tea->mute != ctrl->value) {
tea->mute = ctrl->value;
- return 0;
+ snd_tea575x_set_freq(tea);
}
+ return 0;
}
return -EINVAL;
}
@@ -293,18 +370,16 @@ static struct video_device tea575x_radio = {
/*
* initialize all the tea575x chips
*/
-void snd_tea575x_init(struct snd_tea575x *tea)
+int snd_tea575x_init(struct snd_tea575x *tea)
{
int retval;
- unsigned int val;
struct video_device *tea575x_radio_inst;
- val = tea->ops->read(tea);
- if (val == 0x1ffffff || val == 0) {
- snd_printk(KERN_ERR
- "tea575x-tuner: Cannot find TEA575x chip\n");
- return;
- }
+ tea->mute = 1;
+
+ snd_tea575x_write(tea, 0x55AA);
+ if (snd_tea575x_read(tea) != 0x55AA)
+ return -ENODEV;
tea->in_use = 0;
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
@@ -313,7 +388,7 @@ void snd_tea575x_init(struct snd_tea575x *tea)
tea575x_radio_inst = video_device_alloc();
if (tea575x_radio_inst == NULL) {
printk(KERN_ERR "tea575x-tuner: not enough memory\n");
- return;
+ return -ENOMEM;
}
memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
@@ -328,17 +403,13 @@ void snd_tea575x_init(struct snd_tea575x *tea)
if (retval) {
printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
kfree(tea575x_radio_inst);
- return;
+ return retval;
}
snd_tea575x_set_freq(tea);
-
- /* mute on init */
- if (tea->ops->mute) {
- tea->ops->mute(tea, 1);
- tea->mute = 1;
- }
tea->vd = tea575x_radio_inst;
+
+ return 0;
}
void snd_tea575x_exit(struct snd_tea575x *tea)
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index 76c090218073..6c93e051f9ae 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -22,10 +22,6 @@ config SOUND_VWSND
<file:Documentation/sound/oss/vwsnd> for more info on this driver's
capabilities.
-config SOUND_AU1550_AC97
- tristate "Au1550/Au1200 AC97 Sound"
- depends on SOC_AU1550 || SOC_AU1200
-
config SOUND_MSNDCLAS
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
depends on (m || !STANDALONE) && ISA
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index 90ffb99c6b17..77f21b68bf0f 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -25,7 +25,6 @@ obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
-obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
obj-$(CONFIG_DMASOUND) += dmasound/
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
deleted file mode 100644
index 0cd23d94888f..000000000000
--- a/sound/oss/ac97_codec.c
+++ /dev/null
@@ -1,1203 +0,0 @@
-/*
- * ac97_codec.c: Generic AC97 mixer/modem module
- *
- * Derived from ac97 mixer in maestro and trident driver.
- *
- * Copyright 2000 Silicon Integrated System Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- **************************************************************************
- *
- * The Intel Audio Codec '97 specification is available at:
- * http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
- *
- **************************************************************************
- *
- * History
- * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
- * Removed non existent WM9700
- * Added support for WM9705, WM9708, WM9709, WM9710, WM9711
- * WM9712 and WM9717
- * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
- * corrections to support WM9707 in ViewPad 1000
- * v0.4 Mar 15 2000 Ollie Lho
- * dual codecs support verified with 4 channels output
- * v0.3 Feb 22 2000 Ollie Lho
- * bug fix for record mask setting
- * v0.2 Feb 10 2000 Ollie Lho
- * add ac97_read_proc for /proc/driver/{vendor}/ac97
- * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>
- * Isolated from trident.c to support multiple ac97 codec
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ac97_codec.h>
-#include <asm/uaccess.h>
-#include <linux/mutex.h>
-
-#define CODEC_ID_BUFSZ 14
-
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right);
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
-
-static int ac97_init_mixer(struct ac97_codec *codec);
-
-static int wolfson_init03(struct ac97_codec * codec);
-static int wolfson_init04(struct ac97_codec * codec);
-static int wolfson_init05(struct ac97_codec * codec);
-static int wolfson_init11(struct ac97_codec * codec);
-static int wolfson_init13(struct ac97_codec * codec);
-static int tritech_init(struct ac97_codec * codec);
-static int tritech_maestro_init(struct ac97_codec * codec);
-static int sigmatel_9708_init(struct ac97_codec *codec);
-static int sigmatel_9721_init(struct ac97_codec *codec);
-static int sigmatel_9744_init(struct ac97_codec *codec);
-static int ad1886_init(struct ac97_codec *codec);
-static int eapd_control(struct ac97_codec *codec, int);
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int cmedia_init(struct ac97_codec * codec);
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-
-
-/*
- * AC97 operations.
- *
- * If you are adding a codec then you should be able to use
- * eapd_ops - any codec that supports EAPD amp control (most)
- * null_ops - any ancient codec that supports nothing
- *
- * The three functions are
- * init - used for non AC97 standard initialisation
- * amplifier - used to do amplifier control (1=on 0=off)
- * digital - switch to digital modes (0 = analog)
- *
- * Not all codecs support all features, not all drivers use all the
- * operations yet
- */
-
-static struct ac97_ops null_ops = { NULL, NULL, NULL };
-static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
-static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control};
-static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
-static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
-static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
-static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
-static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
-static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
-static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
-static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
-static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };
-static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
-static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
-static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
-static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
-static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
-
-/* sorted by vendor/device id */
-static const struct {
- u32 id;
- char *name;
- struct ac97_ops *ops;
- int flags;
-} ac97_codec_ids[] = {
- {0x41445303, "Analog Devices AD1819", &null_ops},
- {0x41445340, "Analog Devices AD1881", &null_ops},
- {0x41445348, "Analog Devices AD1881A", &null_ops},
- {0x41445360, "Analog Devices AD1885", &default_ops},
- {0x41445361, "Analog Devices AD1886", &ad1886_ops},
- {0x41445370, "Analog Devices AD1981", &null_ops},
- {0x41445372, "Analog Devices AD1981A", &null_ops},
- {0x41445374, "Analog Devices AD1981B", &null_ops},
- {0x41445460, "Analog Devices AD1885", &default_ops},
- {0x41445461, "Analog Devices AD1886", &ad1886_ops},
- {0x414B4D00, "Asahi Kasei AK4540", &null_ops},
- {0x414B4D01, "Asahi Kasei AK4542", &null_ops},
- {0x414B4D02, "Asahi Kasei AK4543", &null_ops},
- {0x414C4326, "ALC100P", &null_ops},
- {0x414C4710, "ALC200/200P", &null_ops},
- {0x414C4720, "ALC650", &default_digital_ops},
- {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME },
- {0x43525900, "Cirrus Logic CS4297", &default_ops},
- {0x43525903, "Cirrus Logic CS4297", &default_ops},
- {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
- {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},
- {0x43525923, "Cirrus Logic CS4298", &null_ops},
- {0x4352592B, "Cirrus Logic CS4294", &null_ops},
- {0x4352592D, "Cirrus Logic CS4294", &null_ops},
- {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
- {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
- {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
- {0x43585430, "CXT48", &default_ops, AC97_DELUDED_MODEM },
- {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM },
- {0x44543031, "Diamond Technology DT0893", &default_ops},
- {0x45838308, "ESS Allegro ES1988", &null_ops},
- {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */
- {0x4e534331, "National Semiconductor LM4549", &null_ops},
- {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
- {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
- {0x545200FF, "TriTech TR?????", &tritech_m_ops},
- {0x54524102, "TriTech TR28022", &null_ops},
- {0x54524103, "TriTech TR28023", &null_ops},
- {0x54524106, "TriTech TR28026", &null_ops},
- {0x54524108, "TriTech TR28028", &tritech_ops},
- {0x54524123, "TriTech TR A5", &null_ops},
- {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03},
- {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04},
- {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05},
- {0x574D4C09, "Wolfson WM9709", &null_ops},
- {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11},
- {0x574D4C13, "Wolfson WM9713", &wolfson_ops13, AC97_DEFAULT_POWER_OFF},
- {0x83847600, "SigmaTel STAC????", &null_ops},
- {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
- {0x83847605, "SigmaTel STAC9704", &null_ops},
- {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops},
- {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops},
- {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops},
- {0x83847652, "SigmaTel STAC9752/53", &default_ops},
- {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops},
- {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops},
- {0x83847684, "SigmaTel STAC9783/84?", &null_ops},
- {0x57454301, "Winbond 83971D", &null_ops},
-};
-
-/* this table has default mixer values for all OSS mixers. */
-static struct mixer_defaults {
- int mixer;
- unsigned int value;
-} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
- /* all values 0 -> 100 in bytes */
- {SOUND_MIXER_VOLUME, 0x4343},
- {SOUND_MIXER_BASS, 0x4343},
- {SOUND_MIXER_TREBLE, 0x4343},
- {SOUND_MIXER_PCM, 0x4343},
- {SOUND_MIXER_SPEAKER, 0x4343},
- {SOUND_MIXER_LINE, 0x4343},
- {SOUND_MIXER_MIC, 0x0000},
- {SOUND_MIXER_CD, 0x4343},
- {SOUND_MIXER_ALTPCM, 0x4343},
- {SOUND_MIXER_IGAIN, 0x4343},
- {SOUND_MIXER_LINE1, 0x4343},
- {SOUND_MIXER_PHONEIN, 0x4343},
- {SOUND_MIXER_PHONEOUT, 0x4343},
- {SOUND_MIXER_VIDEO, 0x4343},
- {-1,0}
-};
-
-/* table to scale scale from OSS mixer value to AC97 mixer register value */
-static struct ac97_mixer_hw {
- unsigned char offset;
- int scale;
-} ac97_hw[SOUND_MIXER_NRDEVICES]= {
- [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64},
- [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32},
- [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16},
- [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32},
- [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32},
- [SOUND_MIXER_CD] = {AC97_CD_VOL, 32},
- [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64},
- [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16},
- [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32},
- [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32},
- [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64},
- [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32},
-};
-
-/* the following tables allow us to go from OSS <-> ac97 quickly. */
-enum ac97_recsettings {
- AC97_REC_MIC=0,
- AC97_REC_CD,
- AC97_REC_VIDEO,
- AC97_REC_AUX,
- AC97_REC_LINE,
- AC97_REC_STEREO, /* combination of all enabled outputs.. */
- AC97_REC_MONO, /*.. or the mono equivalent */
- AC97_REC_PHONE
-};
-
-static const unsigned int ac97_rm2oss[] = {
- [AC97_REC_MIC] = SOUND_MIXER_MIC,
- [AC97_REC_CD] = SOUND_MIXER_CD,
- [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
- [AC97_REC_AUX] = SOUND_MIXER_LINE1,
- [AC97_REC_LINE] = SOUND_MIXER_LINE,
- [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
- [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
-};
-
-/* indexed by bit position */
-static const unsigned int ac97_oss_rm[] = {
- [SOUND_MIXER_MIC] = AC97_REC_MIC,
- [SOUND_MIXER_CD] = AC97_REC_CD,
- [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
- [SOUND_MIXER_LINE1] = AC97_REC_AUX,
- [SOUND_MIXER_LINE] = AC97_REC_LINE,
- [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
- [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
-};
-
-static LIST_HEAD(codecs);
-static LIST_HEAD(codec_drivers);
-static DEFINE_MUTEX(codec_mutex);
-
-/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
- about that given mixer, and should be holding a spinlock for the card */
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel)
-{
- u16 val;
- int ret = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
- val = codec->codec_read(codec , mh->offset);
-
- if (val & AC97_MUTE) {
- ret = 0;
- } else if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* nice stereo mixers .. */
- int left,right;
-
- left = (val >> 8) & 0x7f;
- right = val & 0x7f;
-
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * 100) / mh->scale;
- left = (left * 100) / mh->scale;
- } else {
- /* these may have 5 or 6 bit resolution */
- if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = 100 - ((right * 100) / scale);
- left = 100 - ((left * 100) / scale);
- }
- ret = left | (right << 8);
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- ret = 100 - (((val & 0x1f) * 100) / scale);
- } else if (oss_channel == SOUND_MIXER_MIC) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- } else if (oss_channel == SOUND_MIXER_BASS) {
- ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- ret = 100 - (((val & 0xe) * 100) / mh->scale);
- }
-
-#ifdef DEBUG
- printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
- "0x%04x -> 0x%04x\n",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, val, ret);
-#endif
-
- return ret;
-}
-
-/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to
- make sure all is well in arg land, call with spinlock held */
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right)
-{
- u16 val = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
-#ifdef DEBUG
- printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "
- "left vol:%2d, right vol:%2d:",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, left, right);
-#endif
-
- if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* stereo mixers */
- if (left == 0 && right == 0) {
- val = AC97_MUTE;
- } else {
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * mh->scale) / 100;
- left = (left * mh->scale) / 100;
- if (right >= mh->scale)
- right = mh->scale-1;
- if (left >= mh->scale)
- left = mh->scale-1;
- } else {
- /* these may have 5 or 6 bit resolution */
- if (oss_channel == SOUND_MIXER_VOLUME ||
- oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = ((100 - right) * scale) / 100;
- left = ((100 - left) * scale) / 100;
- if (right >= scale)
- right = scale-1;
- if (left >= scale)
- left = scale-1;
- }
- val = (left << 8) | right;
- }
- } else if (oss_channel == SOUND_MIXER_BASS) {
- val = codec->codec_read(codec , mh->offset) & ~0x0f00;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= (left << 8) & 0x0e00;
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- val = codec->codec_read(codec , mh->offset) & ~0x000f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left & 0x000e;
- } else if(left == 0) {
- val = AC97_MUTE;
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left << 1;
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- left = ((100 - left) * scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_MIC) {
- val = codec->codec_read(codec , mh->offset) & ~0x801f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left;
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- }
-#ifdef DEBUG
- printk(" 0x%04x", val);
-#endif
-
- codec->codec_write(codec, mh->offset, val);
-
-#ifdef DEBUG
- val = codec->codec_read(codec, mh->offset);
- printk(" -> 0x%04x\n", val);
-#endif
-}
-
-/* a thin wrapper for write_mixer */
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val )
-{
- unsigned int left,right;
-
- /* cleanse input a little */
- right = ((val >> 8) & 0xff) ;
- left = (val & 0xff) ;
-
- if (right > 100) right = 100;
- if (left > 100) left = 100;
-
- codec->mixer_state[oss_mixer] = (right << 8) | left;
- codec->write_mixer(codec, oss_mixer, left, right);
-}
-
-/* read or write the recmask, the ac97 can really have left and right recording
- inputs independently set, but OSS doesn't seem to want us to express that to
- the user. the caller guarantees that we have a supported bit set, and they
- must be holding the card's spinlock */
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask)
-{
- unsigned int val;
-
- if (rw) {
- /* read it from the card */
- val = codec->codec_read(codec, AC97_RECORD_SELECT);
-#ifdef DEBUG
- printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
-#endif
- return (1 << ac97_rm2oss[val & 0x07]);
- }
-
- /* else, write the first set in the mask as the
- output */
- /* clear out current set value first (AC97 supports only 1 input!) */
- val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);
- if (mask != val)
- mask &= ~val;
-
- val = ffs(mask);
- val = ac97_oss_rm[val-1];
- val |= val << 8; /* set both channels */
-
-#ifdef DEBUG
- printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
-#endif
-
- codec->codec_write(codec, AC97_RECORD_SELECT, val);
-
- return 0;
-};
-
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
-{
- int i, val = 0;
-
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- info.modify_counter = codec->modcnt;
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
-
- if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int __user *)arg);
-
- if (_SIOC_DIR(cmd) == _SIOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* give them the current record source */
- if (!codec->recmask_io) {
- val = 0;
- } else {
- val = codec->recmask_io(codec, 1, 0);
- }
- break;
-
- case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
- val = codec->supported_mixers;
- break;
-
- case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
- val = codec->record_sources;
- break;
-
- case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
- val = codec->stereo_mixers;
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default: /* read a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- /* do we ever want to touch the hardware? */
- /* val = codec->read_mixer(codec, i); */
- val = codec->mixer_state[i];
- break;
- }
- return put_user(val, (int __user *)arg);
- }
-
- if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
- codec->modcnt++;
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- if (!codec->recmask_io) return -EINVAL;
- if (!val) return 0;
- if (!(val &= codec->record_sources)) return -EINVAL;
-
- codec->recmask_io(codec, 0, val);
-
- return 0;
- default: /* write a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- ac97_set_mixer(codec, i, val);
-
- return 0;
- }
- }
- return -EINVAL;
-}
-
-/**
- * codec_id - Turn id1/id2 into a PnP string
- * @id1: Vendor ID1
- * @id2: Vendor ID2
- * @buf: CODEC_ID_BUFSZ byte buffer
- *
- * Fills buf with a zero terminated PnP ident string for the id1/id2
- * pair. For convenience the return is the passed in buffer pointer.
- */
-
-static char *codec_id(u16 id1, u16 id2, char *buf)
-{
- if(id1&0x8080) {
- snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);
- } else {
- buf[0] = (id1 >> 8);
- buf[1] = (id1 & 0xFF);
- buf[2] = (id2 >> 8);
- snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);
- }
- return buf;
-}
-
-/**
- * ac97_check_modem - Check if the Codec is a modem
- * @codec: codec to check
- *
- * Return true if the device is an AC97 1.0 or AC97 2.0 modem
- */
-
-static int ac97_check_modem(struct ac97_codec *codec)
-{
- /* Check for an AC97 1.0 soft modem (ID1) */
- if(codec->codec_read(codec, AC97_RESET) & 2)
- return 1;
- /* Check for an AC97 2.x soft modem */
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
- if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
- return 1;
- return 0;
-}
-
-
-/**
- * ac97_alloc_codec - Allocate an AC97 codec
- *
- * Returns a new AC97 codec structure. AC97 codecs may become
- * refcounted soon so this interface is needed. Returns with
- * one reference taken.
- */
-
-struct ac97_codec *ac97_alloc_codec(void)
-{
- struct ac97_codec *codec = kzalloc(sizeof(struct ac97_codec), GFP_KERNEL);
- if(!codec)
- return NULL;
-
- spin_lock_init(&codec->lock);
- INIT_LIST_HEAD(&codec->list);
- return codec;
-}
-
-EXPORT_SYMBOL(ac97_alloc_codec);
-
-/**
- * ac97_release_codec - Release an AC97 codec
- * @codec: codec to release
- *
- * Release an allocated AC97 codec. This will be refcounted in
- * time but for the moment is trivial. Calls the unregister
- * handler if the codec is now defunct.
- */
-
-void ac97_release_codec(struct ac97_codec *codec)
-{
- /* Remove from the list first, we don't want to be
- "rediscovered" */
- mutex_lock(&codec_mutex);
- list_del(&codec->list);
- mutex_unlock(&codec_mutex);
- /*
- * The driver needs to deal with internal
- * locking to avoid accidents here.
- */
- if(codec->driver)
- codec->driver->remove(codec, codec->driver);
- kfree(codec);
-}
-
-EXPORT_SYMBOL(ac97_release_codec);
-
-/**
- * ac97_probe_codec - Initialize and setup AC97-compatible codec
- * @codec: (in/out) Kernel info for a single AC97 codec
- *
- * Reset the AC97 codec, then initialize the mixer and
- * the rest of the @codec structure.
- *
- * The codec_read and codec_write fields of @codec are
- * required to be setup and working when this function
- * is called. All other fields are set by this function.
- *
- * codec_wait field of @codec can optionally be provided
- * when calling this function. If codec_wait is not %NULL,
- * this function will call codec_wait any time it is
- * necessary to wait for the audio chip to reach the
- * codec-ready state. If codec_wait is %NULL, then
- * the default behavior is to call schedule_timeout.
- * Currently codec_wait is used to wait for AC97 codec
- * reset to complete.
- *
- * Some codecs will power down when a register reset is
- * performed. We now check for such codecs.
- *
- * Returns 1 (true) on success, or 0 (false) on failure.
- */
-
-int ac97_probe_codec(struct ac97_codec *codec)
-{
- u16 id1, id2;
- u16 audio;
- int i;
- char cidbuf[CODEC_ID_BUFSZ];
- u16 f;
- struct list_head *l;
- struct ac97_driver *d;
-
- /* wait for codec-ready state */
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
-
- /* will the codec power down if register reset ? */
- id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
- id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
- codec->name = NULL;
- codec->codec_ops = &null_ops;
- for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
- if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
- codec->type = ac97_codec_ids[i].id;
- codec->name = ac97_codec_ids[i].name;
- codec->codec_ops = ac97_codec_ids[i].ops;
- codec->flags = ac97_codec_ids[i].flags;
- break;
- }
- }
-
- codec->model = (id1 << 16) | id2;
- if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {
- /* reset codec and wait for the ready bit before we continue */
- codec->codec_write(codec, AC97_RESET, 0L);
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
- }
-
- /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
- * be read zero.
- *
- * FIXME: is the following comment outdated? -jgarzik
- * Probing of AC97 in this way is not reliable, it is not even SAFE !!
- */
- if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
- printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
- (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary")
- : (codec->id&1 ? "Secondary": "Primary"));
- return 0;
- }
-
- /* probe for Modem Codec */
- codec->modem = ac97_check_modem(codec);
-
- /* enable SPDIF */
- f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if((codec->codec_ops == &null_ops) && (f & 4))
- codec->codec_ops = &default_digital_ops;
-
- /* A device which thinks its a modem but isn't */
- if(codec->flags & AC97_DELUDED_MODEM)
- codec->modem = 0;
-
- if (codec->name == NULL)
- codec->name = "Unknown";
- printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n",
- codec->modem ? "Modem" : (audio ? "Audio" : ""),
- codec_id(id1, id2, cidbuf), codec->name);
-
- if(!ac97_init_mixer(codec))
- return 0;
-
- /*
- * Attach last so the caller can override the mixer
- * callbacks.
- */
-
- mutex_lock(&codec_mutex);
- list_add(&codec->list, &codecs);
-
- list_for_each(l, &codec_drivers) {
- d = list_entry(l, struct ac97_driver, list);
- if ((codec->model ^ d->codec_id) & d->codec_mask)
- continue;
- if(d->probe(codec, d) == 0)
- {
- codec->driver = d;
- break;
- }
- }
-
- mutex_unlock(&codec_mutex);
- return 1;
-}
-
-static int ac97_init_mixer(struct ac97_codec *codec)
-{
- u16 cap;
- int i;
-
- cap = codec->codec_read(codec, AC97_RESET);
-
- /* mixer masks */
- codec->supported_mixers = AC97_SUPPORTED_MASK;
- codec->stereo_mixers = AC97_STEREO_MASK;
- codec->record_sources = AC97_RECORD_MASK;
- if (!(cap & 0x04))
- codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
- if (!(cap & 0x10))
- codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
-
-
- /* detect bit resolution */
- codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
- if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)
- codec->bit_resolution = 6;
- else
- codec->bit_resolution = 5;
-
- /* generic OSS to AC97 wrapper */
- codec->read_mixer = ac97_read_mixer;
- codec->write_mixer = ac97_write_mixer;
- codec->recmask_io = ac97_recmask_io;
- codec->mixer_ioctl = ac97_mixer_ioctl;
-
- /* initialize mixer channel volumes */
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- struct mixer_defaults *md = &mixer_defaults[i];
- if (md->mixer == -1)
- break;
- if (!supported_mixer(codec, md->mixer))
- continue;
- ac97_set_mixer(codec, md->mixer, md->value);
- }
-
- /* codec specific initialization for 4-6 channel output or secondary codec stuff */
- if (codec->codec_ops->init != NULL) {
- codec->codec_ops->init(codec);
- }
-
- /*
- * Volume is MUTE only on this device. We have to initialise
- * it but its useless beyond that.
- */
- if(codec->flags & AC97_NO_PCM_VOLUME)
- {
- codec->supported_mixers &= ~SOUND_MASK_PCM;
- printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");
- }
- return 1;
-}
-
-#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */
-#define AC97_SIGMATEL_DAC2INVERT 0x6e
-#define AC97_SIGMATEL_BIAS1 0x70
-#define AC97_SIGMATEL_BIAS2 0x72
-#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */
-#define AC97_SIGMATEL_CIC1 0x76
-#define AC97_SIGMATEL_CIC2 0x78
-
-
-static int sigmatel_9708_init(struct ac97_codec * codec)
-{
- u16 codec72, codec6c;
-
- codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;
- codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);
-
- if ((codec72==0) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);
- } else if ((codec72==0x8000) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);
- codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);
- } else if ((codec72==0x8000) && (codec6c==0x0080)) {
- /* nothing */
- }
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-
-static int sigmatel_9721_init(struct ac97_codec * codec)
-{
- /* Only set up secondary codec */
- if (codec->id == 0)
- return 0;
-
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
-
- /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
- sloc 3,4 = 0x01, slot 7,8 = 0x00, */
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00);
-
- /* we don't have the crystal when we are on an AMR card, so use
- BIT_CLK as our clock source. Write the magic word ABBA and read
- back to enable register 0x78 */
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_read(codec, AC97_SIGMATEL_CIC1);
-
- /* sync all the clocks*/
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802);
-
- return 0;
-}
-
-
-static int sigmatel_9744_init(struct ac97_codec * codec)
-{
- // patch for SigmaTel
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002);
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-static int cmedia_init(struct ac97_codec *codec)
-{
- /* Initialise the CMedia 9739 */
- /*
- We could set various options here
- Register 0x20 bit 0x100 sets mic as center bass
- Also do multi_channel_ctrl &=~0x3000 |=0x1000
-
- For now we set up the GPIO and PC beep
- */
-
- u16 v;
-
- /* MIC */
- codec->codec_write(codec, 0x64, 0x3000);
- v = codec->codec_read(codec, 0x64);
- v &= ~0x8000;
- codec->codec_write(codec, 0x64, v);
- codec->codec_write(codec, 0x70, 0x0100);
- codec->codec_write(codec, 0x72, 0x0020);
- return 0;
-}
-
-#define AC97_WM97XX_FMIXER_VOL 0x72
-#define AC97_WM97XX_RMIXER_VOL 0x74
-#define AC97_WM97XX_TEST 0x5a
-#define AC97_WM9704_RPCM_VOL 0x70
-#define AC97_WM9711_OUT3VOL 0x16
-
-static int wolfson_init03(struct ac97_codec * codec)
-{
- /* this is known to work for the ViewSonic ViewPad 1000 */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000);
- return 0;
-}
-
-static int wolfson_init04(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808);
-
- // patch for DVD noise
- codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200);
-
- // init vol as PCM vol
- codec->codec_write(codec, AC97_WM9704_RPCM_VOL,
- codec->codec_read(codec, AC97_PCMOUT_VOL));
-
- /* set rear surround volume */
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- return 0;
-}
-
-/* WM9705, WM9710 */
-static int wolfson_init05(struct ac97_codec * codec)
-{
- /* set front mixer volume */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- return 0;
-}
-
-/* WM9711, WM9712 */
-static int wolfson_init11(struct ac97_codec * codec)
-{
- /* stop pop's during suspend/resume */
- codec->codec_write(codec, AC97_WM97XX_TEST,
- codec->codec_read(codec, AC97_WM97XX_TEST) & 0xffbf);
-
- /* set out3 volume */
- codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808);
- return 0;
-}
-
-/* WM9713 */
-static int wolfson_init13(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);
- codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
- codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
- codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
- codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
-
- return 0;
-}
-
-static int tritech_init(struct ac97_codec * codec)
-{
- codec->codec_write(codec, 0x26, 0x0300);
- codec->codec_write(codec, 0x26, 0x0000);
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- codec->codec_write(codec, AC97_RESERVED_3A, 0x0000);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-static int tritech_maestro_init(struct ac97_codec * codec)
-{
- /* no idea what this does */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0XFFFF);
- return 0;
-}
-
-
-
-/*
- * Presario700 workaround
- * for Jack Sense/SPDIF Register mis-setting causing
- * no audible output
- * by Santiago Nullo 04/05/2002
- */
-
-#define AC97_AD1886_JACK_SENSE 0x72
-
-static int ad1886_init(struct ac97_codec * codec)
-{
- /* from AD1886 Specs */
- codec->codec_write(codec, AC97_AD1886_JACK_SENSE, 0x0010);
- return 0;
-}
-
-
-
-
-/*
- * This is basically standard AC97. It should work as a default for
- * almost all modern codecs. Note that some cards wire EAPD *backwards*
- * That side of it is up to the card driver not us to cope with.
- *
- */
-
-static int eapd_control(struct ac97_codec * codec, int on)
-{
- if(on)
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
- else
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000);
- return 0;
-}
-
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 reg;
-
- reg = codec->codec_read(codec, AC97_SPDIF_CONTROL);
-
- switch(rate)
- {
- /* Off by default */
- default:
- case 0:
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF));
- if(rate == 0)
- return 0;
- return -EINVAL;
- case 1:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
- break;
- case 2:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
- break;
- case 3:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
- break;
- }
-
- reg &= ~AC97_SC_CC_MASK;
- reg |= (mode & AUDIO_CCMASK) << 6;
-
- if(mode & AUDIO_DIGITAL)
- reg |= 2;
- if(mode & AUDIO_PRO)
- reg |= 1;
- if(mode & AUDIO_DRS)
- reg |= 0x4000;
-
- codec->codec_write(codec, AC97_SPDIF_CONTROL, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- reg &= (AC97_EA_SLOT_MASK);
- reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots;
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if(!(reg & 0x0400))
- {
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF);
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * Crystal digital audio control (CS4299)
- */
-
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0; break; /* SPEN off */
- case 48000: cv = 0x8004; break; /* 48KHz digital */
- case 44100: cv = 0x8104; break; /* 44.1KHz digital */
- case 32768: /* 32Khz */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x68, cv);
- return 0;
-}
-
-/*
- * CMedia digital audio control
- * Needs more work.
- */
-
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0001; break; /* SPEN off */
- case 48000: cv = 0x0009; break; /* 48KHz digital */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x2A, 0x05c4);
- codec->codec_write(codec, 0x6C, cv);
-
- /* Switch on mix to surround */
- cv = codec->codec_read(codec, 0x64);
- cv &= ~0x0200;
- if(mode)
- cv |= 0x0200;
- codec->codec_write(codec, 0x64, cv);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-#if 0 /* there has been 1 person on the planet with a pt101 that we
- know of. If they care, they can put this back in :) */
-static int pt101_init(struct ac97_codec * codec)
-{
- printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
- /* who knows.. */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0xFFFF);
- codec->codec_write(codec, 0x10, 0x9F1F);
- codec->codec_write(codec, 0x12, 0x0808);
- codec->codec_write(codec, 0x14, 0x9F1F);
- codec->codec_write(codec, 0x16, 0x9F1F);
- codec->codec_write(codec, 0x18, 0x0404);
- codec->codec_write(codec, 0x1A, 0x0000);
- codec->codec_write(codec, 0x1C, 0x0000);
- codec->codec_write(codec, 0x02, 0x0404);
- codec->codec_write(codec, 0x04, 0x0808);
- codec->codec_write(codec, 0x0C, 0x801F);
- codec->codec_write(codec, 0x0E, 0x801F);
- return 0;
-}
-#endif
-
-
-EXPORT_SYMBOL(ac97_probe_codec);
-
-MODULE_LICENSE("GPL");
-
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
deleted file mode 100644
index a8f626d99c5b..000000000000
--- a/sound/oss/au1550_ac97.c
+++ /dev/null
@@ -1,2147 +0,0 @@
-/*
- * au1550_ac97.c -- Sound driver for Alchemy Au1550 MIPS Internet Edge
- * Processor.
- *
- * Copyright 2004 Embedded Edge, LLC
- * dan@embeddededge.com
- *
- * Mostly copied from the au1000.c driver and some from the
- * PowerMac dbdma driver.
- * We assume the processor can do memory coherent DMA.
- *
- * Ported to 2.6 by Matt Porter <mporter@kernel.crashing.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#undef DEBUG
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/sound.h>
-#include <linux/slab.h>
-#include <linux/soundcard.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/ac97_codec.h>
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/hardirq.h>
-#include <asm/mach-au1x00/au1xxx_psc.h>
-#include <asm/mach-au1x00/au1xxx_dbdma.h>
-#include <asm/mach-au1x00/au1xxx.h>
-
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-/* misc stuff */
-#define POLL_COUNT 0x50000
-#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
-
-/* The number of DBDMA ring descriptors to allocate. No sense making
- * this too large....if you can't keep up with a few you aren't likely
- * to be able to with lots of them, either.
- */
-#define NUM_DBDMA_DESCRIPTORS 4
-
-#define err(format, arg...) printk(KERN_ERR format "\n" , ## arg)
-
-/* Boot options
- * 0 = no VRA, 1 = use VRA if codec supports it
- */
-static DEFINE_MUTEX(au1550_ac97_mutex);
-static int vra = 1;
-module_param(vra, bool, 0);
-MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
-
-static struct au1550_state {
- /* soundcore stuff */
- int dev_audio;
-
- struct ac97_codec *codec;
- unsigned codec_base_caps; /* AC'97 reg 00h, "Reset Register" */
- unsigned codec_ext_caps; /* AC'97 reg 28h, "Extended Audio ID" */
- int no_vra; /* do not use VRA */
-
- spinlock_t lock;
- struct mutex open_mutex;
- struct mutex sem;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
-
- struct dmabuf {
- u32 dmanr;
- unsigned sample_rate;
- unsigned src_factor;
- unsigned sample_size;
- int num_channels;
- int dma_bytes_per_sample;
- int user_bytes_per_sample;
- int cnt_factor;
-
- void *rawbuf;
- unsigned buforder;
- unsigned numfrag;
- unsigned fragshift;
- void *nextIn;
- void *nextOut;
- int count;
- unsigned total_bytes;
- unsigned error;
- wait_queue_head_t wait;
-
- /* redundant, but makes calculations easier */
- unsigned fragsize;
- unsigned dma_fragsize;
- unsigned dmasize;
- unsigned dma_qcount;
-
- /* OSS stuff */
- unsigned mapped:1;
- unsigned ready:1;
- unsigned stopped:1;
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
- } dma_dac, dma_adc;
-} au1550_state;
-
-static unsigned
-ld2(unsigned int x)
-{
- unsigned r = 0;
-
- if (x >= 0x10000) {
- x >>= 16;
- r += 16;
- }
- if (x >= 0x100) {
- x >>= 8;
- r += 8;
- }
- if (x >= 0x10) {
- x >>= 4;
- r += 4;
- }
- if (x >= 4) {
- x >>= 2;
- r += 2;
- }
- if (x >= 2)
- r++;
- return r;
-}
-
-static void
-au1550_delay(int msec)
-{
- if (in_interrupt())
- return;
-
- schedule_timeout_uninterruptible(msecs_to_jiffies(msec));
-}
-
-static u16
-rdcodec(struct ac97_codec *codec, u8 addr)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- u16 data;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("rdcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= PSC_AC97CDC_RD; /* read command */
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- /* now wait for the data
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read poll expired!");
- data = 0;
- goto out;
- }
-
- /* wait for command done?
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read cmdwait expired!");
- data = 0;
- goto out;
- }
-
- data = au_readl(PSC_AC97CDC) & 0xffff;
- au_sync();
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- out:
- spin_unlock_irqrestore(&s->lock, flags);
-
- return data;
-}
-
-
-static void
-wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= (u32)data;
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: read cmdwait expired!");
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-waitcodec(struct ac97_codec *codec)
-{
- u16 temp;
- u32 val;
- int i;
-
- /* codec_wait is used to wait for a ready state after
- * an AC97C_RESET.
- */
- au1550_delay(10);
-
- /* first poll the CODEC_READY tag bit
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (val & PSC_AC97STAT_CR)
- break;
- }
- if (i == POLL_COUNT) {
- err("waitcodec: CODEC_READY poll expired!");
- return;
- }
-
- /* get AC'97 powerdown control/status register
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
-
- /* If anything is powered down, power'em up
- */
- if (temp & 0x7f00) {
- /* Power on
- */
- wrcodec(codec, AC97_POWER_CONTROL, 0);
- au1550_delay(100);
-
- /* Reread
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
- }
-
- /* Check if Codec REF,ANL,DAC,ADC ready
- */
- if ((temp & 0x7f0f) != 0x000f)
- err("codec reg 26 status (0x%x) not ready!!", temp);
-}
-
-/* stop the ADC before calling */
-static void
-set_adc_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *adc = &s->dma_adc;
- struct dmabuf *dac = &s->dma_dac;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- adc->src_factor = ((96000 / rate) + 1) >> 1;
- adc->sample_rate = 48000 / adc->src_factor;
- return;
- }
-
- adc->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
-
- /* read it back for actual supported rate
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- pr_debug("set_adc_rate: set to %d Hz\n", adc_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
-
- adc->sample_rate = adc_rate;
- dac->sample_rate = dac_rate;
-}
-
-/* stop the DAC before calling */
-static void
-set_dac_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *dac = &s->dma_dac;
- struct dmabuf *adc = &s->dma_adc;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- dac->src_factor = ((96000 / rate) + 1) >> 1;
- dac->sample_rate = 48000 / dac->src_factor;
- return;
- }
-
- dac->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
-
- /* I don't support different sample rates for multichannel,
- * so make these channels the same.
- */
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
- /* read it back for actual supported rate
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
-
- pr_debug("set_dac_rate: set to %d Hz\n", dac_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- dac->sample_rate = dac_rate;
- adc->sample_rate = adc_rate;
-}
-
-static void
-stop_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
- u32 stat;
- unsigned long flags;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_TP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Transmit Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_TB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-stop_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- unsigned long flags;
- u32 stat;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_RP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Receive Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_RB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-
-static void
-set_xmit_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- switch (num_channels) {
- case 6: /* stereo with surround and center/LFE,
- * slots 3,4,6,7,8,9
- */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(6);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(9);
-
- case 4: /* stereo with surround, slots 3,4,7,8 */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8);
-
- case 2: /* stereo, slots 3,4 */
- case 1: /* mono */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4);
- }
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-static void
-set_recv_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Always enable slots 3 and 4 (stereo). Slot 6 is
- * optional Mic ADC, which we don't support yet.
- */
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4);
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-/* Hold spinlock for both start_dac() and start_adc() calls */
-static void
-start_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
-
- if (!db->stopped)
- return;
-
- set_xmit_slots(db->num_channels);
- au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
- au_sync();
-
- au1xxx_dbdma_start(db->dmanr);
-
- db->stopped = 0;
-}
-
-static void
-start_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- int i;
-
- if (!db->stopped)
- return;
-
- /* Put two buffers on the ring to get things started.
- */
- for (i=0; i<2; i++) {
- au1xxx_dbdma_put_dest(db->dmanr, virt_to_phys(db->nextIn),
- db->dma_fragsize, DDMA_FLAGS_IE);
-
- db->nextIn += db->dma_fragsize;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
- }
-
- set_recv_slots(db->num_channels);
- au1xxx_dbdma_start(db->dmanr);
- au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);
- au_sync();
-
- db->stopped = 0;
-}
-
-static int
-prog_dmabuf(struct au1550_state *s, struct dmabuf *db)
-{
- unsigned user_bytes_per_sec;
- unsigned bufs;
- unsigned rate = db->sample_rate;
-
- if (!db->rawbuf) {
- db->ready = db->mapped = 0;
- db->buforder = 5; /* 32 * PAGE_SIZE */
- db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
- if (!db->rawbuf)
- return -ENOMEM;
- }
-
- db->cnt_factor = 1;
- if (db->sample_size == 8)
- db->cnt_factor *= 2;
- if (db->num_channels == 1)
- db->cnt_factor *= 2;
- db->cnt_factor *= db->src_factor;
-
- db->count = 0;
- db->dma_qcount = 0;
- db->nextIn = db->nextOut = db->rawbuf;
-
- db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;
- db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?
- 2 : db->num_channels);
-
- user_bytes_per_sec = rate * db->user_bytes_per_sample;
- bufs = PAGE_SIZE << db->buforder;
- if (db->ossfragshift) {
- if ((1000 << db->ossfragshift) < user_bytes_per_sec)
- db->fragshift = ld2(user_bytes_per_sec/1000);
- else
- db->fragshift = db->ossfragshift;
- } else {
- db->fragshift = ld2(user_bytes_per_sec / 100 /
- (db->subdivision ? db->subdivision : 1));
- if (db->fragshift < 3)
- db->fragshift = 3;
- }
-
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
-
- while (db->numfrag < 4 && db->fragshift > 3) {
- db->fragshift--;
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
- }
-
- if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
- db->numfrag = db->ossmaxfrags;
-
- db->dmasize = db->dma_fragsize * db->numfrag;
- memset(db->rawbuf, 0, bufs);
-
- pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n",
- rate, db->sample_size, db->num_channels);
- pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n",
- db->fragsize, db->cnt_factor, db->dma_fragsize);
- pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize);
-
- db->ready = 1;
- return 0;
-}
-
-static int
-prog_dmabuf_adc(struct au1550_state *s)
-{
- stop_adc(s);
- return prog_dmabuf(s, &s->dma_adc);
-
-}
-
-static int
-prog_dmabuf_dac(struct au1550_state *s)
-{
- stop_dac(s);
- return prog_dmabuf(s, &s->dma_dac);
-}
-
-
-static void dac_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *) dev_id;
- struct dmabuf *db = &s->dma_dac;
- u32 ac97c_stat;
-
- spin_lock(&s->lock);
-
- ac97c_stat = au_readl(PSC_AC97STAT);
- if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
- pr_debug("AC97C status = 0x%08x\n", ac97c_stat);
- db->dma_qcount--;
-
- if (db->count >= db->fragsize) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->count -= db->fragsize;
- db->total_bytes += db->dma_fragsize;
- db->dma_qcount++;
- }
-
- /* wake up anybody listening */
- if (waitqueue_active(&db->wait))
- wake_up(&db->wait);
-
- spin_unlock(&s->lock);
-}
-
-
-static void adc_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *)dev_id;
- struct dmabuf *dp = &s->dma_adc;
- u32 obytes;
- char *obuf;
-
- spin_lock(&s->lock);
-
- /* Pull the buffer from the dma queue.
- */
- au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);
-
- if ((dp->count + obytes) > dp->dmasize) {
- /* Overrun. Stop ADC and log the error
- */
- spin_unlock(&s->lock);
- stop_adc(s);
- dp->error++;
- err("adc overrun");
- return;
- }
-
- /* Put a new empty buffer on the destination DMA.
- */
- au1xxx_dbdma_put_dest(dp->dmanr, virt_to_phys(dp->nextIn),
- dp->dma_fragsize, DDMA_FLAGS_IE);
-
- dp->nextIn += dp->dma_fragsize;
- if (dp->nextIn >= dp->rawbuf + dp->dmasize)
- dp->nextIn -= dp->dmasize;
-
- dp->count += obytes;
- dp->total_bytes += obytes;
-
- /* wake up anybody listening
- */
- if (waitqueue_active(&dp->wait))
- wake_up(&dp->wait);
-
- spin_unlock(&s->lock);
-}
-
-static loff_t
-au1550_llseek(struct file *file, loff_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-
-static int
-au1550_open_mixdev(struct inode *inode, struct file *file)
-{
- mutex_lock(&au1550_ac97_mutex);
- file->private_data = &au1550_state;
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static int
-au1550_release_mixdev(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int
-mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
- unsigned long arg)
-{
- return codec->mixer_ioctl(codec, cmd, arg);
-}
-
-static long
-au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- struct ac97_codec *codec = s->codec;
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = mixdev_ioctl(codec, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static /*const */ struct file_operations au1550_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .unlocked_ioctl = au1550_ioctl_mixdev,
- .open = au1550_open_mixdev,
- .release = au1550_release_mixdev,
-};
-
-static int
-drain_dac(struct au1550_state *s, int nonblock)
-{
- unsigned long flags;
- int count, tmo;
-
- if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
- return 0;
-
- for (;;) {
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- spin_unlock_irqrestore(&s->lock, flags);
- if (count <= s->dma_dac.fragsize)
- break;
- if (signal_pending(current))
- break;
- if (nonblock)
- return -EBUSY;
- tmo = 1000 * count / (s->no_vra ?
- 48000 : s->dma_dac.sample_rate);
- tmo /= s->dma_dac.dma_bytes_per_sample;
- au1550_delay(tmo);
- }
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
-}
-
-static inline u8 S16_TO_U8(s16 ch)
-{
- return (u8) (ch >> 8) + 0x80;
-}
-static inline s16 U8_TO_S16(u8 ch)
-{
- return (s16) (ch - 0x80) << 8;
-}
-
-/*
- * Translates user samples to dma buffer suitable for AC'97 DAC data:
- * If mono, copy left channel to right channel in dma buffer.
- * If 8 bit samples, cvt to 16-bit before writing to dma buffer.
- * If interpolating (no VRA), duplicate every audio frame src_factor times.
- */
-static int
-translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
- s16 ch, dmasample[6];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_from_user(dmabuf, userbuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- if (copy_from_user(usersample, userbuf,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- ch = U8_TO_S16(usersample[i]);
- else
- ch = *((s16 *) (&usersample[i * 2]));
- dmasample[i] = ch;
- if (mono)
- dmasample[i + 1] = ch; /* right channel */
- }
-
- /* duplicate every audio frame src_factor times
- */
- for (i = 0; i < db->src_factor; i++)
- memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Translates AC'97 ADC samples to user buffer:
- * If mono, send only left channel to user buffer.
- * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer.
- * If decimating (no VRA), skip over src_factor audio frames.
- */
-static int
-translate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_to_user(userbuf, dmabuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- usersample[i] =
- S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));
- else
- *((s16 *) (&usersample[i * 2])) =
- *((s16 *) (&dmabuf[i * 2]));
- }
-
- if (copy_to_user(userbuf, usersample,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Copy audio data to/from user buffer from/to dma buffer, taking care
- * that we wrap when reading/writing the dma buffer. Returns actual byte
- * count written to or read from the dma buffer.
- */
-static int
-copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
-{
- char *bufptr = to_user ? db->nextOut : db->nextIn;
- char *bufend = db->rawbuf + db->dmasize;
- int cnt, ret;
-
- if (bufptr + count > bufend) {
- int partial = (int) (bufend - bufptr);
- if (to_user) {
- if ((cnt = translate_to_user(db, userbuf,
- bufptr, partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_to_user(db, userbuf + partial,
- db->rawbuf,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- } else {
- if ((cnt = translate_from_user(db, bufptr, userbuf,
- partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_from_user(db, db->rawbuf,
- userbuf + partial,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- }
- } else {
- if (to_user)
- ret = translate_to_user(db, userbuf, bufptr, count);
- else
- ret = translate_from_user(db, bufptr, userbuf, count);
- }
-
- return ret;
-}
-
-
-static ssize_t
-au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_adc;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for samples in ADC dma buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- if (db->stopped)
- start_adc(s);
- avail = db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from nextOut to user
- */
- if ((cnt = copy_dmabuf_user(db, buffer,
- count > avail ?
- avail : count, 1)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count -= cnt;
- db->nextOut += cnt;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-static ssize_t
-au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_dac;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret = 0;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- pr_debug("write: count=%d\n", count);
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for space in playback buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- avail = (int) db->dmasize - db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from user to nextIn
- */
- if ((cnt = copy_dmabuf_user(db, (char *) buffer,
- count > avail ?
- avail : count, 0)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count += cnt;
- db->nextIn += cnt;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
-
- /* If the data is available, we want to keep two buffers
- * on the dma queue. If the queue count reaches zero,
- * we know the dma has stopped.
- */
- while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->total_bytes += db->dma_fragsize;
- if (db->dma_qcount == 0)
- start_dac(s);
- db->dma_qcount++;
- }
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-
-/* No kernel lock - we have our own spinlock */
-static unsigned int
-au1550_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- if (file->f_mode & FMODE_WRITE) {
- if (!s->dma_dac.ready)
- return 0;
- poll_wait(file, &s->dma_dac.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- if (!s->dma_adc.ready)
- return 0;
- poll_wait(file, &s->dma_adc.wait, wait);
- }
-
- spin_lock_irqsave(&s->lock, flags);
-
- if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize)
- mask |= POLLIN | POLLRDNORM;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >=
- (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- } else {
- if ((signed) s->dma_dac.dmasize >=
- s->dma_dac.count + (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- }
- }
- spin_unlock_irqrestore(&s->lock, flags);
- return mask;
-}
-
-static int
-au1550_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db;
- unsigned long size;
- int ret = 0;
-
- mutex_lock(&au1550_ac97_mutex);
- mutex_lock(&s->sem);
- if (vma->vm_flags & VM_WRITE)
- db = &s->dma_dac;
- else if (vma->vm_flags & VM_READ)
- db = &s->dma_adc;
- else {
- ret = -EINVAL;
- goto out;
- }
- if (vma->vm_pgoff != 0) {
- ret = -EINVAL;
- goto out;
- }
- size = vma->vm_end - vma->vm_start;
- if (size > (PAGE_SIZE << db->buforder)) {
- ret = -EINVAL;
- goto out;
- }
- if (remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page(db->rawbuf)),
- size, vma->vm_page_prot)) {
- ret = -EAGAIN;
- goto out;
- }
- vma->vm_flags &= ~VM_IO;
- db->mapped = 1;
-out:
- mutex_unlock(&s->sem);
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-#ifdef DEBUG
-static struct ioctl_str_t {
- unsigned int cmd;
- const char *str;
-} ioctl_str[] = {
- {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
- {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
- {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
- {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
- {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
- {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
- {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
- {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
- {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
- {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
- {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
- {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
- {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
- {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
- {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
- {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
- {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
- {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
- {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
- {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
- {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
- {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
- {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
- {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
- {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
- {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
- {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
- {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
- {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
- {OSS_GETVERSION, "OSS_GETVERSION"},
- {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
- {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
- {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
- {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
-};
-#endif
-
-static int
-dma_count_done(struct dmabuf *db)
-{
- if (db->stopped)
- return 0;
-
- return db->dma_fragsize - au1xxx_get_dma_residue(db->dmanr);
-}
-
-
-static int
-au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int count;
- int val, mapped, ret, diff;
-
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
-
-#ifdef DEBUG
- for (count = 0; count < ARRAY_SIZE(ioctl_str); count++) {
- if (ioctl_str[count].cmd == cmd)
- break;
- }
- if (count < ARRAY_SIZE(ioctl_str))
- pr_debug("ioctl %s, arg=0x%lxn", ioctl_str[count].str, arg);
- else
- pr_debug("ioctl 0x%x unknown, arg=0x%lx\n", cmd, arg);
-#endif
-
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *) arg);
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- return drain_dac(s, file->f_flags & O_NONBLOCK);
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
- DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
-
- case SNDCTL_DSP_RESET:
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- synchronize_irq();
- s->dma_dac.count = s->dma_dac.total_bytes = 0;
- s->dma_dac.nextIn = s->dma_dac.nextOut =
- s->dma_dac.rawbuf;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- synchronize_irq();
- s->dma_adc.count = s->dma_adc.total_bytes = 0;
- s->dma_adc.nextIn = s->dma_adc.nextOut =
- s->dma_adc.rawbuf;
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val >= 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- set_adc_rate(s, val);
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- set_dac_rate(s, val);
- }
- if (s->open_mode & FMODE_READ)
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- if (s->open_mode & FMODE_WRITE)
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.num_channels = val ? 2 : 1;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.num_channels = val ? 2 : 1;
- if (s->codec_ext_caps & AC97_EXT_DACS) {
- /* disable surround and center/lfe in AC'97
- */
- u16 ext_stat = rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- if (val < 0 || val > 2)
- return -EINVAL;
- stop_adc(s);
- s->dma_adc.num_channels = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- switch (val) {
- case 1:
- case 2:
- break;
- case 3:
- case 5:
- return -EINVAL;
- case 4:
- if (!(s->codec_ext_caps &
- AC97_EXTID_SDAC))
- return -EINVAL;
- break;
- case 6:
- if ((s->codec_ext_caps &
- AC97_EXT_DACS) != AC97_EXT_DACS)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- stop_dac(s);
- if (val <= 2 &&
- (s->codec_ext_caps & AC97_EXT_DACS)) {
- /* disable surround and center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- } else if (val >= 4) {
- /* enable surround, center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- ext_stat &= ~AC97_EXTSTAT_PRJ;
- if (val == 6)
- ext_stat &=
- ~(AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRK);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat);
- }
-
- s->dma_dac.num_channels = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- if (val == AFMT_S16_LE)
- s->dma_adc.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_adc.sample_size = 8;
- }
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- if (val == AFMT_S16_LE)
- s->dma_dac.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_dac.sample_size = 8;
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- } else {
- if (file->f_mode & FMODE_READ)
- val = (s->dma_adc.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- else
- val = (s->dma_dac.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- spin_lock_irqsave(&s->lock, flags);
- if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
- val |= PCM_ENABLE_OUTPUT;
- spin_unlock_irqrestore(&s->lock, flags);
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_adc(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_adc(s);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_dac(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_dac(s);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- abinfo.fragsize = s->dma_dac.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = (s->dma_dac.dmasize - count) /
- s->dma_dac.cnt_factor;
- abinfo.fragstotal = s->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
- pr_debug("ioctl SNDCTL_DSP_GETOSPACE: bytes=%d, fragments=%d\n", abinfo.bytes, abinfo.fragments);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- abinfo.fragsize = s->dma_adc.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_adc.count;
- count += dma_count_done(&s->dma_adc);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = count / s->dma_adc.cnt_factor;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- count /= s->dma_dac.cnt_factor;
- return put_user(count, (int *) arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_adc.total_bytes;
- count = s->dma_adc.count;
- if (!s->dma_adc.stopped) {
- diff = dma_count_done(&s->dma_adc);
- count += diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff -
- virt_to_phys(s->dma_adc.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) -
- virt_to_phys(s->dma_adc.rawbuf);
- if (s->dma_adc.mapped)
- s->dma_adc.count &= (s->dma_adc.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_dac.total_bytes;
- count = s->dma_dac.count;
- if (!s->dma_dac.stopped) {
- diff = dma_count_done(&s->dma_dac);
- count -= diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff -
- virt_to_phys(s->dma_dac.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) -
- virt_to_phys(s->dma_dac.rawbuf);
- if (s->dma_dac.mapped)
- s->dma_dac.count &= (s->dma_dac.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_dac.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE)
- return put_user(s->dma_dac.fragsize, (int *) arg);
- else
- return put_user(s->dma_adc.fragsize, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ossfragshift = val & 0xffff;
- s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_adc.ossfragshift < 4)
- s->dma_adc.ossfragshift = 4;
- if (s->dma_adc.ossfragshift > 15)
- s->dma_adc.ossfragshift = 15;
- if (s->dma_adc.ossmaxfrags < 4)
- s->dma_adc.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ossfragshift = val & 0xffff;
- s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_dac.ossfragshift < 4)
- s->dma_dac.ossfragshift = 4;
- if (s->dma_dac.ossfragshift > 15)
- s->dma_dac.ossfragshift = 15;
- if (s->dma_dac.ossmaxfrags < 4)
- s->dma_dac.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
- (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
- return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.subdivision = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.subdivision = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.num_channels, (int *)arg);
- else
- return put_user(s->dma_dac.num_channels, (int *)arg);
-
- case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.sample_size, (int *)arg);
- else
- return put_user(s->dma_dac.sample_size, (int *)arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
-
- return mixdev_ioctl(s->codec, cmd, arg);
-}
-
-static long
-au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = au1550_ioctl(file, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static int
-au1550_open(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- DECLARE_WAITQUEUE(wait, current);
- struct au1550_state *s = &au1550_state;
- int ret;
-
-#ifdef DEBUG
- if (file->f_flags & O_NONBLOCK)
- pr_debug("open: non-blocking\n");
- else
- pr_debug("open: blocking\n");
-#endif
-
- file->private_data = s;
- mutex_lock(&au1550_ac97_mutex);
- /* wait for device to become free */
- mutex_lock(&s->open_mutex);
- while (s->open_mode & file->f_mode) {
- ret = -EBUSY;
- if (file->f_flags & O_NONBLOCK)
- goto out;
- add_wait_queue(&s->open_wait, &wait);
- __set_current_state(TASK_INTERRUPTIBLE);
- mutex_unlock(&s->open_mutex);
- schedule();
- remove_wait_queue(&s->open_wait, &wait);
- set_current_state(TASK_RUNNING);
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- goto out2;
- mutex_lock(&s->open_mutex);
- }
-
- stop_dac(s);
- stop_adc(s);
-
- if (file->f_mode & FMODE_READ) {
- s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
- s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
- s->dma_adc.num_channels = 1;
- s->dma_adc.sample_size = 8;
- set_adc_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_adc.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_WRITE) {
- s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
- s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
- s->dma_dac.num_channels = 1;
- s->dma_dac.sample_size = 8;
- set_dac_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_dac.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_READ) {
- if ((ret = prog_dmabuf_adc(s)))
- goto out;
- }
- if (file->f_mode & FMODE_WRITE) {
- if ((ret = prog_dmabuf_dac(s)))
- goto out;
- }
-
- s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_init(&s->sem);
- ret = 0;
-out:
- mutex_unlock(&s->open_mutex);
-out2:
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-static int
-au1550_release(struct inode *inode, struct file *file)
-{
- struct au1550_state *s = file->private_data;
-
- mutex_lock(&au1550_ac97_mutex);
-
- if (file->f_mode & FMODE_WRITE) {
- mutex_unlock(&au1550_ac97_mutex);
- drain_dac(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&au1550_ac97_mutex);
- }
-
- mutex_lock(&s->open_mutex);
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- kfree(s->dma_dac.rawbuf);
- s->dma_dac.rawbuf = NULL;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- kfree(s->dma_adc.rawbuf);
- s->dma_adc.rawbuf = NULL;
- }
- s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
- mutex_unlock(&s->open_mutex);
- wake_up(&s->open_wait);
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static /*const */ struct file_operations au1550_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .read = au1550_read,
- .write = au1550_write,
- .poll = au1550_poll,
- .unlocked_ioctl = au1550_unlocked_ioctl,
- .mmap = au1550_mmap,
- .open = au1550_open,
- .release = au1550_release,
-};
-
-MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
-MODULE_DESCRIPTION("Au1550 AC97 Audio Driver");
-MODULE_LICENSE("GPL");
-
-
-static int __devinit
-au1550_probe(void)
-{
- struct au1550_state *s = &au1550_state;
- int val;
-
- memset(s, 0, sizeof(struct au1550_state));
-
- init_waitqueue_head(&s->dma_adc.wait);
- init_waitqueue_head(&s->dma_dac.wait);
- init_waitqueue_head(&s->open_wait);
- mutex_init(&s->open_mutex);
- spin_lock_init(&s->lock);
-
- s->codec = ac97_alloc_codec();
- if(s->codec == NULL) {
- err("Out of memory");
- return -1;
- }
- s->codec->private_data = s;
- s->codec->id = 0;
- s->codec->codec_read = rdcodec;
- s->codec->codec_write = wrcodec;
- s->codec->codec_wait = waitcodec;
-
- if (!request_mem_region(CPHYSADDR(AC97_PSC_SEL),
- 0x30, "Au1550 AC97")) {
- err("AC'97 ports in use");
- }
-
- /* Allocate the DMA Channels
- */
- if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
- DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
- err("Can't get DAC DMA");
- goto err_dma1;
- }
- au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get DAC DMA descriptors");
- goto err_dma1;
- }
-
- if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
- DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
- err("Can't get ADC DMA");
- goto err_dma2;
- }
- au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get ADC DMA descriptors");
- goto err_dma2;
- }
-
- pr_info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
-
- /* register devices */
-
- if ((s->dev_audio = register_sound_dsp(&au1550_audio_fops, -1)) < 0)
- goto err_dev1;
- if ((s->codec->dev_mixer =
- register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
- goto err_dev2;
-
- /* The GPIO for the appropriate PSC was configured by the
- * board specific start up.
- *
- * configure PSC for AC'97
- */
- au_writel(0, AC97_PSC_CTRL); /* Disable PSC */
- au_sync();
- au_writel((PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE), AC97_PSC_SEL);
- au_sync();
-
- /* cold reset the AC'97
- */
- au_writel(PSC_AC97RST_RST, PSC_AC97RST);
- au_sync();
- au1550_delay(10);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* need to delay around 500msec(bleech) to give
- some CODECs enough time to wakeup */
- au1550_delay(500);
-
- /* warm reset the AC'97 to start the bitclk
- */
- au_writel(PSC_AC97RST_SNC, PSC_AC97RST);
- au_sync();
- udelay(100);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* Enable PSC
- */
- au_writel(PSC_CTRL_ENABLE, AC97_PSC_CTRL);
- au_sync();
-
- /* Wait for PSC ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_SR) == 0);
-
- /* Configure AC97 controller.
- * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
- */
- val = PSC_AC97CFG_SET_LEN(16);
- val |= PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8;
-
- /* Enable device so we can at least
- * talk over the AC-link.
- */
- au_writel(val, PSC_AC97CFG);
- au_writel(PSC_AC97MSK_ALLMASK, PSC_AC97MSK);
- au_sync();
- val |= PSC_AC97CFG_DE_ENABLE;
- au_writel(val, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_DR) == 0);
-
- /* codec init */
- if (!ac97_probe_codec(s->codec))
- goto err_dev3;
-
- s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
- s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
- pr_info("AC'97 Base/Extended ID = %04x/%04x",
- s->codec_base_caps, s->codec_ext_caps);
-
- if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
- /* codec does not support VRA
- */
- s->no_vra = 1;
- } else if (!vra) {
- /* Boot option says disable VRA
- */
- u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat & ~AC97_EXTSTAT_VRA);
- s->no_vra = 1;
- }
- if (s->no_vra)
- pr_info("no VRA, interpolating and decimating");
-
- /* set mic to be the recording source */
- val = SOUND_MASK_MIC;
- mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
- (unsigned long) &val);
-
- return 0;
-
- err_dev3:
- unregister_sound_mixer(s->codec->dev_mixer);
- err_dev2:
- unregister_sound_dsp(s->dev_audio);
- err_dev1:
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- err_dma2:
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- err_dma1:
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
-
- ac97_release_codec(s->codec);
- return -1;
-}
-
-static void __devinit
-au1550_remove(void)
-{
- struct au1550_state *s = &au1550_state;
-
- if (!s)
- return;
- synchronize_irq();
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
- unregister_sound_dsp(s->dev_audio);
- unregister_sound_mixer(s->codec->dev_mixer);
- ac97_release_codec(s->codec);
-}
-
-static int __init
-init_au1550(void)
-{
- return au1550_probe();
-}
-
-static void __exit
-cleanup_au1550(void)
-{
- au1550_remove();
-}
-
-module_init(init_au1550);
-module_exit(cleanup_au1550);
-
-#ifndef MODULE
-
-static int __init
-au1550_setup(char *options)
-{
- char *this_opt;
-
- if (!options || !*options)
- return 0;
-
- while ((this_opt = strsep(&options, ","))) {
- if (!*this_opt)
- continue;
- if (!strncmp(this_opt, "vra", 3)) {
- vra = 1;
- }
- }
-
- return 1;
-}
-
-__setup("au1550_audio=", au1550_setup);
-
-#endif /* MODULE */
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 389cd7931668..e90d103e177e 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -534,6 +534,14 @@ config SND_ES1968_INPUT
If you say N the buttons will directly control the master volume.
It is recommended to say Y.
+config SND_ES1968_RADIO
+ bool "Enable TEA5757 radio tuner support for es1968"
+ depends on SND_ES1968
+ depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968
+ help
+ Say Y here to include support for TEA5757 radio tuner integrated on
+ some MediaForte cards (e.g. SF64-PCE2).
+
config SND_FM801
tristate "ForteMedia FM801"
select SND_OPL3_LIB
@@ -552,13 +560,13 @@ config SND_FM801_TEA575X_BOOL
depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
help
Say Y here to include support for soundcards based on the ForteMedia
- FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
- Forte SF256-PCS-02) into the snd-fm801 driver.
+ FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
+ SF64-PCR) into the snd-fm801 driver.
-config SND_FM801_TEA575X
+config SND_TEA575X
tristate
- depends on SND_FM801_TEA575X_BOOL
- default SND_FM801
+ depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
+ default SND_FM801 || SND_ES1968
source "sound/pci/hda/Kconfig"
@@ -658,6 +666,15 @@ config SND_KORG1212
To compile this driver as a module, choose M here: the module
will be called snd-korg1212.
+config SND_LOLA
+ tristate "Digigram Lola"
+ select SND_PCM
+ help
+ Say Y to include support for Digigram Lola boards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-lola.
+
config SND_LX6464ES
tristate "Digigram LX6464ES"
select SND_PCM
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 9cf4348ec137..54fe325e3aa5 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_SND) += \
ca0106/ \
cs46xx/ \
cs5535audio/ \
+ lola/ \
lx6464es/ \
echoaudio/ \
emu10k1/ \
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index f8ccc9677c6f..2ca6f4f85b41 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -42,10 +42,29 @@
#include <sound/tlv.h>
#include <sound/hwdep.h>
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
+#if defined CONFIG_SND_DEBUG
+/* copied from pcm_lib.c, hope later patch will make that version public
+and this copy can be removed */
+static void pcm_debug_name(struct snd_pcm_substream *substream,
+ char *name, size_t len)
+{
+ snprintf(name, len, "pcmC%dD%d%c:%d",
+ substream->pcm->card->number,
+ substream->pcm->device,
+ substream->stream ? 'c' : 'p',
+ substream->number);
+}
+#define DEBUG_NAME(substream, name) char name[16]; pcm_debug_name(substream, name, sizeof(name))
+#else
+#define pcm_debug_name(s, n, l) do { } while (0)
+#define DEBUG_NAME(name, substream) do { } while (0)
+#endif
+
#if defined CONFIG_SND_DEBUG_VERBOSE
/**
* snd_printddd - very verbose debug printk
@@ -58,7 +77,7 @@ MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
#define snd_printddd(format, args...) \
__snd_printk(3, __FILE__, __LINE__, format, ##args)
#else
-#define snd_printddd(format, args...) do { } while (0)
+#define snd_printddd(format, args...) do { } while (0)
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
@@ -101,13 +120,6 @@ static int adapter_fs = DEFAULT_SAMPLERATE;
#define PERIOD_BYTES_MIN 2048
#define BUFFER_BYTES_MAX (512 * 1024)
-/* convert stream to character */
-#define SCHR(s) ((s == SNDRV_PCM_STREAM_PLAYBACK) ? 'P' : 'C')
-
-/*#define TIMER_MILLISECONDS 20
-#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000)
-*/
-
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
struct clk_source {
@@ -136,7 +148,7 @@ struct snd_card_asihpi {
u32 h_mixer;
struct clk_cache cc;
- u16 support_mmap;
+ u16 can_dma;
u16 support_grouping;
u16 support_mrx;
u16 update_interval_frames;
@@ -155,6 +167,7 @@ struct snd_card_asihpi_pcm {
unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */
unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */
+ unsigned int drained_count;
struct snd_pcm_substream *substream;
u32 h_stream;
struct hpi_format format;
@@ -288,19 +301,26 @@ static u16 handle_error(u16 err, int line, char *filename)
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
/***************************** GENERAL PCM ****************/
-static void print_hwparams(struct snd_pcm_hw_params *p)
+
+static void print_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *p)
{
- snd_printd("HWPARAMS \n");
- snd_printd("samplerate %d \n", params_rate(p));
- snd_printd("Channels %d \n", params_channels(p));
- snd_printd("Format %d \n", params_format(p));
- snd_printd("subformat %d \n", params_subformat(p));
- snd_printd("Buffer bytes %d \n", params_buffer_bytes(p));
- snd_printd("Period bytes %d \n", params_period_bytes(p));
- snd_printd("access %d \n", params_access(p));
- snd_printd("period_size %d \n", params_period_size(p));
- snd_printd("periods %d \n", params_periods(p));
- snd_printd("buffer_size %d \n", params_buffer_size(p));
+ DEBUG_NAME(substream, name);
+ snd_printd("%s HWPARAMS\n", name);
+ snd_printd(" samplerate %d Hz\n", params_rate(p));
+ snd_printd(" channels %d\n", params_channels(p));
+ snd_printd(" format %d\n", params_format(p));
+ snd_printd(" subformat %d\n", params_subformat(p));
+ snd_printd(" buffer %d B\n", params_buffer_bytes(p));
+ snd_printd(" period %d B\n", params_period_bytes(p));
+ snd_printd(" access %d\n", params_access(p));
+ snd_printd(" period_size %d\n", params_period_size(p));
+ snd_printd(" periods %d\n", params_periods(p));
+ snd_printd(" buffer_size %d\n", params_buffer_size(p));
+ snd_printd(" %d B/s\n", params_rate(p) *
+ params_channels(p) *
+ snd_pcm_format_width(params_format(p)) / 8);
+
}
static snd_pcm_format_t hpi_to_alsa_formats[] = {
@@ -451,7 +471,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
int width;
unsigned int bytes_per_sec;
- print_hwparams(params);
+ print_hwparams(substream, params);
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (err < 0)
return err;
@@ -459,10 +479,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
if (err)
return err;
- snd_printdd("format %d, %d chans, %d_hz\n",
- format, params_channels(params),
- params_rate(params));
-
hpi_handle_error(hpi_format_create(&dpcm->format,
params_channels(params),
format, params_rate(params), 0, 0));
@@ -477,8 +493,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
}
dpcm->hpi_buffer_attached = 0;
- if (card->support_mmap) {
-
+ if (card->can_dma) {
err = hpi_stream_host_buffer_attach(dpcm->h_stream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
@@ -509,8 +524,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
dpcm->bytes_per_sec = bytes_per_sec;
dpcm->buffer_bytes = params_buffer_bytes(params);
dpcm->period_bytes = params_period_bytes(params);
- snd_printdd("buffer_bytes=%d, period_bytes=%d, bps=%d\n",
- dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec);
return 0;
}
@@ -564,9 +577,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
u16 e;
+ DEBUG_NAME(substream, name);
+
+ snd_printdd("%s trigger\n", name);
- snd_printdd("%c%d trigger\n",
- SCHR(substream->stream), substream->number);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_pcm_group_for_each_entry(s, substream) {
@@ -580,8 +594,8 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
if (substream->stream != s->stream)
continue;
- if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
- (card->support_mmap)) {
+ ds->drained_count = 0;
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* How do I know how much valid data is present
* in buffer? Must be at least one period!
* Guessing 2 periods, but if
@@ -599,9 +613,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
}
if (card->support_grouping) {
- snd_printdd("\t%c%d group\n",
- SCHR(s->stream),
- s->number);
+ snd_printdd("%d group\n", s->number);
e = hpi_stream_group_add(
dpcm->h_stream,
ds->h_stream);
@@ -618,7 +630,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
/* start the master stream */
snd_card_asihpi_pcm_timer_start(substream);
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
- !card->support_mmap)
+ !card->can_dma)
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
@@ -636,9 +648,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
if (card->support_grouping) {
- snd_printdd("\t%c%d group\n",
- SCHR(s->stream),
- s->number);
+ snd_printdd("%d group\n", s->number);
snd_pcm_trigger_done(s, substream);
} else
break;
@@ -732,9 +742,9 @@ static void snd_card_asihpi_timer_function(unsigned long data)
int loops = 0;
u16 state;
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
+ DEBUG_NAME(substream, name);
- snd_printdd("%c%d snd_card_asihpi_timer_function\n",
- SCHR(substream->stream), substream->number);
+ snd_printdd("%s snd_card_asihpi_timer_function\n", name);
/* find minimum newdata and buffer pos in group */
snd_pcm_group_for_each_entry(s, substream) {
@@ -756,6 +766,9 @@ static void snd_card_asihpi_timer_function(unsigned long data)
/* number of bytes in on-card buffer */
runtime->delay = on_card_bytes;
+ if (!card->can_dma)
+ on_card_bytes = bytes_avail;
+
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
if (state == HPI_STATE_STOPPED) {
@@ -763,12 +776,18 @@ static void snd_card_asihpi_timer_function(unsigned long data)
(on_card_bytes < ds->pcm_buf_host_rw_ofs)) {
hpi_handle_error(hpi_stream_start(ds->h_stream));
snd_printdd("P%d start\n", s->number);
+ ds->drained_count = 0;
}
} else if (state == HPI_STATE_DRAINED) {
snd_printd(KERN_WARNING "P%d drained\n",
s->number);
- /*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
- continue; */
+ ds->drained_count++;
+ if (ds->drained_count > 2) {
+ snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
+ continue;
+ }
+ } else {
+ ds->drained_count = 0;
}
} else
pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
@@ -786,16 +805,18 @@ static void snd_card_asihpi_timer_function(unsigned long data)
newdata);
}
- snd_printdd("hw_ptr x%04lX, appl_ptr x%04lX\n",
+ snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n",
(unsigned long)frames_to_bytes(runtime,
runtime->status->hw_ptr),
(unsigned long)frames_to_bytes(runtime,
runtime->control->appl_ptr));
- snd_printdd("%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X,"
- " aux=x%04X space=x%04X\n",
- loops, SCHR(s->stream), s->number,
- state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail,
+ snd_printdd("%d S=%d, "
+ "rw=0x%04X, dma=0x%04X, left=0x%04X, "
+ "aux=0x%04X space=0x%04X\n",
+ s->number, state,
+ ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs,
+ (int)bytes_avail,
(int)on_card_bytes, buffer_size-bytes_avail);
loops++;
}
@@ -814,7 +835,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
next_jiffies = max(next_jiffies, 1U);
dpcm->timer.expires = jiffies + next_jiffies;
- snd_printdd("jif %d buf pos x%04X newdata x%04X xfer x%04X\n",
+ snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n",
next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
snd_pcm_group_for_each_entry(s, substream) {
@@ -826,30 +847,63 @@ static void snd_card_asihpi_timer_function(unsigned long data)
ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
- if (xfercount && (on_card_bytes <= ds->period_bytes)) {
- if (card->support_mmap) {
- if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_printddd("P%d write x%04x\n",
+ if (xfercount &&
+ /* Limit use of on card fifo for playback */
+ ((on_card_bytes <= ds->period_bytes) ||
+ (s->stream == SNDRV_PCM_STREAM_CAPTURE)))
+
+ {
+
+ unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
+ unsigned int xfer1, xfer2;
+ char *pd = &s->runtime->dma_area[buf_ofs];
+
+ if (card->can_dma) { /* buffer wrap is handled at lower level */
+ xfer1 = xfercount;
+ xfer2 = 0;
+ } else {
+ xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
+ xfer2 = xfercount - xfer1;
+ }
+
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_printddd("P%d write1 0x%04X 0x%04X\n",
+ s->number, xfer1, buf_ofs);
+ hpi_handle_error(
+ hpi_outstream_write_buf(
+ ds->h_stream, pd, xfer1,
+ &ds->format));
+
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+
+ snd_printddd("P%d write2 0x%04X 0x%04X\n",
s->number,
- ds->period_bytes);
+ xfercount - xfer1, buf_ofs);
hpi_handle_error(
hpi_outstream_write_buf(
- ds->h_stream,
- &s->runtime->
- dma_area[0],
- xfercount,
+ ds->h_stream, pd,
+ xfercount - xfer1,
&ds->format));
- } else {
- snd_printddd("C%d read x%04x\n",
- s->number,
- xfercount);
+ }
+ } else {
+ snd_printddd("C%d read1 0x%04x\n",
+ s->number, xfer1);
+ hpi_handle_error(
+ hpi_instream_read_buf(
+ ds->h_stream,
+ pd, xfer1));
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+ snd_printddd("C%d read2 0x%04x\n",
+ s->number, xfer2);
hpi_handle_error(
hpi_instream_read_buf(
ds->h_stream,
- NULL, xfercount));
+ pd, xfer2));
}
- ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
- } /* else R/W will be handled by read/write callbacks */
+ }
+ ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
ds->pcm_buf_elapsed_dma_ofs = pcm_buf_dma_ofs;
snd_pcm_period_elapsed(s);
}
@@ -863,7 +917,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
- snd_printdd(KERN_INFO "Playback ioctl %d\n", cmd);
+ snd_printddd(KERN_INFO "P%d ioctl %d\n", substream->number, cmd);
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
@@ -873,7 +927,7 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- snd_printdd("playback prepare %d\n", substream->number);
+ snd_printdd("P%d prepare\n", substream->number);
hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
dpcm->pcm_buf_host_rw_ofs = 0;
@@ -890,7 +944,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t ptr;
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
- snd_printddd("playback_pointer=x%04lx\n", (unsigned long)ptr);
+ snd_printddd("P%d pointer = 0x%04lx\n", substream->number, (unsigned long)ptr);
return ptr;
}
@@ -986,11 +1040,9 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE;
-
- if (card->support_mmap)
- snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
if (card->support_grouping)
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
@@ -998,7 +1050,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
/* struct is copied, so can create initializer dynamically */
runtime->hw = snd_card_asihpi_playback;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -1028,58 +1080,6 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- unsigned int len;
-
- len = frames_to_bytes(runtime, count);
-
- if (copy_from_user(runtime->dma_area, src, len))
- return -EFAULT;
-
- snd_printddd("playback copy%d %u bytes\n",
- substream->number, len);
-
- hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream,
- runtime->dma_area, len, &dpcm->format));
-
- dpcm->pcm_buf_host_rw_ofs += len;
-
- return 0;
-}
-
-static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
- substream, int channel,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
-{
- /* Usually writes silence to DMA buffer, which should be overwritten
- by real audio later. Our fifos cannot be overwritten, and are not
- free-running DMAs. Silence is output on fifo underflow.
- This callback is still required to allow the copy callback to be used.
- */
- return 0;
-}
-
-static struct snd_pcm_ops snd_card_asihpi_playback_ops = {
- .open = snd_card_asihpi_playback_open,
- .close = snd_card_asihpi_playback_close,
- .ioctl = snd_card_asihpi_playback_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_playback_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_playback_pointer,
- .copy = snd_card_asihpi_playback_copy,
- .silence = snd_card_asihpi_playback_silence,
-};
-
static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
@@ -1211,18 +1211,16 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
snd_card_asihpi_capture_format(card, dpcm->h_stream,
&snd_card_asihpi_capture);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
- snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
-
- if (card->support_mmap)
- snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
if (card->support_grouping)
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
runtime->hw = snd_card_asihpi_capture;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -1246,28 +1244,6 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- u32 len;
-
- len = frames_to_bytes(runtime, count);
-
- snd_printddd("capture copy%d %d bytes\n", substream->number, len);
- hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream,
- runtime->dma_area, len));
-
- dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len;
-
- if (copy_to_user(dst, runtime->dma_area, len))
- return -EFAULT;
-
- return 0;
-}
-
static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
@@ -1279,18 +1255,6 @@ static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.pointer = snd_card_asihpi_capture_pointer,
};
-static struct snd_pcm_ops snd_card_asihpi_capture_ops = {
- .open = snd_card_asihpi_capture_open,
- .close = snd_card_asihpi_capture_close,
- .ioctl = snd_card_asihpi_capture_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_capture_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_capture_pointer,
- .copy = snd_card_asihpi_capture_copy
-};
-
static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
int device, int substreams)
{
@@ -1303,17 +1267,10 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
if (err < 0)
return err;
/* pointer to ops struct is stored, dont change ops afterwards! */
- if (asihpi->support_mmap) {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_asihpi_playback_mmap_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_asihpi_capture_mmap_ops);
- } else {
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_asihpi_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_asihpi_capture_ops);
- }
pcm->private_data = asihpi;
pcm->info_flags = 0;
@@ -1413,14 +1370,16 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
struct hpi_control *hpi_ctl,
char *name)
{
- char *dir = "";
+ char *dir;
memset(snd_control, 0, sizeof(*snd_control));
snd_control->name = hpi_ctl->name;
snd_control->private_value = hpi_ctl->h_control;
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
snd_control->index = 0;
- if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
+ if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
+ dir = ""; /* clock is neither capture nor playback */
+ else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
dir = "Capture "; /* On or towards a PCM capture destination*/
else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
(!hpi_ctl->dst_node_type))
@@ -1433,7 +1392,7 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
dir = "Playback "; /* PCM Playback source, or output node */
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
- sprintf(hpi_ctl->name, "%s%d %s%d %s%s",
+ sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
asihpi_dst_names[hpi_ctl->dst_node_type],
@@ -2875,14 +2834,14 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
if (err)
asihpi->update_interval_frames = 512;
- if (!asihpi->support_mmap)
+ if (!asihpi->can_dma)
asihpi->update_interval_frames *= 2;
hpi_handle_error(hpi_instream_open(asihpi->adapter_index,
0, &h_stream));
err = hpi_instream_host_buffer_free(h_stream);
- asihpi->support_mmap = (!err);
+ asihpi->can_dma = (!err);
hpi_handle_error(hpi_instream_close(h_stream));
@@ -2894,8 +2853,8 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
asihpi->out_max_chans = 2;
}
- snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
- asihpi->support_mmap,
+ snd_printk(KERN_INFO "has dma:%d, grouping:%d, mrx:%d\n",
+ asihpi->can_dma,
asihpi->support_grouping,
asihpi->support_mrx
);
@@ -2925,10 +2884,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
by enable_hwdep module param*/
snd_asihpi_hpi_new(asihpi, 0, NULL);
- if (asihpi->support_mmap)
- strcpy(card->driver, "ASIHPI-MMAP");
- else
- strcpy(card->driver, "ASIHPI");
+ strcpy(card->driver, "ASIHPI");
sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
sprintf(card->longname, "%s %i",
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index 8c8aac4c567e..df4aed5295dd 100644
--- a/sound/pci/asihpi/hpi6000.c
+++ b/sound/pci/asihpi/hpi6000.c
@@ -200,8 +200,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
@@ -222,9 +222,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -279,6 +276,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
adapter_get_asserts(pao, phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
+
default:
hw_message(pao, phm, phr);
break;
@@ -333,26 +334,22 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
- /* subsytem messages get executed by every HPI. */
- /* All other messages are ignored unless the adapter index matches */
- /* an adapter in the HPI */
- /*HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->wObject, phm->wFunction); */
-
- /* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+ HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n",
+ phm->adapter_index);
return;
}
+ /* Don't even try to communicate with crashed DSP */
if (pao->dsp_crashed >= 10) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
+ HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n",
+ phm->adapter_index);
return;
}
}
@@ -463,15 +460,9 @@ static void subsys_create_adapter(struct hpi_message *phm,
phr->error = 0;
}
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- pao = hpi_find_adapter(phm->obj_index);
- if (!pao)
- return;
-
delete_adapter_obj(pao);
hpi_delete_adapter(pao);
phr->error = 0;
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index 22e9f08dea6d..9d5df54a6b46 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -152,8 +152,8 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
@@ -223,15 +223,13 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index);
/*****************************************************************************/
-static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
+static void subsys_message(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -279,6 +277,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
+
default:
hw_message(pao, phm, phr);
break;
@@ -371,36 +373,17 @@ static void instream_message(struct hpi_adapter_obj *pao,
/** Entry point to this HPI backend
* All calls to the HPI start here
*/
-void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
+ struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- /* subsytem messages are processed by every HPI.
- * All other messages are ignored unless the adapter index matches
- * an adapter in the HPI
- */
- /* HPI_DEBUG_LOG(DEBUG, "HPI Obj=%d, Func=%d\n", phm->wObject,
- phm->wFunction); */
-
- /* if Dsp has crashed then do not communicate with it any more */
- if (phm->object != HPI_OBJ_SUBSYSTEM) {
- pao = hpi_find_adapter(phm->adapter_index);
- if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
- return;
- }
-
- if ((pao->dsp_crashed >= 10)
- && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
- /* allow last resort debug read even after crash */
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
- return;
- }
+ if (pao && (pao->dsp_crashed >= 10)
+ && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
+ /* allow last resort debug read even after crash */
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_DSP_HARDWARE);
+ HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", phm->object,
+ phm->function);
+ return;
}
/* Init default response */
@@ -412,7 +395,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
- subsys_message(phm, phr);
+ subsys_message(pao, phm, phr);
break;
case HPI_OBJ_ADAPTER:
@@ -444,6 +427,26 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
}
}
+void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_adapter_obj *pao = NULL;
+
+ if (phm->object != HPI_OBJ_SUBSYSTEM) {
+ /* normal messages must have valid adapter index */
+ pao = hpi_find_adapter(phm->adapter_index);
+ } else {
+ /* subsys messages don't address an adapter */
+ _HPI_6205(NULL, phm, phr);
+ return;
+ }
+
+ if (pao)
+ _HPI_6205(pao, phm, phr);
+ else
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+}
+
/*****************************************************************************/
/* SUBSYSTEM */
@@ -491,13 +494,11 @@ static void subsys_create_adapter(struct hpi_message *phm,
}
/** delete an adapter - required by WDM driver */
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao;
struct hpi_hw_obj *phw;
- pao = hpi_find_adapter(phm->obj_index);
if (!pao) {
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
return;
@@ -563,11 +564,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
}
err = adapter_boot_load_dsp(pao, pos_error_code);
- if (err)
+ if (err) {
+ HPI_DEBUG_LOG(ERROR, "DSP code load failed\n");
/* no need to clean up as SubSysCreateAdapter */
/* calls DeleteAdapter on error. */
return err;
-
+ }
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
/* allow boot load even if mem alloc wont work */
@@ -604,6 +606,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
control_cache.number_of_controls,
interface->control_cache.size_in_bytes,
p_control_cache_virtual);
+
if (!phw->p_cache)
err = HPI_ERROR_MEMORY_ALLOC;
}
@@ -675,16 +678,14 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
}
/** Free memory areas allocated by adapter
- * this routine is called from SubSysDeleteAdapter,
+ * this routine is called from AdapterDelete,
* and SubSysCreateAdapter if duplicate index
*/
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
{
- struct hpi_hw_obj *phw;
+ struct hpi_hw_obj *phw = pao->priv;
int i;
- phw = pao->priv;
-
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
hpios_locked_mem_free(&phw->h_control_cache);
hpi_free_control_cache(phw->p_cache);
@@ -1275,6 +1276,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
case HPI_ADAPTER_FAMILY_ASI(0x6300):
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
break;
+ case HPI_ADAPTER_FAMILY_ASI(0x5500):
case HPI_ADAPTER_FAMILY_ASI(0x5600):
case HPI_ADAPTER_FAMILY_ASI(0x6500):
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
@@ -2059,7 +2061,6 @@ static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us)
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
{
struct bus_master_interface *interface = phw->p_interface_buffer;
-
u32 r;
interface->host_cmd = cmd;
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index 3b9fd115da36..bf5eced76bac 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -294,7 +294,7 @@ enum HPI_CONTROL_ATTRIBUTES {
/* These defines are used to fill in protocol information for an Ethernet packet
sent using HMI on CS18102 */
-/** ID supplied by Cirrius for ASI packets. */
+/** ID supplied by Cirrus for ASI packets. */
#define HPI_ETHERNET_PACKET_ID 0x85
/** Simple packet - no special routing required */
#define HPI_ETHERNET_PACKET_V1 0x01
@@ -307,7 +307,7 @@ enum HPI_CONTROL_ATTRIBUTES {
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
-#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */
+#define HPI_ETHERNET_UDP_PORT 44600 /**< HPI UDP service */
/** Default network timeout in milli-seconds. */
#define HPI_ETHERNET_TIMEOUT_MS 500
@@ -397,14 +397,14 @@ enum HPI_FUNCTION_IDS {
HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3),
- HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4),
+ /* HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4), */
HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5),
HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6),
- HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7),
+ /* HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7), */
HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8),
HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
- HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10),
- HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11),
+ /* HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), */
+ /* HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), */
HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14),
@@ -433,7 +433,8 @@ enum HPI_FUNCTION_IDS {
HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18),
HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20),
-#define HPI_ADAPTER_FUNCTION_COUNT 20
+ HPI_ADAPTER_DELETE = HPI_FUNC_ID(ADAPTER, 21),
+#define HPI_ADAPTER_FUNCTION_COUNT 21
HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
@@ -1561,8 +1562,6 @@ void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
u16 *pw_adapter_index);
-u16 hpi_subsys_delete_adapter(u16 adapter_index);
-
u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
@@ -1584,9 +1583,7 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR);
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for individual HPI entry points */
-hpi_handler_func HPI_1000;
hpi_handler_func HPI_6000;
hpi_handler_func HPI_6205;
-hpi_handler_func HPI_COMMON;
#endif /* _HPI_INTERNAL_H_ */
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index 3e9c5c289764..b15a02e91f82 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -227,8 +227,9 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
if (info->control_type) {
pC->p_info[info->control_index] = info;
cached++;
- } else /* dummy cache entry */
+ } else { /* dummy cache entry */
pC->p_info[info->control_index] = NULL;
+ }
byte_count += info->size_in32bit_words * 4;
@@ -298,7 +299,7 @@ struct pad_ofs_size {
unsigned int field_size;
};
-static struct pad_ofs_size pad_desc[] = {
+static const struct pad_ofs_size pad_desc[] = {
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
@@ -617,6 +618,10 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
}
}
+/** Allocate control cache.
+
+\return Cache pointer, or NULL if allocation fails.
+*/
struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
const u32 size_in_bytes, u8 *p_dsp_control_buffer)
{
@@ -667,7 +672,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
phr->u.s.num_adapters = adapters.gw_num_adapters;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h
index 590f0b69e655..d53cdf6e535f 100644
--- a/sound/pci/asihpi/hpicmn.h
+++ b/sound/pci/asihpi/hpicmn.h
@@ -60,3 +60,5 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
+
+hpi_handler_func HPI_COMMON;
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index c38fc9487560..7397b169b89f 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -105,33 +105,6 @@ u16 hpi_subsys_get_version_ex(u32 *pversion_ex)
return hr.error;
}
-u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
- u16 *pw_adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_CREATE_ADAPTER);
- hm.u.s.resource = *p_resource;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_adapter_index = hr.u.s.adapter_index;
- return hr.error;
-}
-
-u16 hpi_subsys_delete_adapter(u16 adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.obj_index = adapter_index;
- hpi_send_recv(&hm, &hr);
- return hr.error;
-}
-
u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
{
struct hpi_message hm;
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index 360028b9abf5..7352a5f7b4f7 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -211,24 +211,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
HPIMSGX__init(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- HPIMSGX__cleanup(phm->obj_index, h_owner);
- {
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_CLOSE);
- hm.adapter_index = phm->obj_index;
- hw_entry_point(&hm, &hr);
- }
- if ((phm->obj_index < HPI_MAX_ADAPTERS)
- && hpi_entry_points[phm->obj_index]) {
- hpi_entry_points[phm->obj_index] (phm, phr);
- hpi_entry_points[phm->obj_index] = NULL;
- } else
- phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
-
- break;
default:
/* Must explicitly handle every subsys message in this switch */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
@@ -247,6 +229,19 @@ static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ HPIMSGX__cleanup(phm->adapter_index, h_owner);
+ {
+ struct hpi_message hm;
+ struct hpi_response hr;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_CLOSE);
+ hm.adapter_index = phm->adapter_index;
+ hw_entry_point(&hm, &hr);
+ }
+ hw_entry_point(phm, phr);
+ break;
+
default:
hw_entry_point(phm, phr);
break;
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index cd624f13ff8e..d8e7047512f8 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -25,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
+#include "hpicmn.h"
#include <linux/fs.h>
#include <linux/slab.h>
@@ -161,26 +162,24 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
- pa = &adapters[hm->h.adapter_index];
+ switch (hm->h.function) {
+ case HPI_SUBSYS_CREATE_ADAPTER:
+ case HPI_ADAPTER_DELETE:
+ /* Application must not use these functions! */
+ hr->h.size = sizeof(hr->h);
+ hr->h.error = HPI_ERROR_INVALID_OPERATION;
+ hr->h.function = hm->h.function;
+ uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
+ if (uncopied_bytes)
+ err = -EFAULT;
+ else
+ err = 0;
+ goto out;
+ }
+
hr->h.size = res_max_size;
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
- switch (hm->h.function) {
- case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
- /* Application must not use these functions! */
- hr->h.size = sizeof(hr->h);
- hr->h.error = HPI_ERROR_INVALID_OPERATION;
- hr->h.function = hm->h.function;
- uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
- if (uncopied_bytes)
- err = -EFAULT;
- else
- err = 0;
- goto out;
-
- default:
- hpi_send_recv_f(&hm->m0, &hr->r0, file);
- }
+ hpi_send_recv_f(&hm->m0, &hr->r0, file);
} else {
u16 __user *ptr = NULL;
u32 size = 0;
@@ -188,8 +187,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
u32 adapter = hm->h.adapter_index;
+ pa = &adapters[adapter];
- if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
+ if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) {
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN,
HPI_ERROR_BAD_ADAPTER_NUMBER);
@@ -317,7 +317,7 @@ out:
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
- int err, idx, nm;
+ int idx, nm;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
@@ -351,11 +351,8 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
- HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
- idx, pci_dev->resource[idx].name,
- (unsigned long long)pci_resource_start(pci_dev, idx),
- (unsigned long long)pci_resource_end(pci_dev, idx),
- (unsigned long long)pci_resource_flags(pci_dev, idx));
+ HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
+ &pci_dev->resource[idx]);
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
@@ -395,17 +392,20 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
adapter.index = hr.u.s.adapter_index;
adapter.type = hr.u.s.adapter_type;
+
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_OPEN);
hm.adapter_index = adapter.index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
- err = hpi_adapter_open(adapter.index);
- if (err)
+ if (hr.error)
goto err;
adapter.snd_card_asihpi = NULL;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
- adapters[hr.u.s.adapter_index] = adapter;
+ adapters[adapter.index] = adapter;
mutex_init(&adapters[adapter.index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
@@ -440,10 +440,9 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
struct hpi_adapter *pa;
pa = pci_get_drvdata(pci_dev);
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.obj_index = pa->index;
- hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_DELETE);
+ hm.adapter_index = pa->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h
index 5d69c31fe3f4..79fbee3845eb 100644
--- a/sound/pci/au88x0/au8810.h
+++ b/sound/pci/au88x0/au8810.h
@@ -4,7 +4,7 @@
#define CHIP_AU8810
-#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME "Aureal Advantage"
#define CARD_NAME_SHORT "au8810"
#define NR_ADB 0x10
diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h
index abbe85e4f7a9..cafdb9668a34 100644
--- a/sound/pci/au88x0/au8820.h
+++ b/sound/pci/au88x0/au8820.h
@@ -11,7 +11,7 @@
#define CHIP_AU8820
-#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex"
#define CARD_NAME_SHORT "au8820"
/* Number of ADB and WT channels */
diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h
index 04ece1b1c218..999b29ab34ad 100644
--- a/sound/pci/au88x0/au8830.h
+++ b/sound/pci/au88x0/au8830.h
@@ -11,7 +11,7 @@
#define CHIP_AU8830
-#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex 2"
#define CARD_NAME_SHORT "au8830"
#define NR_ADB 0x20
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index 62e959120c44..c5f7ae46afef 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -426,11 +426,11 @@ static struct snd_pcm_ops snd_vortex_playback_ops = {
*/
static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
- "AU88x0 ADB",
- "AU88x0 SPDIF",
- "AU88x0 A3D",
- "AU88x0 WT",
- "AU88x0 I2S",
+ CARD_NAME " ADB",
+ CARD_NAME " SPDIF",
+ CARD_NAME " A3D",
+ CARD_NAME " WT",
+ CARD_NAME " I2S",
};
static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
"adb",
@@ -527,7 +527,8 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
nr_capt, &pcm);
if (err < 0)
return err;
- strcpy(pcm->name, vortex_pcm_name[idx]);
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
chip->pcm[idx] = pcm;
// This is an evil hack, but it saves a lot of duplicated code.
VORTEX_PCM_TYPE(pcm) = idx;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 7a9401462c1c..dae4050ede5c 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -303,6 +303,9 @@ static const u32 db_table[101] = {
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
+/* EMU10K1 bass/treble db gain */
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);
+
static const u32 onoff_table[2] = {
0x00000000, 0x00000001
};
@@ -2163,6 +2166,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
ctl = &controls[i + 1];
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -2172,6 +2176,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
#define BASS_GPR 0x8c
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 05afe06e353a..9d890a5aec5a 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1729,8 +1729,6 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Master Mono Playback Volume",
"PCM Out Path & Mute",
"Mono Output Select",
- "Front Playback Switch",
- "Front Playback Volume",
"Surround Playback Switch",
"Surround Playback Volume",
"Center Playback Switch",
@@ -1879,6 +1877,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
emu->rear_ac97 = 1;
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
+ remove_ctl(card,"Front Playback Volume");
+ remove_ctl(card,"Front Playback Switch");
}
/* remove unused AC97 controls */
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
@@ -1913,6 +1913,12 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
for (; *c; c += 2)
rename_ctl(card, c[0], c[1]);
+ if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
+ remove_ctl(card, "Center Playback Volume");
+ remove_ctl(card, "LFE Playback Volume");
+ remove_ctl(card, "Wave Center Playback Volume");
+ remove_ctl(card, "Wave LFE Playback Volume");
+ }
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 7c17f45d876d..ab0a6156a704 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -112,6 +112,10 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
+#ifdef CONFIG_SND_ES1968_RADIO
+#include <sound/tea575x-tuner.h>
+#endif
+
#define CARD_NAME "ESS Maestro1/2"
#define DRIVER_NAME "ES1968"
@@ -553,6 +557,10 @@ struct es1968 {
spinlock_t ac97_lock;
struct tasklet_struct hwvol_tq;
#endif
+
+#ifdef CONFIG_SND_ES1968_RADIO
+ struct snd_tea575x tea;
+#endif
};
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
@@ -2571,6 +2579,63 @@ static int __devinit snd_es1968_input_register(struct es1968 *chip)
}
#endif /* CONFIG_SND_ES1968_INPUT */
+#ifdef CONFIG_SND_ES1968_RADIO
+#define GPIO_DATA 0x60
+#define IO_MASK 4 /* mask register offset from GPIO_DATA
+ bits 1=unmask write to given bit */
+#define IO_DIR 8 /* direction register offset from GPIO_DATA
+ bits 0/1=read/write direction */
+/* mask bits for GPIO lines */
+#define STR_DATA 0x0040 /* GPIO6 */
+#define STR_CLK 0x0080 /* GPIO7 */
+#define STR_WREN 0x0100 /* GPIO8 */
+#define STR_MOST 0x0200 /* GPIO9 */
+
+static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 val = 0;
+
+ val |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+ val |= (pins & TEA575X_CLK) ? STR_CLK : 0;
+ val |= (pins & TEA575X_WREN) ? STR_WREN : 0;
+
+ outw(val, io);
+}
+
+static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 val = inw(io);
+
+ return (val & STR_DATA) ? TEA575X_DATA : 0 |
+ (val & STR_MOST) ? TEA575X_MOST : 0;
+}
+
+static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 odir = inw(io + IO_DIR);
+
+ if (output) {
+ outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+ outw(odir | STR_DATA | STR_CLK | STR_WREN, io + IO_DIR);
+ } else {
+ outw(~(STR_CLK | STR_WREN | STR_DATA | STR_MOST), io + IO_MASK);
+ outw((odir & ~(STR_DATA | STR_MOST)) | STR_CLK | STR_WREN, io + IO_DIR);
+ }
+}
+
+static struct snd_tea575x_ops snd_es1968_tea_ops = {
+ .set_pins = snd_es1968_tea575x_set_pins,
+ .get_pins = snd_es1968_tea575x_get_pins,
+ .set_direction = snd_es1968_tea575x_set_direction,
+};
+#endif
+
static int snd_es1968_free(struct es1968 *chip)
{
#ifdef CONFIG_SND_ES1968_INPUT
@@ -2585,6 +2650,10 @@ static int snd_es1968_free(struct es1968 *chip)
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
}
+#ifdef CONFIG_SND_ES1968_RADIO
+ snd_tea575x_exit(&chip->tea);
+#endif
+
if (chip->irq >= 0)
free_irq(chip->irq, chip);
snd_es1968_free_gameport(chip);
@@ -2723,6 +2792,15 @@ static int __devinit snd_es1968_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
+#ifdef CONFIG_SND_ES1968_RADIO
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_es1968_tea_ops;
+ strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+ if (!snd_tea575x_init(&chip->tea))
+ printk(KERN_INFO "es1968: detected TEA575x radio\n");
+#endif
+
*chip_ret = chip;
return 0;
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index e1baad74ea4b..eacd4901a308 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -38,7 +38,6 @@
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <sound/tea575x-tuner.h>
-#define TEA575X_RADIO 1
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -53,7 +52,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
/*
* Enable TEA575x tuner
* 1 = MediaForte 256-PCS
- * 2 = MediaForte 256-PCPR
+ * 2 = MediaForte 256-PCP
* 3 = MediaForte 64-PCR
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
* High 16-bits are video (radio) device number + 1
@@ -67,7 +66,7 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
#define TUNER_ONLY (1<<4)
#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
@@ -196,7 +195,7 @@ struct fm801 {
spinlock_t reg_lock;
struct snd_info_entry *proc_entry;
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
struct snd_tea575x tea;
#endif
@@ -715,310 +714,89 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc
* TEA5757 radio
*/
-#ifdef TEA575X_RADIO
-
-/* 256PCS GPIO numbers */
-#define TEA_256PCS_DATA 1
-#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
-#define TEA_256PCS_BUS_CLOCK 3
-
-static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
+/* GPIO to TEA575x maps */
+struct snd_fm801_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
+static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
+ { .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" },
+ { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" },
+ { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
+};
-static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea)
+static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
- val |= 1;
- }
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_unlock_irq(&chip->reg_lock);
+ reg &= ~(FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
- return val;
-}
+ reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0;
+ reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0;
+ /* WRITE_ENABLE is inverted */
+ reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
-/* 256PCPR GPIO numbers */
-#define TEA_256PCPR_BUS_CLOCK 0
-#define TEA_256PCPR_DATA 1
-#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */
-
-static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
-
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
}
-static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea)
+static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
- val |= 1;
- }
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
+ return (reg & FM801_GPIO_GP(gpio.data)) ? TEA575X_DATA : 0 |
+ (reg & FM801_GPIO_GP(gpio.most)) ? TEA575X_MOST : 0;
}
-/* 64PCR GPIO numbers */
-#define TEA_64PCR_BUS_CLOCK 0
-#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */
-#define TEA_64PCR_DATA 2
-
-static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val)
+static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
/* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
+ reg |= FM801_GPIO_GS(gpio.data) |
+ FM801_GPIO_GS(gpio.wren) |
+ FM801_GPIO_GS(gpio.clk) |
+ FM801_GPIO_GS(gpio.most);
+ if (output) {
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
+ } else {
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.most) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.most) |
+ FM801_GPIO_GP(gpio.wren);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.clk));
}
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_64PCR_DATA);
outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
- val |= 1;
- }
-
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
}
-static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
- unsigned int mute)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
-
- spin_lock_irq(&chip->reg_lock);
-
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- if (mute)
- /* 0xf800 (mute) */
- reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- else
- /* 0xf802 (unmute) */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
- {
- /* 1 = MediaForte 256-PCS */
- .write = snd_fm801_tea575x_256pcs_write,
- .read = snd_fm801_tea575x_256pcs_read,
- },
- {
- /* 2 = MediaForte 256-PCPR */
- .write = snd_fm801_tea575x_256pcpr_write,
- .read = snd_fm801_tea575x_256pcpr_read,
- },
- {
- /* 3 = MediaForte 64-PCR */
- .write = snd_fm801_tea575x_64pcr_write,
- .read = snd_fm801_tea575x_64pcr_read,
- .mute = snd_fm801_tea575x_64pcr_mute,
- }
+static struct snd_tea575x_ops snd_fm801_tea_ops = {
+ .set_pins = snd_fm801_tea575x_set_pins,
+ .get_pins = snd_fm801_tea575x_get_pins,
+ .set_direction = snd_fm801_tea575x_set_direction,
};
#endif
@@ -1371,7 +1149,7 @@ static int snd_fm801_free(struct fm801 *chip)
outw(cmdw, FM801_REG(chip, IRQ_MASK));
__end_hw:
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
snd_tea575x_exit(&chip->tea);
#endif
if (chip->irq >= 0)
@@ -1450,16 +1228,25 @@ static int __devinit snd_fm801_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_fm801_tea_ops;
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
- chip->tea.dev_nr = tea575x_tuner >> 16;
- chip->tea.card = card;
- chip->tea.freq_fixup = 10700;
- chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
- snd_tea575x_init(&chip->tea);
- }
+ if (snd_tea575x_init(&chip->tea))
+ snd_printk(KERN_ERR "TEA575x radio not found\n");
+ } else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0)
+ /* autodetect tuner connection */
+ for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
+ chip->tea575x_tuner = tea575x_tuner;
+ if (!snd_tea575x_init(&chip->tea)) {
+ snd_printk(KERN_INFO "detected TEA575x radio type %s\n",
+ snd_fm801_tea575x_gpios[tea575x_tuner - 1].name);
+ break;
+ }
+ }
+ strlcpy(chip->tea.card, snd_fm801_tea575x_gpios[(tea575x_tuner & TUNER_TYPE_MASK) - 1].name, sizeof(chip->tea.card));
#endif
*rchip = chip;
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 27709f0cd2a6..f3353b49c785 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -235,8 +235,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
+ { PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */
#if 0
- { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
#endif
{ 0, }
@@ -1261,9 +1261,9 @@ static struct shortname_table {
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
+ { 0x746e, "AMD AMD8111" },
#if 0
{ 0x5455, "ALi M5455" },
- { 0x746d, "AMD AMD8111" },
#endif
{ 0 },
};
diff --git a/sound/pci/lola/Makefile b/sound/pci/lola/Makefile
new file mode 100644
index 000000000000..8178a2a59d00
--- /dev/null
+++ b/sound/pci/lola/Makefile
@@ -0,0 +1,4 @@
+snd-lola-y := lola.o lola_pcm.o lola_clock.o lola_mixer.o
+snd-lola-$(CONFIG_SND_DEBUG) += lola_proc.o
+
+obj-$(CONFIG_SND_LOLA) += snd-lola.o
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
new file mode 100644
index 000000000000..34b24286d279
--- /dev/null
+++ b/sound/pci/lola/lola.c
@@ -0,0 +1,791 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include "lola.h"
+
+/* Standard options */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram Lola driver.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram Lola driver.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram Lola driver.");
+
+/* Lola-specific options */
+
+/* for instance use always max granularity which is compatible
+ * with all sample rates
+ */
+static int granularity[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1)] = LOLA_GRANULARITY_MAX
+};
+
+/* below a sample_rate of 16kHz the analogue audio quality is NOT excellent */
+static int sample_rate_min[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1) ] = 16000
+};
+
+module_param_array(granularity, int, NULL, 0444);
+MODULE_PARM_DESC(granularity, "Granularity value");
+module_param_array(sample_rate_min, int, NULL, 0444);
+MODULE_PARM_DESC(sample_rate_min, "Minimal sample rate");
+
+/*
+ */
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Digigram, Lola}}");
+MODULE_DESCRIPTION("Digigram Lola driver");
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int debug;
+module_param(debug, int, 0644);
+#define verbose_debug(fmt, args...) \
+ do { if (debug > 1) printk(KERN_DEBUG SFX fmt, ##args); } while (0)
+#else
+#define verbose_debug(fmt, args...)
+#endif
+
+/*
+ * pseudo-codec read/write via CORB/RIRB
+ */
+
+static int corb_send_verb(struct lola *chip, unsigned int nid,
+ unsigned int verb, unsigned int data,
+ unsigned int extdata)
+{
+ unsigned long flags;
+ int ret = -EIO;
+
+ chip->last_cmd_nid = nid;
+ chip->last_verb = verb;
+ chip->last_data = data;
+ chip->last_extdata = extdata;
+ data |= (nid << 20) | (verb << 8);
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) {
+ unsigned int wp = chip->corb.wp + 1;
+ wp %= LOLA_CORB_ENTRIES;
+ chip->corb.wp = wp;
+ chip->corb.buf[wp * 2] = cpu_to_le32(data);
+ chip->corb.buf[wp * 2 + 1] = cpu_to_le32(extdata);
+ lola_writew(chip, BAR0, CORBWP, wp);
+ chip->rirb.cmds++;
+ smp_wmb();
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return ret;
+}
+
+static void lola_queue_unsol_event(struct lola *chip, unsigned int res,
+ unsigned int res_ex)
+{
+ lola_update_ext_clock_freq(chip, res);
+}
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void lola_update_rirb(struct lola *chip)
+{
+ unsigned int rp, wp;
+ u32 res, res_ex;
+
+ wp = lola_readw(chip, BAR0, RIRBWP);
+ if (wp == chip->rirb.wp)
+ return;
+ chip->rirb.wp = wp;
+
+ while (chip->rirb.rp != wp) {
+ chip->rirb.rp++;
+ chip->rirb.rp %= LOLA_CORB_ENTRIES;
+
+ rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+ res = le32_to_cpu(chip->rirb.buf[rp]);
+ if (res_ex & LOLA_RIRB_EX_UNSOL_EV)
+ lola_queue_unsol_event(chip, res, res_ex);
+ else if (chip->rirb.cmds) {
+ chip->res = res;
+ chip->res_ex = res_ex;
+ smp_wmb();
+ chip->rirb.cmds--;
+ }
+ }
+}
+
+static int rirb_get_response(struct lola *chip, unsigned int *val,
+ unsigned int *extval)
+{
+ unsigned long timeout;
+
+ again:
+ timeout = jiffies + msecs_to_jiffies(1000);
+ for (;;) {
+ if (chip->polling_mode) {
+ spin_lock_irq(&chip->reg_lock);
+ lola_update_rirb(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ if (!chip->rirb.cmds) {
+ *val = chip->res;
+ if (extval)
+ *extval = chip->res_ex;
+ verbose_debug("get_response: %x, %x\n",
+ chip->res, chip->res_ex);
+ if (chip->res_ex & LOLA_RIRB_EX_ERROR) {
+ printk(KERN_WARNING SFX "RIRB ERROR: "
+ "NID=%x, verb=%x, data=%x, ext=%x\n",
+ chip->last_cmd_nid,
+ chip->last_verb, chip->last_data,
+ chip->last_extdata);
+ return -EIO;
+ }
+ return 0;
+ }
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(20);
+ cond_resched();
+ }
+ printk(KERN_WARNING SFX "RIRB response error\n");
+ if (!chip->polling_mode) {
+ printk(KERN_WARNING SFX "switching to polling mode\n");
+ chip->polling_mode = 1;
+ goto again;
+ }
+ return -EIO;
+}
+
+/* aynchronous write of a codec verb with data */
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata)
+{
+ verbose_debug("codec_write NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ return corb_send_verb(chip, nid, verb, data, extdata);
+}
+
+/* write a codec verb with data and read the returned status */
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval)
+{
+ int err;
+
+ verbose_debug("codec_read NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ err = corb_send_verb(chip, nid, verb, data, extdata);
+ if (err < 0)
+ return err;
+ err = rirb_get_response(chip, val, extval);
+ return err;
+}
+
+/* flush all pending codec writes */
+int lola_codec_flush(struct lola *chip)
+{
+ unsigned int tmp;
+ return rirb_get_response(chip, &tmp, NULL);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t lola_interrupt(int irq, void *dev_id)
+{
+ struct lola *chip = dev_id;
+ unsigned int notify_ins, notify_outs, error_ins, error_outs;
+ int handled = 0;
+ int i;
+
+ notify_ins = notify_outs = error_ins = error_outs = 0;
+ spin_lock(&chip->reg_lock);
+ for (;;) {
+ unsigned int status, in_sts, out_sts;
+ unsigned int reg;
+
+ status = lola_readl(chip, BAR1, DINTSTS);
+ if (!status || status == -1)
+ break;
+
+ in_sts = lola_readl(chip, BAR1, DIINTSTS);
+ out_sts = lola_readl(chip, BAR1, DOINTSTS);
+
+ /* clear Input Interrupts */
+ for (i = 0; in_sts && i < chip->pcm[CAPT].num_streams; i++) {
+ if (!(in_sts & (1 << i)))
+ continue;
+ in_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_ins |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_ins |= (1 << i);
+ /* clear */
+ lola_dsd_write(chip, i, STS, reg);
+ }
+
+ /* clear Output Interrupts */
+ for (i = 0; out_sts && i < chip->pcm[PLAY].num_streams; i++) {
+ if (!(out_sts & (1 << i)))
+ continue;
+ out_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i + MAX_STREAM_IN_COUNT, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_outs |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_outs |= (1 << i);
+ lola_dsd_write(chip, i + MAX_STREAM_IN_COUNT, STS, reg);
+ }
+
+ if (status & LOLA_DINT_CTRL) {
+ unsigned char rbsts; /* ring status is byte access */
+ rbsts = lola_readb(chip, BAR0, RIRBSTS);
+ rbsts &= LOLA_RIRB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, RIRBSTS, rbsts);
+ rbsts = lola_readb(chip, BAR0, CORBSTS);
+ rbsts &= LOLA_CORB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, CORBSTS, rbsts);
+
+ lola_update_rirb(chip);
+ }
+
+ if (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)) {
+ /* clear global fifo error interrupt */
+ lola_writel(chip, BAR1, DINTSTS,
+ (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)));
+ }
+ handled = 1;
+ }
+ spin_unlock(&chip->reg_lock);
+
+ lola_pcm_update(chip, &chip->pcm[CAPT], notify_ins);
+ lola_pcm_update(chip, &chip->pcm[PLAY], notify_outs);
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * controller
+ */
+static int reset_controller(struct lola *chip)
+{
+ unsigned int gctl = lola_readl(chip, BAR0, GCTL);
+ unsigned long end_time;
+
+ if (gctl) {
+ /* to be sure */
+ lola_writel(chip, BAR1, BOARD_MODE, 0);
+ return 0;
+ }
+
+ chip->cold_reset = 1;
+ lola_writel(chip, BAR0, GCTL, LOLA_GCTL_RESET);
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ msleep(1);
+ gctl = lola_readl(chip, BAR0, GCTL);
+ if (gctl)
+ break;
+ } while (time_before(jiffies, end_time));
+ if (!gctl) {
+ printk(KERN_ERR SFX "cannot reset controller\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void lola_irq_enable(struct lola *chip)
+{
+ unsigned int val;
+
+ /* enalbe all I/O streams */
+ val = (1 << chip->pcm[PLAY].num_streams) - 1;
+ lola_writel(chip, BAR1, DOINTCTL, val);
+ val = (1 << chip->pcm[CAPT].num_streams) - 1;
+ lola_writel(chip, BAR1, DIINTCTL, val);
+
+ /* enable global irqs */
+ val = LOLA_DINT_GLOBAL | LOLA_DINT_CTRL | LOLA_DINT_FIFOERR |
+ LOLA_DINT_MUERR;
+ lola_writel(chip, BAR1, DINTCTL, val);
+}
+
+static void lola_irq_disable(struct lola *chip)
+{
+ lola_writel(chip, BAR1, DINTCTL, 0);
+ lola_writel(chip, BAR1, DIINTCTL, 0);
+ lola_writel(chip, BAR1, DOINTCTL, 0);
+}
+
+static int setup_corb_rirb(struct lola *chip)
+{
+ int err;
+ unsigned char tmp;
+ unsigned long end_time;
+
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->rb);
+ if (err < 0)
+ return err;
+
+ chip->corb.addr = chip->rb.addr;
+ chip->corb.buf = (u32 *)chip->rb.area;
+ chip->rirb.addr = chip->rb.addr + 2048;
+ chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ if (!lola_readb(chip, BAR0, RIRBCTL) &&
+ !lola_readb(chip, BAR0, CORBCTL))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, end_time));
+
+ /* CORB set up */
+ lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
+ lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
+ /* set the corb size to 256 entries */
+ lola_writeb(chip, BAR0, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ lola_writew(chip, BAR0, CORBWP, 0);
+ /* reset the corb hw read pointer */
+ lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
+ /* enable corb dma */
+ lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, CORBSTS, tmp);
+ chip->corb.wp = 0;
+
+ /* RIRB set up */
+ lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
+ lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
+ /* set the rirb size to 256 entries */
+ lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
+ /* set N=1, get RIRB response interrupt for new entry */
+ lola_writew(chip, BAR0, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, RIRBSTS, tmp);
+ chip->rirb.rp = chip->rirb.cmds = 0;
+
+ return 0;
+}
+
+static void stop_corb_rirb(struct lola *chip)
+{
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+}
+
+static void lola_reset_setups(struct lola *chip)
+{
+ /* update the granularity */
+ lola_set_granularity(chip, chip->granularity, true);
+ /* update the sample clock */
+ lola_set_clock_index(chip, chip->clock.cur_index);
+ /* enable unsolicited events of the clock widget */
+ lola_enable_clock_events(chip);
+ /* update the analog gains */
+ lola_setup_all_analog_gains(chip, CAPT, false); /* input, update */
+ /* update SRC configuration if applicable */
+ lola_set_src_config(chip, chip->input_src_mask, false);
+ /* update the analog outputs */
+ lola_setup_all_analog_gains(chip, PLAY, false); /* output, update */
+}
+
+static int lola_parse_tree(struct lola *chip)
+{
+ unsigned int val;
+ int nid, err;
+
+ err = lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read VENDOR_ID\n");
+ return err;
+ }
+ val >>= 16;
+ if (val != 0x1369) {
+ printk(KERN_ERR SFX "Unknown codec vendor 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read FUNCTION_TYPE for 0x%x\n", nid);
+ return err;
+ }
+ if (val != 1) {
+ printk(KERN_ERR SFX "Unknown function type %d\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read SPECCAPS\n");
+ return err;
+ }
+ chip->lola_caps = val;
+ chip->pin[CAPT].num_pins = LOLA_AFG_INPUT_PIN_COUNT(chip->lola_caps);
+ chip->pin[PLAY].num_pins = LOLA_AFG_OUTPUT_PIN_COUNT(chip->lola_caps);
+ snd_printdd(SFX "speccaps=0x%x, pins in=%d, out=%d\n",
+ chip->lola_caps,
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+
+ if (chip->pin[CAPT].num_pins > MAX_AUDIO_INOUT_COUNT ||
+ chip->pin[PLAY].num_pins > MAX_AUDIO_INOUT_COUNT) {
+ printk(KERN_ERR SFX "Invalid Lola-spec caps 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ nid = 0x02;
+ err = lola_init_pcm(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pcm(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ err = lola_init_pins(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pins(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_clock_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_mixer_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+
+ /* enable unsolicited events of the clock widget */
+ err = lola_enable_clock_events(chip);
+ if (err < 0)
+ return err;
+
+ /* if last ResetController was not a ColdReset, we don't know
+ * the state of the card; initialize here again
+ */
+ if (!chip->cold_reset) {
+ lola_reset_setups(chip);
+ chip->cold_reset = 1;
+ } else {
+ /* set the granularity if it is not the default */
+ if (chip->granularity != LOLA_GRANULARITY_MIN)
+ lola_set_granularity(chip, chip->granularity, true);
+ }
+
+ return 0;
+}
+
+static void lola_stop_hw(struct lola *chip)
+{
+ stop_corb_rirb(chip);
+ lola_irq_disable(chip);
+}
+
+static void lola_free(struct lola *chip)
+{
+ if (chip->initialized)
+ lola_stop_hw(chip);
+ lola_free_pcm(chip);
+ lola_free_mixer(chip);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->bar[0].remap_addr)
+ iounmap(chip->bar[0].remap_addr);
+ if (chip->bar[1].remap_addr)
+ iounmap(chip->bar[1].remap_addr);
+ if (chip->rb.area)
+ snd_dma_free_pages(&chip->rb);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+}
+
+static int lola_dev_free(struct snd_device *device)
+{
+ lola_free(device->device_data);
+ return 0;
+}
+
+static int __devinit lola_create(struct snd_card *card, struct pci_dev *pci,
+ int dev, struct lola **rchip)
+{
+ struct lola *chip;
+ int err;
+ unsigned int dever;
+ static struct snd_device_ops ops = {
+ .dev_free = lola_dev_free,
+ };
+
+ *rchip = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ snd_printk(KERN_ERR SFX "cannot allocate chip\n");
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ chip->granularity = granularity[dev];
+ switch (chip->granularity) {
+ case 8:
+ chip->sample_rate_max = 48000;
+ break;
+ case 16:
+ chip->sample_rate_max = 96000;
+ break;
+ case 32:
+ chip->sample_rate_max = 192000;
+ break;
+ default:
+ snd_printk(KERN_WARNING SFX
+ "Invalid granularity %d, reset to %d\n",
+ chip->granularity, LOLA_GRANULARITY_MAX);
+ chip->granularity = LOLA_GRANULARITY_MAX;
+ chip->sample_rate_max = 192000;
+ break;
+ }
+ chip->sample_rate_min = sample_rate_min[dev];
+ if (chip->sample_rate_min > chip->sample_rate_max) {
+ snd_printk(KERN_WARNING SFX
+ "Invalid sample_rate_min %d, reset to 16000\n",
+ chip->sample_rate_min);
+ chip->sample_rate_min = 16000;
+ }
+
+ err = pci_request_regions(pci, DRVNAME);
+ if (err < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ chip->bar[0].addr = pci_resource_start(pci, 0);
+ chip->bar[0].remap_addr = pci_ioremap_bar(pci, 0);
+ chip->bar[1].addr = pci_resource_start(pci, 2);
+ chip->bar[1].remap_addr = pci_ioremap_bar(pci, 2);
+ if (!chip->bar[0].remap_addr || !chip->bar[1].remap_addr) {
+ snd_printk(KERN_ERR SFX "ioremap error\n");
+ err = -ENXIO;
+ goto errout;
+ }
+
+ pci_set_master(pci);
+
+ err = reset_controller(chip);
+ if (err < 0)
+ goto errout;
+
+ if (request_irq(pci->irq, lola_interrupt, IRQF_SHARED,
+ DRVNAME, chip)) {
+ printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
+ err = -EBUSY;
+ goto errout;
+ }
+ chip->irq = pci->irq;
+ synchronize_irq(chip->irq);
+
+ dever = lola_readl(chip, BAR1, DEVER);
+ chip->pcm[CAPT].num_streams = (dever >> 0) & 0x3ff;
+ chip->pcm[PLAY].num_streams = (dever >> 10) & 0x3ff;
+ chip->version = (dever >> 24) & 0xff;
+ snd_printdd(SFX "streams in=%d, out=%d, version=0x%x\n",
+ chip->pcm[CAPT].num_streams, chip->pcm[PLAY].num_streams,
+ chip->version);
+
+ /* Test LOLA_BAR1_DEVER */
+ if (chip->pcm[CAPT].num_streams > MAX_STREAM_IN_COUNT ||
+ chip->pcm[PLAY].num_streams > MAX_STREAM_OUT_COUNT ||
+ (!chip->pcm[CAPT].num_streams &&
+ !chip->pcm[PLAY].num_streams)) {
+ printk(KERN_ERR SFX "invalid DEVER = %x\n", dever);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ err = setup_corb_rirb(chip);
+ if (err < 0)
+ goto errout;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
+ goto errout;
+ }
+
+ strcpy(card->driver, "Lola");
+ strlcpy(card->shortname, "Digigram Lola", sizeof(card->shortname));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, chip->bar[0].addr, chip->irq);
+ strcpy(card->mixername, card->shortname);
+
+ lola_irq_enable(chip);
+
+ chip->initialized = 1;
+ *rchip = chip;
+ return 0;
+
+ errout:
+ lola_free(chip);
+ return err;
+}
+
+static int __devinit lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ struct lola *chip;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "Error creating card!\n");
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ err = lola_create(card, pci, dev, &chip);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+
+ err = lola_parse_tree(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = lola_create_pcm(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = lola_create_mixer(chip);
+ if (err < 0)
+ goto out_free;
+
+ lola_proc_debug_new(chip);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto out_free;
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return err;
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static void __devexit lola_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static DEFINE_PCI_DEVICE_TABLE(lola_ids) = {
+ { PCI_VDEVICE(DIGIGRAM, 0x0001) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lola_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+ .name = DRVNAME,
+ .id_table = lola_ids,
+ .probe = lola_probe,
+ .remove = __devexit_p(lola_remove),
+};
+
+static int __init alsa_card_lola_init(void)
+{
+ return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_lola_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_lola_init)
+module_exit(alsa_card_lola_exit)
diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h
new file mode 100644
index 000000000000..d5708e29b16d
--- /dev/null
+++ b/sound/pci/lola/lola.h
@@ -0,0 +1,527 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LOLA_H
+#define _LOLA_H
+
+#define DRVNAME "snd-lola"
+#define SFX DRVNAME ": "
+
+/*
+ * Lola HD Audio Registers BAR0
+ */
+#define LOLA_BAR0_GCAP 0x00
+#define LOLA_BAR0_VMIN 0x02
+#define LOLA_BAR0_VMAJ 0x03
+#define LOLA_BAR0_OUTPAY 0x04
+#define LOLA_BAR0_INPAY 0x06
+#define LOLA_BAR0_GCTL 0x08
+#define LOLA_BAR0_WAKEEN 0x0c
+#define LOLA_BAR0_STATESTS 0x0e
+#define LOLA_BAR0_GSTS 0x10
+#define LOLA_BAR0_OUTSTRMPAY 0x18
+#define LOLA_BAR0_INSTRMPAY 0x1a
+#define LOLA_BAR0_INTCTL 0x20
+#define LOLA_BAR0_INTSTS 0x24
+#define LOLA_BAR0_WALCLK 0x30
+#define LOLA_BAR0_SSYNC 0x38
+
+#define LOLA_BAR0_CORBLBASE 0x40
+#define LOLA_BAR0_CORBUBASE 0x44
+#define LOLA_BAR0_CORBWP 0x48 /* no ULONG access */
+#define LOLA_BAR0_CORBRP 0x4a /* no ULONG access */
+#define LOLA_BAR0_CORBCTL 0x4c /* no ULONG access */
+#define LOLA_BAR0_CORBSTS 0x4d /* UCHAR access only */
+#define LOLA_BAR0_CORBSIZE 0x4e /* no ULONG access */
+
+#define LOLA_BAR0_RIRBLBASE 0x50
+#define LOLA_BAR0_RIRBUBASE 0x54
+#define LOLA_BAR0_RIRBWP 0x58
+#define LOLA_BAR0_RINTCNT 0x5a /* no ULONG access */
+#define LOLA_BAR0_RIRBCTL 0x5c
+#define LOLA_BAR0_RIRBSTS 0x5d /* UCHAR access only */
+#define LOLA_BAR0_RIRBSIZE 0x5e /* no ULONG access */
+
+#define LOLA_BAR0_ICW 0x60
+#define LOLA_BAR0_IRR 0x64
+#define LOLA_BAR0_ICS 0x68
+#define LOLA_BAR0_DPLBASE 0x70
+#define LOLA_BAR0_DPUBASE 0x74
+
+/* stream register offsets from stream base 0x80 */
+#define LOLA_BAR0_SD0_OFFSET 0x80
+#define LOLA_REG0_SD_CTL 0x00
+#define LOLA_REG0_SD_STS 0x03
+#define LOLA_REG0_SD_LPIB 0x04
+#define LOLA_REG0_SD_CBL 0x08
+#define LOLA_REG0_SD_LVI 0x0c
+#define LOLA_REG0_SD_FIFOW 0x0e
+#define LOLA_REG0_SD_FIFOSIZE 0x10
+#define LOLA_REG0_SD_FORMAT 0x12
+#define LOLA_REG0_SD_BDLPL 0x18
+#define LOLA_REG0_SD_BDLPU 0x1c
+
+/*
+ * Lola Digigram Registers BAR1
+ */
+#define LOLA_BAR1_FPGAVER 0x00
+#define LOLA_BAR1_DEVER 0x04
+#define LOLA_BAR1_UCBMV 0x08
+#define LOLA_BAR1_JTAG 0x0c
+#define LOLA_BAR1_UARTRX 0x10
+#define LOLA_BAR1_UARTTX 0x14
+#define LOLA_BAR1_UARTCR 0x18
+#define LOLA_BAR1_NVRAMVER 0x1c
+#define LOLA_BAR1_CTRLSPI 0x20
+#define LOLA_BAR1_DSPI 0x24
+#define LOLA_BAR1_AISPI 0x28
+#define LOLA_BAR1_GRAN 0x2c
+
+#define LOLA_BAR1_DINTCTL 0x80
+#define LOLA_BAR1_DIINTCTL 0x84
+#define LOLA_BAR1_DOINTCTL 0x88
+#define LOLA_BAR1_LRC 0x90
+#define LOLA_BAR1_DINTSTS 0x94
+#define LOLA_BAR1_DIINTSTS 0x98
+#define LOLA_BAR1_DOINTSTS 0x9c
+
+#define LOLA_BAR1_DSD0_OFFSET 0xa0
+#define LOLA_BAR1_DSD_SIZE 0x18
+
+#define LOLA_BAR1_DSDnSTS 0x00
+#define LOLA_BAR1_DSDnLPIB 0x04
+#define LOLA_BAR1_DSDnCTL 0x08
+#define LOLA_BAR1_DSDnLVI 0x0c
+#define LOLA_BAR1_DSDnBDPL 0x10
+#define LOLA_BAR1_DSDnBDPU 0x14
+
+#define LOLA_BAR1_SSYNC 0x03e8
+
+#define LOLA_BAR1_BOARD_CTRL 0x0f00
+#define LOLA_BAR1_BOARD_MODE 0x0f02
+
+#define LOLA_BAR1_SOURCE_GAIN_ENABLE 0x1000
+#define LOLA_BAR1_DEST00_MIX_GAIN_ENABLE 0x1004
+#define LOLA_BAR1_DEST31_MIX_GAIN_ENABLE 0x1080
+#define LOLA_BAR1_SOURCE00_01_GAIN 0x1084
+#define LOLA_BAR1_SOURCE30_31_GAIN 0x10c0
+#define LOLA_BAR1_SOURCE_GAIN(src) \
+ (LOLA_BAR1_SOURCE00_01_GAIN + (src) * 2)
+#define LOLA_BAR1_DEST00_MIX00_01_GAIN 0x10c4
+#define LOLA_BAR1_DEST00_MIX30_31_GAIN 0x1100
+#define LOLA_BAR1_DEST01_MIX00_01_GAIN 0x1104
+#define LOLA_BAR1_DEST01_MIX30_31_GAIN 0x1140
+#define LOLA_BAR1_DEST31_MIX00_01_GAIN 0x1884
+#define LOLA_BAR1_DEST31_MIX30_31_GAIN 0x18c0
+#define LOLA_BAR1_MIX_GAIN(dest, mix) \
+ (LOLA_BAR1_DEST00_MIX00_01_GAIN + (dest) * 0x40 + (mix) * 2)
+#define LOLA_BAR1_ANALOG_CLIP_IN 0x18c4
+#define LOLA_BAR1_PEAKMETERS_SOURCE00_01 0x18c8
+#define LOLA_BAR1_PEAKMETERS_SOURCE30_31 0x1904
+#define LOLA_BAR1_PEAKMETERS_SOURCE(src) \
+ (LOLA_BAR1_PEAKMETERS_SOURCE00_01 + (src) * 2)
+#define LOLA_BAR1_PEAKMETERS_DEST00_01 0x1908
+#define LOLA_BAR1_PEAKMETERS_DEST30_31 0x1944
+#define LOLA_BAR1_PEAKMETERS_DEST(dest) \
+ (LOLA_BAR1_PEAKMETERS_DEST00_01 + (dest) * 2)
+#define LOLA_BAR1_PEAKMETERS_AGC00_01 0x1948
+#define LOLA_BAR1_PEAKMETERS_AGC14_15 0x1964
+#define LOLA_BAR1_PEAKMETERS_AGC(x) \
+ (LOLA_BAR1_PEAKMETERS_AGC00_01 + (x) * 2)
+
+/* GCTL reset bit */
+#define LOLA_GCTL_RESET (1 << 0)
+/* GCTL unsolicited response enable bit */
+#define LOLA_GCTL_UREN (1 << 8)
+
+/* CORB/RIRB control, read/write pointer */
+#define LOLA_RBCTL_DMA_EN 0x02 /* enable DMA */
+#define LOLA_RBCTL_IRQ_EN 0x01 /* enable IRQ */
+#define LOLA_RBRWP_CLR 0x8000 /* read/write pointer clear */
+
+#define LOLA_RIRB_EX_UNSOL_EV 0x40000000
+#define LOLA_RIRB_EX_ERROR 0x80000000
+
+/* CORB int mask: CMEI[0] */
+#define LOLA_CORB_INT_CMEI 0x01
+#define LOLA_CORB_INT_MASK LOLA_CORB_INT_CMEI
+
+/* RIRB int mask: overrun[2], response[0] */
+#define LOLA_RIRB_INT_RESPONSE 0x01
+#define LOLA_RIRB_INT_OVERRUN 0x04
+#define LOLA_RIRB_INT_MASK (LOLA_RIRB_INT_RESPONSE | LOLA_RIRB_INT_OVERRUN)
+
+/* DINTCTL and DINTSTS */
+#define LOLA_DINT_GLOBAL 0x80000000 /* global interrupt enable bit */
+#define LOLA_DINT_CTRL 0x40000000 /* controller interrupt enable bit */
+#define LOLA_DINT_FIFOERR 0x20000000 /* global fifo error enable bit */
+#define LOLA_DINT_MUERR 0x10000000 /* global microcontroller underrun error */
+
+/* DSDnCTL bits */
+#define LOLA_DSD_CTL_SRST 0x01 /* stream reset bit */
+#define LOLA_DSD_CTL_SRUN 0x02 /* stream DMA start bit */
+#define LOLA_DSD_CTL_IOCE 0x04 /* interrupt on completion enable */
+#define LOLA_DSD_CTL_DEIE 0x10 /* descriptor error interrupt enable */
+#define LOLA_DSD_CTL_VLRCV 0x20 /* valid LRCountValue information in bits 8..31 */
+#define LOLA_LRC_MASK 0xffffff00
+
+/* DSDnSTS */
+#define LOLA_DSD_STS_BCIS 0x04 /* buffer completion interrupt status */
+#define LOLA_DSD_STS_DESE 0x10 /* descriptor error interrupt */
+#define LOLA_DSD_STS_FIFORDY 0x20 /* fifo ready */
+
+#define LOLA_CORB_ENTRIES 256
+
+#define MAX_STREAM_IN_COUNT 16
+#define MAX_STREAM_OUT_COUNT 16
+#define MAX_STREAM_COUNT 16
+#define MAX_PINS MAX_STREAM_COUNT
+#define MAX_STREAM_BUFFER_COUNT 16
+#define MAX_AUDIO_INOUT_COUNT 16
+
+#define LOLA_CLOCK_TYPE_INTERNAL 0
+#define LOLA_CLOCK_TYPE_AES 1
+#define LOLA_CLOCK_TYPE_AES_SYNC 2
+#define LOLA_CLOCK_TYPE_WORDCLOCK 3
+#define LOLA_CLOCK_TYPE_ETHERSOUND 4
+#define LOLA_CLOCK_TYPE_VIDEO 5
+
+#define LOLA_CLOCK_FORMAT_NONE 0
+#define LOLA_CLOCK_FORMAT_NTSC 1
+#define LOLA_CLOCK_FORMAT_PAL 2
+
+#define MAX_SAMPLE_CLOCK_COUNT 48
+
+/* parameters used with mixer widget's mixer capabilities */
+#define LOLA_PEAK_METER_CAN_AGC_MASK 1
+#define LOLA_PEAK_METER_CAN_ANALOG_CLIP_MASK 2
+
+struct lola_bar {
+ unsigned long addr;
+ void __iomem *remap_addr;
+};
+
+/* CORB/RIRB */
+struct lola_rb {
+ u32 *buf; /* CORB/RIRB buffer, 8 byte per each entry */
+ dma_addr_t addr; /* physical address of CORB/RIRB buffer */
+ unsigned short rp, wp; /* read/write pointers */
+ int cmds; /* number of pending requests */
+};
+
+/* Pin widget setup */
+struct lola_pin {
+ unsigned int nid;
+ bool is_analog;
+ unsigned int amp_mute;
+ unsigned int amp_step_size;
+ unsigned int amp_num_steps;
+ unsigned int amp_offset;
+ unsigned int max_level;
+ unsigned int config_default_reg;
+ unsigned int fixed_gain_list_len;
+ unsigned int cur_gain_step;
+};
+
+struct lola_pin_array {
+ unsigned int num_pins;
+ unsigned int num_analog_pins;
+ struct lola_pin pins[MAX_PINS];
+};
+
+/* Clock widget setup */
+struct lola_sample_clock {
+ unsigned int type;
+ unsigned int format;
+ unsigned int freq;
+};
+
+struct lola_clock_widget {
+ unsigned int nid;
+ unsigned int items;
+ unsigned int cur_index;
+ unsigned int cur_freq;
+ bool cur_valid;
+ struct lola_sample_clock sample_clock[MAX_SAMPLE_CLOCK_COUNT];
+ unsigned int idx_lookup[MAX_SAMPLE_CLOCK_COUNT];
+};
+
+#define LOLA_MIXER_DIM 32
+struct lola_mixer_array {
+ u32 src_gain_enable;
+ u32 dest_mix_gain_enable[LOLA_MIXER_DIM];
+ u16 src_gain[LOLA_MIXER_DIM];
+ u16 dest_mix_gain[LOLA_MIXER_DIM][LOLA_MIXER_DIM];
+};
+
+/* Mixer widget setup */
+struct lola_mixer_widget {
+ unsigned int nid;
+ unsigned int caps;
+ struct lola_mixer_array __user *array;
+ struct lola_mixer_array *array_saved;
+ unsigned int src_stream_outs;
+ unsigned int src_phys_ins;
+ unsigned int dest_stream_ins;
+ unsigned int dest_phys_outs;
+ unsigned int src_stream_out_ofs;
+ unsigned int dest_phys_out_ofs;
+ unsigned int src_mask;
+ unsigned int dest_mask;
+};
+
+/* Audio stream */
+struct lola_stream {
+ unsigned int nid; /* audio widget NID */
+ unsigned int index; /* array index */
+ unsigned int dsd; /* DSD index */
+ bool can_float;
+ struct snd_pcm_substream *substream; /* assigned PCM substream */
+ struct lola_stream *master; /* master stream (for multi-channel) */
+
+ /* buffer setup */
+ unsigned int bufsize;
+ unsigned int period_bytes;
+ unsigned int frags;
+
+ /* format + channel setup */
+ unsigned int format_verb;
+
+ /* flags */
+ unsigned int opened:1;
+ unsigned int prepared:1;
+ unsigned int paused:1;
+ unsigned int running:1;
+};
+
+#define PLAY SNDRV_PCM_STREAM_PLAYBACK
+#define CAPT SNDRV_PCM_STREAM_CAPTURE
+
+struct lola_pcm {
+ unsigned int num_streams;
+ struct snd_dma_buffer bdl; /* BDL buffer */
+ struct lola_stream streams[MAX_STREAM_COUNT];
+};
+
+/* card instance */
+struct lola {
+ struct snd_card *card;
+ struct pci_dev *pci;
+
+ /* pci resources */
+ struct lola_bar bar[2];
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+ struct mutex open_mutex;
+
+ /* CORB/RIRB */
+ struct lola_rb corb;
+ struct lola_rb rirb;
+ unsigned int res, res_ex; /* last read values */
+ /* last command (for debugging) */
+ unsigned int last_cmd_nid, last_verb, last_data, last_extdata;
+
+ /* CORB/RIRB buffers */
+ struct snd_dma_buffer rb;
+
+ /* unsolicited events */
+ unsigned int last_unsol_res;
+
+ /* streams */
+ struct lola_pcm pcm[2];
+
+ /* input src */
+ unsigned int input_src_caps_mask;
+ unsigned int input_src_mask;
+
+ /* pins */
+ struct lola_pin_array pin[2];
+
+ /* clock */
+ struct lola_clock_widget clock;
+ int ref_count_rate;
+ unsigned int sample_rate;
+
+ /* mixer */
+ struct lola_mixer_widget mixer;
+
+ /* hw info */
+ unsigned int version;
+ unsigned int lola_caps;
+
+ /* parameters */
+ unsigned int granularity;
+ unsigned int sample_rate_min;
+ unsigned int sample_rate_max;
+
+ /* flags */
+ unsigned int initialized:1;
+ unsigned int cold_reset:1;
+ unsigned int polling_mode:1;
+
+ /* for debugging */
+ unsigned int debug_res;
+ unsigned int debug_res_ex;
+};
+
+#define BAR0 0
+#define BAR1 1
+
+/* Helper macros */
+#define lola_readl(chip, idx, name) \
+ readl((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readw(chip, idx, name) \
+ readw((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readb(chip, idx, name) \
+ readb((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writel(chip, idx, name, val) \
+ writel((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writew(chip, idx, name, val) \
+ writew((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writeb(chip, idx, name, val) \
+ writeb((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+
+#define lola_dsd_read(chip, dsd, name) \
+ readl((chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+#define lola_dsd_write(chip, dsd, name, val) \
+ writel((val), (chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+
+/* GET verbs HDAudio */
+#define LOLA_VERB_GET_STREAM_FORMAT 0xa00
+#define LOLA_VERB_GET_AMP_GAIN_MUTE 0xb00
+#define LOLA_VERB_PARAMETERS 0xf00
+#define LOLA_VERB_GET_POWER_STATE 0xf05
+#define LOLA_VERB_GET_CONV 0xf06
+#define LOLA_VERB_GET_UNSOLICITED_RESPONSE 0xf08
+#define LOLA_VERB_GET_DIGI_CONVERT_1 0xf0d
+#define LOLA_VERB_GET_CONFIG_DEFAULT 0xf1c
+#define LOLA_VERB_GET_SUBSYSTEM_ID 0xf20
+/* GET verbs Digigram */
+#define LOLA_VERB_GET_FIXED_GAIN 0xfc0
+#define LOLA_VERB_GET_GAIN_SELECT 0xfc1
+#define LOLA_VERB_GET_MAX_LEVEL 0xfc2
+#define LOLA_VERB_GET_CLOCK_LIST 0xfc3
+#define LOLA_VERB_GET_CLOCK_SELECT 0xfc4
+#define LOLA_VERB_GET_CLOCK_STATUS 0xfc5
+
+/* SET verbs HDAudio */
+#define LOLA_VERB_SET_STREAM_FORMAT 0x200
+#define LOLA_VERB_SET_AMP_GAIN_MUTE 0x300
+#define LOLA_VERB_SET_POWER_STATE 0x705
+#define LOLA_VERB_SET_CHANNEL_STREAMID 0x706
+#define LOLA_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define LOLA_VERB_SET_DIGI_CONVERT_1 0x70d
+/* SET verbs Digigram */
+#define LOLA_VERB_SET_GAIN_SELECT 0xf81
+#define LOLA_VERB_SET_CLOCK_SELECT 0xf84
+#define LOLA_VERB_SET_GRANULARITY_STEPS 0xf86
+#define LOLA_VERB_SET_SOURCE_GAIN 0xf87
+#define LOLA_VERB_SET_MIX_GAIN 0xf88
+#define LOLA_VERB_SET_DESTINATION_GAIN 0xf89
+#define LOLA_VERB_SET_SRC 0xf8a
+
+/* Parameter IDs used with LOLA_VERB_PARAMETERS */
+#define LOLA_PAR_VENDOR_ID 0x00
+#define LOLA_PAR_FUNCTION_TYPE 0x05
+#define LOLA_PAR_AUDIO_WIDGET_CAP 0x09
+#define LOLA_PAR_PCM 0x0a
+#define LOLA_PAR_STREAM_FORMATS 0x0b
+#define LOLA_PAR_PIN_CAP 0x0c
+#define LOLA_PAR_AMP_IN_CAP 0x0d
+#define LOLA_PAR_CONNLIST_LEN 0x0e
+#define LOLA_PAR_POWER_STATE 0x0f
+#define LOLA_PAR_GPIO_CAP 0x11
+#define LOLA_PAR_AMP_OUT_CAP 0x12
+#define LOLA_PAR_SPECIFIC_CAPS 0x80
+#define LOLA_PAR_FIXED_GAIN_LIST 0x81
+
+/* extract results of LOLA_PAR_SPECIFIC_CAPS */
+#define LOLA_AFG_MIXER_WIDGET_PRESENT(res) ((res & (1 << 21)) != 0)
+#define LOLA_AFG_CLOCK_WIDGET_PRESENT(res) ((res & (1 << 20)) != 0)
+#define LOLA_AFG_INPUT_PIN_COUNT(res) ((res >> 10) & 0x2ff)
+#define LOLA_AFG_OUTPUT_PIN_COUNT(res) ((res) & 0x2ff)
+
+/* extract results of LOLA_PAR_AMP_IN_CAP / LOLA_PAR_AMP_OUT_CAP */
+#define LOLA_AMP_MUTE_CAPABLE(res) ((res & (1 << 31)) != 0)
+#define LOLA_AMP_STEP_SIZE(res) ((res >> 24) & 0x7f)
+#define LOLA_AMP_NUM_STEPS(res) ((res >> 12) & 0x3ff)
+#define LOLA_AMP_OFFSET(res) ((res) & 0x3ff)
+
+#define LOLA_GRANULARITY_MIN 8
+#define LOLA_GRANULARITY_MAX 32
+#define LOLA_GRANULARITY_STEP 8
+
+/* parameters used with unsolicited command/response */
+#define LOLA_UNSOLICITED_TAG_MASK 0x3f
+#define LOLA_UNSOLICITED_TAG 0x1a
+#define LOLA_UNSOLICITED_ENABLE 0x80
+#define LOLA_UNSOL_RESP_TAG_OFFSET 26
+
+/* count values in the Vendor Specific Mixer Widget's Audio Widget Capabilities */
+#define LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(res) ((res >> 2) & 0x1f)
+#define LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(res) ((res >> 7) & 0x1f)
+
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata);
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval);
+int lola_codec_flush(struct lola *chip);
+#define lola_read_param(chip, nid, param, val) \
+ lola_codec_read(chip, nid, LOLA_VERB_PARAMETERS, param, 0, val, NULL)
+
+/* PCM */
+int lola_create_pcm(struct lola *chip);
+void lola_free_pcm(struct lola *chip);
+int lola_init_pcm(struct lola *chip, int dir, int *nidp);
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits);
+
+/* clock */
+int lola_init_clock_widget(struct lola *chip, int nid);
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force);
+int lola_enable_clock_events(struct lola *chip);
+int lola_set_clock_index(struct lola *chip, unsigned int idx);
+int lola_set_clock(struct lola *chip, int idx);
+int lola_set_sample_rate(struct lola *chip, int rate);
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val);
+unsigned int lola_sample_rate_convert(unsigned int coded);
+
+/* mixer */
+int lola_init_pins(struct lola *chip, int dir, int *nidp);
+int lola_init_mixer_widget(struct lola *chip, int nid);
+void lola_free_mixer(struct lola *chip);
+int lola_create_mixer(struct lola *chip);
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute);
+void lola_save_mixer(struct lola *chip);
+void lola_restore_mixer(struct lola *chip);
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update);
+
+/* proc */
+#ifdef CONFIG_SND_DEBUG
+void lola_proc_debug_new(struct lola *chip);
+#else
+#define lola_proc_debug_new(chip)
+#endif
+
+#endif /* _LOLA_H */
diff --git a/sound/pci/lola/lola_clock.c b/sound/pci/lola/lola_clock.c
new file mode 100644
index 000000000000..72f8ef0ac865
--- /dev/null
+++ b/sound/pci/lola/lola_clock.c
@@ -0,0 +1,323 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+unsigned int lola_sample_rate_convert(unsigned int coded)
+{
+ unsigned int freq;
+
+ /* base frequency */
+ switch (coded & 0x3) {
+ case 0: freq = 48000; break;
+ case 1: freq = 44100; break;
+ case 2: freq = 32000; break;
+ default: return 0; /* error */
+ }
+
+ /* multiplier / devisor */
+ switch (coded & 0x1c) {
+ case (0 << 2): break;
+ case (4 << 2): break;
+ case (1 << 2): freq *= 2; break;
+ case (2 << 2): freq *= 4; break;
+ case (5 << 2): freq /= 2; break;
+ case (6 << 2): freq /= 4; break;
+ default: return 0; /* error */
+ }
+
+ /* ajustement */
+ switch (coded & 0x60) {
+ case (0 << 5): break;
+ case (1 << 5): freq = (freq * 999) / 1000; break;
+ case (2 << 5): freq = (freq * 1001) / 1000; break;
+ default: return 0; /* error */
+ }
+ return freq;
+}
+
+/*
+ * Granualrity
+ */
+
+#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000
+#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000
+
+static bool check_gran_clock_compatibility(struct lola *chip,
+ unsigned int val,
+ unsigned int freq)
+{
+ if (!chip->granularity)
+ return true;
+
+ if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX ||
+ (val % LOLA_GRANULARITY_STEP) != 0)
+ return false;
+
+ if (val == LOLA_GRANULARITY_MIN) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN)
+ return false;
+ } else if (val < LOLA_GRANULARITY_MAX) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX)
+ return false;
+ }
+ return true;
+}
+
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force)
+{
+ int err;
+
+ if (!force) {
+ if (val == chip->granularity)
+ return 0;
+#if 0
+ /* change Gran only if there are no streams allocated ! */
+ if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask)
+ return -EBUSY;
+#endif
+ if (!check_gran_clock_compatibility(chip, val,
+ chip->clock.cur_freq))
+ return -EINVAL;
+ }
+
+ chip->granularity = val;
+ val /= LOLA_GRANULARITY_STEP;
+
+ /* audio function group */
+ err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS,
+ val, 0);
+ if (err < 0)
+ return err;
+ /* this can be a very slow function !!! */
+ usleep_range(400 * val, 20000);
+ return lola_codec_flush(chip);
+}
+
+/*
+ * Clock widget handling
+ */
+
+int __devinit lola_init_clock_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int i, j, nitems, nb_verbs, idx, idx_list;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */
+ snd_printdd("No valid clock widget\n");
+ return 0;
+ }
+
+ chip->clock.nid = nid;
+ chip->clock.items = val & 0xff;
+ snd_printdd("clock_list nid=%x, entries=%d\n", nid,
+ chip->clock.items);
+ if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) {
+ printk(KERN_ERR SFX "CLOCK_LIST too big: %d\n",
+ chip->clock.items);
+ return -EINVAL;
+ }
+
+ nitems = chip->clock.items;
+ nb_verbs = (nitems + 3) / 4;
+ idx = 0;
+ idx_list = 0;
+ for (i = 0; i < nb_verbs; i++) {
+ unsigned int res_ex;
+ unsigned short items[4];
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ idx, 0, &val, &res_ex);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read CLOCK_LIST\n");
+ return -EINVAL;
+ }
+
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ int format = LOLA_CLOCK_FORMAT_NONE;
+ bool add_clock = true;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ else if (freq == 48000) {
+ chip->clock.cur_index = idx_list;
+ chip->clock.cur_freq = 48000;
+ chip->clock.cur_valid = true;
+ }
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ /* video clock has a format (0:NTSC, 1:PAL)*/
+ if (items[j] & 0x80)
+ format = LOLA_CLOCK_FORMAT_NTSC;
+ else
+ format = LOLA_CLOCK_FORMAT_PAL;
+ }
+ if (add_clock) {
+ struct lola_sample_clock *sc;
+ sc = &chip->clock.sample_clock[idx_list];
+ sc->type = type;
+ sc->format = format;
+ sc->freq = freq;
+ /* keep the index used with the board */
+ chip->clock.idx_lookup[idx_list] = idx;
+ idx_list++;
+ } else {
+ chip->clock.items--;
+ }
+ if (++idx >= nitems)
+ break;
+ }
+ }
+ return 0;
+}
+
+/* enable unsolicited events of the clock widget */
+int lola_enable_clock_events(struct lola *chip)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_UNSOLICITED_ENABLE,
+ LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG,
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ printk(KERN_WARNING SFX "error in enable_clock_events %d\n",
+ res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int lola_set_clock_index(struct lola *chip, unsigned int idx)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_CLOCK_SELECT,
+ chip->clock.idx_lookup[idx],
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ printk(KERN_WARNING SFX "error in set_clock %d\n", res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val)
+{
+ unsigned int tag;
+
+ /* the current EXTERNAL clock information gets updated by interrupt
+ * with an unsolicited response
+ */
+ if (!val)
+ return false;
+ tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK;
+ if (tag != LOLA_UNSOLICITED_TAG)
+ return false;
+
+ /* only for current = external clocks */
+ if (chip->clock.sample_clock[chip->clock.cur_index].type !=
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f);
+ chip->clock.cur_valid = (val & 0x100) != 0;
+ }
+ return true;
+}
+
+int lola_set_clock(struct lola *chip, int idx)
+{
+ int freq = 0;
+ bool valid = false;
+
+ if (idx == chip->clock.cur_index) {
+ /* current clock is allowed */
+ freq = chip->clock.cur_freq;
+ valid = chip->clock.cur_valid;
+ } else if (chip->clock.sample_clock[idx].type ==
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ /* internal clocks allowed */
+ freq = chip->clock.sample_clock[idx].freq;
+ valid = true;
+ }
+
+ if (!freq || !valid)
+ return -EINVAL;
+
+ if (!check_gran_clock_compatibility(chip, chip->granularity, freq))
+ return -EINVAL;
+
+ if (idx != chip->clock.cur_index) {
+ int err = lola_set_clock_index(chip, idx);
+ if (err < 0)
+ return err;
+ /* update new settings */
+ chip->clock.cur_index = idx;
+ chip->clock.cur_freq = freq;
+ chip->clock.cur_valid = true;
+ }
+ return 0;
+}
+
+int lola_set_sample_rate(struct lola *chip, int rate)
+{
+ int i;
+
+ if (chip->clock.cur_freq == rate && chip->clock.cur_valid)
+ return 0;
+ /* search for new dwClockIndex */
+ for (i = 0; i < chip->clock.items; i++) {
+ if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL &&
+ chip->clock.sample_clock[i].freq == rate)
+ break;
+ }
+ if (i >= chip->clock.items)
+ return -EINVAL;
+ return lola_set_clock(chip, i);
+}
+
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
new file mode 100644
index 000000000000..5d518f1a712c
--- /dev/null
+++ b/sound/pci/lola/lola_mixer.c
@@ -0,0 +1,839 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lola.h"
+
+static int __devinit lola_init_pin(struct lola *chip, struct lola_pin *pin,
+ int dir, int nid)
+{
+ unsigned int val;
+ int err;
+
+ pin->nid = nid;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ val &= 0x00f00fff; /* test TYPE and bits 0..11 */
+ if (val == 0x00400200) /* Type = 4, Digital = 1 */
+ pin->is_analog = false;
+ else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
+ pin->is_analog = true;
+ else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
+ pin->is_analog = true;
+ else {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n", val, nid);
+ return -EINVAL;
+ }
+
+ /* analog parameters only following, so continue in case of Digital pin
+ */
+ if (!pin->is_analog)
+ return 0;
+
+ if (dir == PLAY)
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
+ else
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read AMP-caps for 0x%x\n", nid);
+ return err;
+ }
+
+ pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
+ pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
+ pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
+ if (pin->amp_num_steps) {
+ /* zero as mute state */
+ pin->amp_num_steps++;
+ pin->amp_step_size++;
+ }
+ pin->amp_offset = LOLA_AMP_OFFSET(val);
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
+ NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't get MAX_LEVEL 0x%x\n", nid);
+ return err;
+ }
+ pin->max_level = val & 0x3ff; /* 10 bits */
+
+ pin->config_default_reg = 0;
+ pin->fixed_gain_list_len = 0;
+ pin->cur_gain_step = 0;
+
+ return 0;
+}
+
+int __devinit lola_init_pins(struct lola *chip, int dir, int *nidp)
+{
+ int i, err, nid;
+ nid = *nidp;
+ for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
+ err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
+ if (err < 0)
+ return err;
+ if (chip->pin[dir].pins[i].is_analog)
+ chip->pin[dir].num_analog_pins++;
+ }
+ *nidp = nid;
+ return 0;
+}
+
+void lola_free_mixer(struct lola *chip)
+{
+ if (chip->mixer.array_saved)
+ vfree(chip->mixer.array_saved);
+}
+
+int __devinit lola_init_mixer_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
+ snd_printdd("No valid mixer widget\n");
+ return 0;
+ }
+
+ chip->mixer.nid = nid;
+ chip->mixer.caps = val;
+ chip->mixer.array = (struct lola_mixer_array __iomem *)
+ (chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
+
+ /* reserve memory to copy mixer data for sleep mode transitions */
+ chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
+
+ /* mixer matrix sources are physical input data and play streams */
+ chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
+ chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
+
+ /* mixer matrix destinations are record streams and physical output */
+ chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
+ chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
+
+ /* mixer matrix can have unused areas between PhysIn and
+ * Play or Record and PhysOut zones
+ */
+ chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
+ LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
+ chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
+ LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(val);
+
+ /* example : MixerMatrix of LoLa881
+ * 0-------8------16-------8------16
+ * | | | | |
+ * | INPUT | | INPUT | |
+ * | -> |unused | -> |unused |
+ * | RECORD| | OUTPUT| |
+ * | | | | |
+ * 8--------------------------------
+ * | | | | |
+ * | | | | |
+ * |unused |unused |unused |unused |
+ * | | | | |
+ * | | | | |
+ * 16-------------------------------
+ * | | | | |
+ * | PLAY | | PLAY | |
+ * | -> |unused | -> |unused |
+ * | RECORD| | OUTPUT| |
+ * | | | | |
+ * 8--------------------------------
+ * | | | | |
+ * | | | | |
+ * |unused |unused |unused |unused |
+ * | | | | |
+ * | | | | |
+ * 16-------------------------------
+ */
+ if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
+ chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
+ printk(KERN_ERR SFX "Invalid mixer widget size\n");
+ return -EINVAL;
+ }
+
+ chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
+ (((1U << chip->mixer.src_stream_outs) - 1)
+ << chip->mixer.src_stream_out_ofs);
+ chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
+ (((1U << chip->mixer.dest_phys_outs) - 1)
+ << chip->mixer.dest_phys_out_ofs);
+
+ return 0;
+}
+
+static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
+ unsigned short gain, bool on)
+{
+ unsigned int oldval, val;
+
+ if (!(chip->mixer.src_mask & (1 << id)))
+ return -EINVAL;
+ writew(gain, &chip->mixer.array->src_gain[id]);
+ oldval = val = readl(&chip->mixer.array->src_gain_enable);
+ if (on)
+ val |= (1 << id);
+ else
+ val &= ~(1 << id);
+ writel(val, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ /* inform micro-controller about the new source gain */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, id, 0);
+}
+
+#if 0 /* not used */
+static int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
+ unsigned short *gains)
+{
+ int i;
+
+ if ((chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->src_gain[i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
+ /* update for all srcs at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
+ }
+ /* update manually */
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, i, 0);
+ }
+ }
+ return 0;
+}
+#endif /* not used */
+
+static int lola_mixer_set_mapping_gain(struct lola *chip,
+ unsigned int src, unsigned int dest,
+ unsigned short gain, bool on)
+{
+ unsigned int val;
+
+ if (!(chip->mixer.src_mask & (1 << src)) ||
+ !(chip->mixer.dest_mask & (1 << dest)))
+ return -EINVAL;
+ if (on)
+ writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
+ val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
+ if (on)
+ val |= (1 << src);
+ else
+ val &= ~(1 << src);
+ writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
+ lola_codec_flush(chip);
+ return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
+ src, dest);
+}
+
+static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
+ unsigned int mask, unsigned short *gains)
+{
+ int i;
+
+ if (!(chip->mixer.dest_mask & (1 << id)) ||
+ (chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
+ lola_codec_flush(chip);
+ /* update for all dests at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call);
+
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
+{
+ struct lola_pin *pin;
+ int idx, max_idx;
+
+ pin = chip->pin[dir].pins;
+ max_idx = chip->pin[dir].num_pins;
+ for (idx = 0; idx < max_idx; idx++) {
+ if (pin[idx].is_analog) {
+ unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
+ /* set volume and do not save the value */
+ set_analog_volume(chip, dir, idx, val, false);
+ }
+ }
+ return lola_codec_flush(chip);
+}
+
+void lola_save_mixer(struct lola *chip)
+{
+ /* mute analog output */
+ if (chip->mixer.array_saved) {
+ /* store contents of mixer array */
+ memcpy_fromio(chip->mixer.array_saved, chip->mixer.array,
+ sizeof(*chip->mixer.array));
+ }
+ lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */
+}
+
+void lola_restore_mixer(struct lola *chip)
+{
+ int i;
+
+ /*lola_reset_setups(chip);*/
+ if (chip->mixer.array_saved) {
+ /* restore contents of mixer array */
+ memcpy_toio(chip->mixer.array, chip->mixer.array_saved,
+ sizeof(*chip->mixer.array));
+ /* inform micro-controller about all restored values
+ * and ignore return values
+ */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ chip->mixer.src_stream_out_ofs + i, 0);
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.dest_phys_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ chip->mixer.dest_phys_out_ofs + i, 0);
+ lola_codec_flush(chip);
+ }
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call)
+{
+ struct lola_pin *pin;
+ int err;
+
+ if (idx >= chip->pin[dir].num_pins)
+ return -EINVAL;
+ pin = &chip->pin[dir].pins[idx];
+ if (!pin->is_analog || pin->amp_num_steps <= val)
+ return -EINVAL;
+ if (external_call && pin->cur_gain_step == val)
+ return 0;
+ if (external_call)
+ lola_codec_flush(chip);
+ err = lola_codec_write(chip, pin->nid,
+ LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
+ if (err < 0)
+ return err;
+ if (external_call)
+ pin->cur_gain_step = val;
+ return 0;
+}
+
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
+{
+ int ret = 0;
+ int success = 0;
+ int n, err;
+
+ /* SRC can be activated and the dwInputSRCMask is valid? */
+ if ((chip->input_src_caps_mask & src_mask) != src_mask)
+ return -EINVAL;
+ /* handle all even Inputs - SRC is a stereo setting !!! */
+ for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
+ unsigned int mask = 3U << n; /* handle the stereo case */
+ unsigned int new_src, src_state;
+ if (!(chip->input_src_caps_mask & mask))
+ continue;
+ /* if one IO needs SRC, both stereo IO will get SRC */
+ new_src = (src_mask & mask) != 0;
+ if (update) {
+ src_state = (chip->input_src_mask & mask) != 0;
+ if (src_state == new_src)
+ continue; /* nothing to change for this IO */
+ }
+ err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
+ LOLA_VERB_SET_SRC, new_src, 0);
+ if (!err)
+ success++;
+ else
+ ret = err;
+ }
+ if (success)
+ ret = lola_codec_flush(chip);
+ if (!ret)
+ chip->input_src_mask = src_mask;
+ return ret;
+}
+
+/*
+ */
+static int init_mixer_values(struct lola *chip)
+{
+ int i;
+
+ /* all src on */
+ lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
+
+ /* clear all matrix */
+ memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
+ /* set src gain to 0dB */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_mixer_set_src_gain(chip,
+ i + chip->mixer.src_stream_out_ofs,
+ 336, true); /* 0dB */
+ /* set 1:1 dest gain */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
+ int src = i % chip->mixer.src_phys_ins;
+ lola_mixer_set_mapping_gain(chip, src, i, 336, true);
+ }
+ for (i = 0; i < chip->mixer.src_stream_outs; i++) {
+ int src = chip->mixer.src_stream_out_ofs + i;
+ int dst = chip->mixer.dest_phys_out_ofs +
+ i % chip->mixer.dest_phys_outs;
+ lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
+ }
+ return 0;
+}
+
+/*
+ * analog mixer control element
+ */
+static int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chip->pin[dir].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
+ return 0;
+}
+
+static int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ chip->pin[dir].pins[i].cur_gain_step;
+ return 0;
+}
+
+static int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i, err;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++) {
+ err = set_analog_volume(chip, dir, i,
+ ucontrol->value.integer.value[i],
+ true);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ unsigned int val1, val2;
+ struct lola_pin *pin;
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ pin = &chip->pin[dir].pins[0];
+
+ val2 = pin->amp_step_size * 25;
+ val1 = -1 * (int)pin->amp_offset * (int)val2;
+#ifdef TLV_DB_SCALE_MUTE
+ val2 |= TLV_DB_SCALE_MUTE;
+#endif
+ if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
+ return -EFAULT;
+ if (put_user(2 * sizeof(unsigned int), tlv + 1))
+ return -EFAULT;
+ if (put_user(val1, tlv + 2))
+ return -EFAULT;
+ if (put_user(val2, tlv + 3))
+ return -EFAULT;
+ return 0;
+}
+
+static struct snd_kcontrol_new lola_analog_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = lola_analog_vol_info,
+ .get = lola_analog_vol_get,
+ .put = lola_analog_vol_put,
+ .tlv.c = lola_analog_vol_tlv,
+};
+
+static int __devinit create_analog_mixer(struct lola *chip, int dir, char *name)
+{
+ if (!chip->pin[dir].num_pins)
+ return 0;
+ /* no analog volumes on digital only adapters */
+ if (chip->pin[dir].num_pins != chip->pin[dir].num_analog_pins)
+ return 0;
+ lola_analog_mixer.name = name;
+ lola_analog_mixer.private_value = dir;
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_analog_mixer, chip));
+}
+
+/*
+ * Hardware sample rate converter on digital input
+ */
+static int lola_input_src_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = chip->pin[CAPT].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int lola_input_src_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ !!(chip->input_src_mask & (1 << i));
+ return 0;
+}
+
+static int lola_input_src_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+ unsigned int mask;
+
+ mask = 0;
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ if (ucontrol->value.integer.value[i])
+ mask |= 1 << i;
+ return lola_set_src_config(chip, mask, true);
+}
+
+static struct snd_kcontrol_new lola_input_src_mixer __devinitdata = {
+ .name = "Digital SRC Capture Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = lola_input_src_info,
+ .get = lola_input_src_get,
+ .put = lola_input_src_put,
+};
+
+/*
+ * Lola16161 or Lola881 can have Hardware sample rate converters
+ * on its digital input pins
+ */
+static int __devinit create_input_src_mixer(struct lola *chip)
+{
+ if (!chip->input_src_caps_mask)
+ return 0;
+
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_input_src_mixer, chip));
+}
+
+/*
+ * src gain mixer
+ */
+static int lola_src_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 409;
+ return 0;
+}
+
+static int lola_src_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int mask, i;
+
+ mask = readl(&chip->mixer.array->src_gain_enable);
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << idx)))
+ return -EINVAL;
+ if (mask & (1 << idx))
+ val = readw(&chip->mixer.array->src_gain[idx]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_src_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ int i, err;
+
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val)
+ val--;
+ err = lola_mixer_set_src_gain(chip, idx, val, !!val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
+static const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_src_gain_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_src_gain_info,
+ .get = lola_src_gain_get,
+ .put = lola_src_gain_put,
+ .tlv.p = lola_src_gain_tlv,
+};
+
+static int __devinit create_src_gain_mixer(struct lola *chip,
+ int num, int ofs, char *name)
+{
+ lola_src_gain_mixer.name = name;
+ lola_src_gain_mixer.private_value = ofs + (num << 8);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_src_gain_mixer, chip));
+}
+
+/*
+ * destination gain (matrix-like) mixer
+ */
+static int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = src_num;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 433;
+ return 0;
+}
+
+static int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask, i;
+
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
+ for (i = 0; i < src_num; i++) {
+ unsigned int src = src_ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << src)))
+ return -EINVAL;
+ if (mask & (1 << dst))
+ val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask;
+ unsigned short gains[MAX_STREAM_COUNT];
+ int i, num;
+
+ mask = 0;
+ num = 0;
+ for (i = 0; i < src_num; i++) {
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val) {
+ gains[num++] = val - 1;
+ mask |= 1 << i;
+ }
+ }
+ mask <<= src_ofs;
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ return lola_mixer_set_dest_gains(chip, dst, mask, gains);
+}
+
+static const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_dest_gain_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_dest_gain_info,
+ .get = lola_dest_gain_get,
+ .put = lola_dest_gain_put,
+ .tlv.p = lola_dest_gain_tlv,
+};
+
+static int __devinit create_dest_gain_mixer(struct lola *chip,
+ int src_num, int src_ofs,
+ int num, int ofs, char *name)
+{
+ lola_dest_gain_mixer.count = num;
+ lola_dest_gain_mixer.name = name;
+ lola_dest_gain_mixer.private_value =
+ src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_dest_gain_mixer, chip));
+}
+
+/*
+ */
+int __devinit lola_create_mixer(struct lola *chip)
+{
+ int err;
+
+ err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_input_src_mixer(chip);
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
+ "Line Source Gain Volume");
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ "Stream Source Gain Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_stream_ins, 0,
+ "Line Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_stream_ins, 0,
+ "Stream-Loopback Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Line-Loopback Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Stream Playback Volume");
+ if (err < 0)
+ return err;
+
+ return init_mixer_values(chip);
+}
diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c
new file mode 100644
index 000000000000..c44db68eecb5
--- /dev/null
+++ b/sound/pci/lola/lola_pcm.c
@@ -0,0 +1,706 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+#define LOLA_MAX_BDL_ENTRIES 8
+#define LOLA_MAX_BUF_SIZE (1024*1024*1024)
+#define LOLA_BDL_ENTRY_SIZE (16 * 16)
+
+static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ return &chip->pcm[substream->stream];
+}
+
+static struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream)
+{
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ unsigned int idx = substream->number;
+ return &pcm->streams[idx];
+}
+
+static unsigned int lola_get_lrc(struct lola *chip)
+{
+ return lola_readl(chip, BAR1, LRC);
+}
+
+static unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync)
+{
+ unsigned int tstamp = lola_get_lrc(chip) >> 8;
+ if (chip->granularity) {
+ unsigned int wait_banks = quick_no_sync ? 0 : 8;
+ tstamp += (wait_banks + 1) * chip->granularity - 1;
+ tstamp -= tstamp % chip->granularity;
+ }
+ return tstamp << 8;
+}
+
+/* clear any pending interrupt status */
+static void lola_stream_clear_pending_irq(struct lola *chip,
+ struct lola_stream *str)
+{
+ unsigned int val = lola_dsd_read(chip, str->dsd, STS);
+ val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS;
+ if (val)
+ lola_dsd_write(chip, str->dsd, STS, val);
+}
+
+static void lola_stream_start(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+}
+
+static void lola_stream_stop(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+ lola_stream_clear_pending_irq(chip, str);
+}
+
+static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int val;
+ val = lola_dsd_read(chip, str->dsd, CTL);
+ if (!(val & LOLA_DSD_CTL_SRST))
+ return;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "SRST not clear (stream %d)\n", str->dsd);
+}
+
+static int lola_stream_wait_for_fifo(struct lola *chip,
+ struct lola_stream *str,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) == val)
+ return 0;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "FIFO not ready (stream %d)\n", str->dsd);
+ return -EIO;
+}
+
+/* sync for FIFO ready/empty for all linked streams;
+ * clear paused flag when FIFO gets ready again
+ */
+static int lola_sync_wait_for_fifo(struct lola *chip,
+ struct snd_pcm_substream *substream,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ struct snd_pcm_substream *s;
+ int pending = 0;
+
+ while (time_before(jiffies, end_time)) {
+ pending = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->prepared && str->paused) {
+ unsigned int reg;
+ reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) != val) {
+ pending = str->dsd + 1;
+ break;
+ }
+ if (ready)
+ str->paused = 0;
+ }
+ }
+ if (!pending)
+ return 0;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "FIFO not ready (pending %d)\n", pending - 1);
+ return -EIO;
+}
+
+/* finish pause - prepare for a new resume */
+static void lola_sync_pause(struct lola *chip,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_substream *s;
+
+ lola_sync_wait_for_fifo(chip, substream, false);
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->paused && str->prepared)
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ }
+ lola_sync_wait_for_fifo(chip, substream, true);
+}
+
+static void lola_stream_reset(struct lola *chip, struct lola_stream *str)
+{
+ if (str->prepared) {
+ if (str->paused)
+ lola_sync_pause(chip, str->substream);
+ str->prepared = 0;
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ lola_stream_wait_for_fifo(chip, str, false);
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST);
+ lola_dsd_write(chip, str->dsd, LVI, 0);
+ lola_dsd_write(chip, str->dsd, BDPU, 0);
+ lola_dsd_write(chip, str->dsd, BDPL, 0);
+ wait_for_srst_clear(chip, str);
+ }
+}
+
+static struct snd_pcm_hardware lola_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = LOLA_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = LOLA_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = LOLA_MAX_BDL_ENTRIES,
+ .fifo_size = 0,
+};
+
+static int lola_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ mutex_lock(&chip->open_mutex);
+ if (str->opened) {
+ mutex_unlock(&chip->open_mutex);
+ return -EBUSY;
+ }
+ str->substream = substream;
+ str->master = NULL;
+ str->opened = 1;
+ runtime->hw = lola_pcm_hw;
+ runtime->hw.channels_max = pcm->num_streams - str->index;
+ if (chip->sample_rate) {
+ /* sample rate is locked */
+ runtime->hw.rate_min = chip->sample_rate;
+ runtime->hw.rate_max = chip->sample_rate;
+ } else {
+ runtime->hw.rate_min = chip->sample_rate_min;
+ runtime->hw.rate_max = chip->sample_rate_max;
+ }
+ chip->ref_count_rate++;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ /* period size = multiple of chip->granularity (8, 16 or 32 frames)*/
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ chip->granularity);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ chip->granularity);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static void lola_cleanup_slave_streams(struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ int i;
+ for (i = str->index + 1; i < pcm->num_streams; i++) {
+ struct lola_stream *s = &pcm->streams[i];
+ if (s->master != str)
+ break;
+ s->master = NULL;
+ s->opened = 0;
+ }
+}
+
+static int lola_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ if (str->substream == substream) {
+ str->substream = NULL;
+ str->opened = 0;
+ }
+ if (--chip->ref_count_rate == 0) {
+ /* release sample rate */
+ chip->sample_rate = 0;
+ }
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static int lola_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct lola_stream *str = lola_get_stream(substream);
+
+ str->bufsize = 0;
+ str->period_bytes = 0;
+ str->format_verb = 0;
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ mutex_unlock(&chip->open_mutex);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct snd_pcm_substream *substream,
+ struct lola_stream *str, u32 **bdlp,
+ int ofs, int size)
+{
+ u32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (str->frags >= LOLA_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = size ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ str->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/*
+ * set up BDL entries
+ */
+static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
+ struct snd_pcm_substream *substream,
+ struct lola_stream *str)
+{
+ u32 *bdl;
+ int i, ofs, periods, period_bytes;
+
+ period_bytes = str->period_bytes;
+ periods = str->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (u32 *)(pcm->bdl.area + LOLA_BDL_ENTRY_SIZE * str->index);
+ ofs = 0;
+ str->frags = 0;
+ for (i = 0; i < periods; i++) {
+ ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
+ str->bufsize, period_bytes);
+ return -EINVAL;
+}
+
+static unsigned int lola_get_format_verb(struct snd_pcm_substream *substream)
+{
+ unsigned int verb;
+
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ verb = 0x00000000;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ verb = 0x00000200;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ verb = 0x00000300;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ verb = 0x00001300;
+ break;
+ default:
+ return 0;
+ }
+ verb |= substream->runtime->channels;
+ return verb;
+}
+
+static int lola_set_stream_config(struct lola *chip,
+ struct lola_stream *str,
+ int channels)
+{
+ int i, err;
+ unsigned int verb, val;
+
+ /* set format info for all channels
+ * (with only one command for the first channel)
+ */
+ err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT,
+ str->format_verb, 0, &val, NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Cannot set stream format 0x%x\n",
+ str->format_verb);
+ return err;
+ }
+
+ /* update stream - channel config */
+ for (i = 0; i < channels; i++) {
+ verb = (str->index << 6) | i;
+ err = lola_codec_read(chip, str[i].nid,
+ LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb,
+ &val, NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Cannot set stream channel %d\n", i);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ dma_addr_t bdl;
+
+ if (str->prepared)
+ return -EINVAL;
+
+ /* set up BDL */
+ bdl = pcm->bdl.addr + LOLA_BDL_ENTRY_SIZE * str->index;
+ lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl);
+ lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl));
+ /* program the stream LVI (last valid index) of the BDL */
+ lola_dsd_write(chip, str->dsd, LVI, str->frags - 1);
+ lola_stream_clear_pending_irq(chip, str);
+
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN);
+
+ str->prepared = 1;
+
+ return lola_stream_wait_for_fifo(chip, str, true);
+}
+
+static int lola_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int bufsize, period_bytes, format_verb;
+ int i, err;
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ if (str->index + runtime->channels > pcm->num_streams) {
+ mutex_unlock(&chip->open_mutex);
+ return -EINVAL;
+ }
+ for (i = 1; i < runtime->channels; i++) {
+ str[i].master = str;
+ str[i].opened = 1;
+ }
+ mutex_unlock(&chip->open_mutex);
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ format_verb = lola_get_format_verb(substream);
+
+ str->bufsize = bufsize;
+ str->period_bytes = period_bytes;
+ str->format_verb = format_verb;
+
+ err = lola_setup_periods(chip, pcm, substream, str);
+ if (err < 0)
+ return err;
+
+ err = lola_set_sample_rate(chip, runtime->rate);
+ if (err < 0)
+ return err;
+ chip->sample_rate = runtime->rate; /* sample rate gets locked */
+
+ err = lola_set_stream_config(chip, str, runtime->channels);
+ if (err < 0)
+ return err;
+
+ err = lola_setup_controller(chip, pcm, str);
+ if (err < 0) {
+ lola_stream_reset(chip, str);
+ return err;
+ }
+
+ return 0;
+}
+
+static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str;
+ struct snd_pcm_substream *s;
+ unsigned int start;
+ unsigned int tstamp;
+ bool sync_streams;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * sample correct synchronization is only needed starting several
+ * streams. On stop or if only one stream do as quick as possible
+ */
+ sync_streams = (start && snd_pcm_stream_linked(substream));
+ tstamp = lola_get_tstamp(chip, !sync_streams);
+ spin_lock(&chip->reg_lock);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (start)
+ lola_stream_start(chip, str, tstamp);
+ else
+ lola_stream_stop(chip, str, tstamp);
+ str->running = start;
+ str->paused = !start;
+ snd_pcm_trigger_done(s, substream);
+ }
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB);
+
+ if (pos >= str->bufsize)
+ pos = 0;
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
+{
+ int i;
+
+ for (i = 0; bits && i < pcm->num_streams; i++) {
+ if (bits & (1 << i)) {
+ struct lola_stream *str = &pcm->streams[i];
+ if (str->substream && str->running)
+ snd_pcm_period_elapsed(str->substream);
+ bits &= ~(1 << i);
+ }
+ }
+}
+
+static struct snd_pcm_ops lola_pcm_ops = {
+ .open = lola_pcm_open,
+ .close = lola_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = lola_pcm_hw_params,
+ .hw_free = lola_pcm_hw_free,
+ .prepare = lola_pcm_prepare,
+ .trigger = lola_pcm_trigger,
+ .pointer = lola_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+int __devinit lola_create_pcm(struct lola *chip)
+{
+ struct snd_pcm *pcm;
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->pcm[i].bdl);
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_pcm_new(chip->card, "Digigram Lola", 0,
+ chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams,
+ chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strlcpy(pcm->name, "Digigram Lola", sizeof(pcm->name));
+ pcm->private_data = chip;
+ for (i = 0; i < 2; i++) {
+ if (chip->pcm[i].num_streams)
+ snd_pcm_set_ops(pcm, i, &lola_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 1024 * 64, 32 * 1024 * 1024);
+ return 0;
+}
+
+void lola_free_pcm(struct lola *chip)
+{
+ snd_dma_free_pages(&chip->pcm[0].bdl);
+ snd_dma_free_pages(&chip->pcm[1].bdl);
+}
+
+/*
+ */
+
+static int lola_init_stream(struct lola *chip, struct lola_stream *str,
+ int idx, int nid, int dir)
+{
+ unsigned int val;
+ int err;
+
+ str->nid = nid;
+ str->index = idx;
+ str->dsd = idx;
+ if (dir == PLAY)
+ str->dsd += MAX_STREAM_IN_COUNT;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ if (dir == PLAY) {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */
+ if ((val & 0x00f00dff) != 0x00000010) {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ } else {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1)
+ * (bug : ignore bit8: Conn list = 0/1)
+ */
+ if ((val & 0x00f00cff) != 0x00100010) {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ /* test bit9:DIGITAL and bit12:SRC_PRESENT*/
+ if ((val & 0x00001200) == 0x00001200)
+ chip->input_src_caps_mask |= (1 << idx);
+ }
+
+ err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read FORMATS 0x%x\n", nid);
+ return err;
+ }
+ val &= 3;
+ if (val == 3)
+ str->can_float = true;
+ if (!(val & 1)) {
+ printk(KERN_ERR SFX "Invalid formats 0x%x for 0x%x", val, nid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int __devinit lola_init_pcm(struct lola *chip, int dir, int *nidp)
+{
+ struct lola_pcm *pcm = &chip->pcm[dir];
+ int i, nid, err;
+
+ nid = *nidp;
+ for (i = 0; i < pcm->num_streams; i++, nid++) {
+ err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir);
+ if (err < 0)
+ return err;
+ }
+ *nidp = nid;
+ return 0;
+}
diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c
new file mode 100644
index 000000000000..9d7daf897c9d
--- /dev/null
+++ b/sound/pci/lola/lola_proc.c
@@ -0,0 +1,222 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+static void print_audio_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ snd_iprintf(buffer, " Formats: 0x%x\n", val);
+}
+
+static void print_pin_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, unsigned int ampcap,
+ const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ if (val == 0x00400200)
+ return;
+ lola_read_param(chip, nid, ampcap, &val);
+ snd_iprintf(buffer, " Amp-Caps: 0x%x\n", val);
+ snd_iprintf(buffer, " mute=%d, step-size=%d, steps=%d, ofs=%d\n",
+ LOLA_AMP_MUTE_CAPABLE(val),
+ LOLA_AMP_STEP_SIZE(val),
+ LOLA_AMP_NUM_STEPS(val),
+ LOLA_AMP_OFFSET(val));
+ lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val, NULL);
+ snd_iprintf(buffer, " Max-level: 0x%x\n", val);
+}
+
+static void print_clock_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ int i, j, num_clocks;
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Clock] wcaps 0x%x\n", nid, val);
+ num_clocks = val & 0xff;
+ for (i = 0; i < num_clocks; i += 4) {
+ unsigned int res_ex;
+ unsigned short items[4];
+ const char *name;
+
+ lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ i, 0, &val, &res_ex);
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ if (i + j >= num_clocks)
+ break;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ name = "Internal";
+ freq = lola_sample_rate_convert(freq);
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ name = "Video";
+ freq = lola_sample_rate_convert(freq);
+ } else {
+ name = "Other";
+ }
+ snd_iprintf(buffer, " Clock %d: Type %d:%s, freq=%d\n",
+ i + j, type, name, freq);
+ }
+ }
+}
+
+static void print_mixer_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Mixer] wcaps 0x%x\n", nid, val);
+}
+
+static void lola_proc_codec_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ unsigned int val;
+ int i, nid;
+
+ lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ snd_iprintf(buffer, "Vendor: 0x%08x\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ snd_iprintf(buffer, "Function Type: %d\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ snd_iprintf(buffer, "Specific-Caps: 0x%08x\n", val);
+ snd_iprintf(buffer, " Pins-In %d, Pins-Out %d\n",
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+ nid = 2;
+ for (i = 0; i < chip->pcm[CAPT].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-In]");
+ for (i = 0; i < chip->pcm[PLAY].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-Out]");
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_IN_CAP,
+ "[Pin-In]");
+ for (i = 0; i < chip->pin[PLAY].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_OUT_CAP,
+ "[Pin-Out]");
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ print_clock_widget(buffer, chip, nid);
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ print_mixer_widget(buffer, chip, nid);
+ nid++;
+ }
+}
+
+/* direct codec access for debugging */
+static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ char line[64];
+ unsigned int id, verb, data, extdata;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%i %i %i %i", &id, &verb, &data, &extdata) != 4)
+ continue;
+ lola_codec_read(chip, id, verb, data, extdata,
+ &chip->debug_res,
+ &chip->debug_res_ex);
+ }
+}
+
+static void lola_proc_codec_rw_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ snd_iprintf(buffer, "0x%x 0x%x\n", chip->debug_res, chip->debug_res_ex);
+}
+
+/*
+ * dump some registers
+ */
+static void lola_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ int i;
+
+ for (i = 0; i < 0x40; i += 4) {
+ snd_iprintf(buffer, "BAR0 %02x: %08x\n", i,
+ readl(chip->bar[BAR0].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 0x30; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0x80; i < 0xa0; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 32; i++) {
+ snd_iprintf(buffer, "DSD %02x STS %08x\n", i,
+ lola_dsd_read(chip, i, STS));
+ snd_iprintf(buffer, "DSD %02x LPIB %08x\n", i,
+ lola_dsd_read(chip, i, LPIB));
+ snd_iprintf(buffer, "DSD %02x CTL %08x\n", i,
+ lola_dsd_read(chip, i, CTL));
+ snd_iprintf(buffer, "DSD %02x LVIL %08x\n", i,
+ lola_dsd_read(chip, i, LVI));
+ snd_iprintf(buffer, "DSD %02x BDPL %08x\n", i,
+ lola_dsd_read(chip, i, BDPL));
+ snd_iprintf(buffer, "DSD %02x BDPU %08x\n", i,
+ lola_dsd_read(chip, i, BDPU));
+ }
+}
+
+void __devinit lola_proc_debug_new(struct lola *chip)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(chip->card, "codec", &entry))
+ snd_info_set_text_ops(entry, chip, lola_proc_codec_read);
+ if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) {
+ snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read);
+ entry->mode |= S_IWUSR;
+ entry->c.text.write = lola_proc_codec_rw_write;
+ }
+ if (!snd_card_proc_new(chip->card, "regs", &entry))
+ snd_info_set_text_ops(entry, chip, lola_proc_regs_read);
+}
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 961d98297695..9cea84c3e0c6 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1000,7 +1000,7 @@ static void device_change_handler(struct work_struct *work)
chip->lineout_sw_ctl);
if (mix->anded_reset)
msleep(10);
- check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify,
+ check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify,
chip->speaker_sw_ctl);
} else {
/* unmute speaker, mute others */
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index af3c73053ee4..28afbbf69ce0 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -184,7 +184,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
.codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
.platform_name = "atmel-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.ops = &at91sam9g20ek_ops,
};
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index cb99f04abe88..1d3e258c9ea8 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -77,7 +77,7 @@ static struct snd_soc_dai_link db1200_i2s_dai = {
.codec_dai_name = "wm8731-hifi",
.cpu_dai_name = "au1xpsc_i2s.1",
.platform_name = "au1xpsc-pcm.1",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.ops = &db1200_i2s_wm8731_ops,
};
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 5a2fd8abaefa..98b44b316e78 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -243,6 +243,9 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
@@ -314,6 +317,9 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max
@@ -377,6 +383,9 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
struct snd_dma_buffer *buf;
int stream;
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
sizeof(struct ac97_frame) / 4;
#endif
@@ -405,8 +414,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
#endif
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@@ -458,7 +465,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bf5xx_pcm_driver = {
.driver = {
- .name = "bf5xx-pcm-audio",
+ .name = "bfin-ac97-pcm-audio",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index ffbac26b9bce..6d2162590889 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -41,48 +41,7 @@
* anomaly does not affect blackfin sound drivers.
*/
-static int *cmd_count;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-#define SPORT_REQ(x) \
- [x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
- P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
-static u16 sport_req[][7] = {
-#ifdef SPORT0_TCR1
- SPORT_REQ(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_REQ(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_REQ(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_REQ(3),
-#endif
-};
-
-#define SPORT_PARAMS(x) \
- [x] = { \
- .dma_rx_chan = CH_SPORT##x##_RX, \
- .dma_tx_chan = CH_SPORT##x##_TX, \
- .err_irq = IRQ_SPORT##x##_ERROR, \
- .regs = (struct sport_register *)SPORT##x##_TCR1, \
- }
-static struct sport_param sport_params[4] = {
-#ifdef SPORT0_TCR1
- SPORT_PARAMS(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_PARAMS(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_PARAMS(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_PARAMS(3),
-#endif
-};
+static struct sport_device *ac97_sport_handle;
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
size_t count, unsigned int chan_mask)
@@ -140,7 +99,8 @@ static unsigned int sport_tx_curr_frag(struct sport_device *sport)
static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
{
- struct sport_device *sport = sport_handle;
+ struct sport_device *sport = ac97_sport_handle;
+ int *cmd_count = sport->private_data;
int nextfrag = sport_tx_curr_frag(sport);
struct ac97_frame *nextwrite;
@@ -161,6 +121,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
+ struct sport_device *sport_handle = ac97_sport_handle;
struct ac97_frame out_frame[2], in_frame[2];
pr_debug("%s enter 0x%x\n", __func__, reg);
@@ -185,6 +146,8 @@ static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
+ struct sport_device *sport_handle = ac97_sport_handle;
+
pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
if (sport_handle->tx_run) {
@@ -203,28 +166,19 @@ void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
{
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
- (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
-
-#define CONCAT(a, b, c) a ## b ## c
-#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
-
- u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
- u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
+ struct sport_device *sport_handle = ac97_sport_handle;
+ u16 gpio = P_IDENT(sport_handle->pin_req[3]);
pr_debug("%s enter\n", __func__);
- peripheral_free(per);
+ peripheral_free_list(sport_handle->pin_req);
gpio_request(gpio, "bf5xx-ac97");
gpio_direction_output(gpio, 1);
udelay(2);
gpio_set_value(gpio, 0);
udelay(1);
gpio_free(gpio);
- peripheral_request(per, "soc-audio");
-#else
- pr_info("%s: Not implemented\n", __func__);
-#endif
+ peripheral_request_list(sport_handle->pin_req, "soc-audio");
}
static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
@@ -306,18 +260,32 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
#define bf5xx_ac97_resume NULL
#endif
-static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
+static struct snd_soc_dai_driver bfin_ac97_dai = {
+ .ac97_control = 1,
+ .suspend = bf5xx_ac97_suspend,
+ .resume = bf5xx_ac97_resume,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+ .channels_max = 6,
+#else
+ .channels_max = 2,
+#endif
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+
+static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev)
{
- int ret = 0;
- cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
- if (cmd_count == NULL)
- return -ENOMEM;
-
- if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- ret = -EFAULT;
- goto peripheral_err;
- }
+ struct sport_device *sport_handle;
+ int ret;
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */
@@ -329,12 +297,14 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
}
gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
#endif
- sport_handle = sport_init(&sport_params[sport_num], 2, \
- sizeof(struct ac97_frame), NULL);
+
+ sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
+ PAGE_SIZE);
if (!sport_handle) {
ret = -ENODEV;
goto sport_err;
}
+
/*SPORT works in TDM mode to simulate AC97 transfers*/
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
@@ -361,67 +331,37 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
goto sport_config_err;
}
+ ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ goto sport_config_err;
+ }
+
+ ac97_sport_handle = sport_handle;
+
return 0;
sport_config_err:
- kfree(sport_handle);
+ sport_done(sport_handle);
sport_err:
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
gpio_err:
#endif
- peripheral_free_list(sport_req[sport_num]);
-peripheral_err:
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
return ret;
}
-static int bf5xx_ac97_remove(struct snd_soc_dai *dai)
+static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
- peripheral_free_list(sport_req[sport_num]);
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
- return 0;
-}
-
-struct snd_soc_dai_driver bfin_ac97_dai = {
- .ac97_control = 1,
- .probe = bf5xx_ac97_probe,
- .remove = bf5xx_ac97_remove,
- .suspend = bf5xx_ac97_suspend,
- .resume = bf5xx_ac97_resume,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- .channels_max = 6,
-#else
- .channels_max = 2,
-#endif
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
-};
-EXPORT_SYMBOL_GPL(bfin_ac97_dai);
-
-static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
-{
- return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
-}
-static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_dai(&pdev->dev);
return 0;
}
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index 83012da9dfc2..ea4951cf5526 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -29,22 +29,12 @@
#include <asm/portmux.h>
#include "../codecs/ad1836.h"
-#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1836;
-static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -75,23 +65,33 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ad1836_ops = {
- .startup = bf5xx_ad1836_startup,
.hw_params = bf5xx_ad1836_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad1836_dai = {
- .name = "ad1836",
- .stream_name = "AD1836",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name = "ad1836-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad1836-codec.0",
- .ops = &bf5xx_ad1836_ops,
+static struct snd_soc_dai_link bf5xx_ad1836_dai[] = {
+ {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .cpu_dai_name = "bfin-tdm.0",
+ .codec_dai_name = "ad1836-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad1836.0",
+ .ops = &bf5xx_ad1836_ops,
+ },
+ {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .cpu_dai_name = "bfin-tdm.1",
+ .codec_dai_name = "ad1836-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad1836.0",
+ .ops = &bf5xx_ad1836_ops,
+ },
};
static struct snd_soc_card bf5xx_ad1836 = {
- .name = "bf5xx_ad1836",
- .dai_link = &bf5xx_ad1836_dai,
+ .name = "bfin-ad1836",
+ .dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c
index d3ccb926b5e4..d6651c033cb7 100644
--- a/sound/soc/blackfin/bf5xx-ad193x.c
+++ b/sound/soc/blackfin/bf5xx-ad193x.c
@@ -38,30 +38,28 @@
#include <asm/portmux.h>
#include "../codecs/ad193x.h"
-#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad193x;
-static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int clk = 0;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
+
+ switch (params_rate(params)) {
+ case 48000:
+ clk = 12288000;
+ break;
+ }
+
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
@@ -74,6 +72,12 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
/* set codec DAI slots, 8 channels, all channels are enabled */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
@@ -89,23 +93,33 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ad193x_ops = {
- .startup = bf5xx_ad193x_startup,
.hw_params = bf5xx_ad193x_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad193x_dai = {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad193x-codec.5",
- .ops = &bf5xx_ad193x_ops,
+static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
+ {
+ .name = "ad193x",
+ .stream_name = "AD193X",
+ .cpu_dai_name = "bfin-tdm.0",
+ .codec_dai_name ="ad193x-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad193x.5",
+ .ops = &bf5xx_ad193x_ops,
+ },
+ {
+ .name = "ad193x",
+ .stream_name = "AD193X",
+ .cpu_dai_name = "bfin-tdm.1",
+ .codec_dai_name ="ad193x-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad193x.5",
+ .ops = &bf5xx_ad193x_ops,
+ },
};
static struct snd_soc_card bf5xx_ad193x = {
- .name = "bf5xx_ad193x",
- .dai_link = &bf5xx_ad193x_dai,
+ .name = "bfin-ad193x",
+ .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
index d57c9c9c9883..06a84b211b52 100644
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -47,39 +47,34 @@
#include <asm/portmux.h>
#include "../codecs/ad1980.h"
-#include "bf5xx-sport.h"
+
#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;
-static int bf5xx_board_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
-static struct snd_soc_ops bf5xx_board_ops = {
- .startup = bf5xx_board_startup,
-};
-
-static struct snd_soc_dai_link bf5xx_board_dai = {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "bfin-ac97",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad1980-codec",
- .ops = &bf5xx_board_ops,
+static struct snd_soc_dai_link bf5xx_board_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai_name = "bfin-ac97.0",
+ .codec_dai_name = "ad1980-hifi",
+ .platform_name = "bfin-ac97-pcm-audio",
+ .codec_name = "ad1980",
+ },
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai_name = "bfin-ac97.1",
+ .codec_dai_name = "ad1980-hifi",
+ .platform_name = "bfin-ac97-pcm-audio",
+ .codec_name = "ad1980",
+ },
};
static struct snd_soc_card bf5xx_board = {
- .name = "bf5xx-board",
- .dai_link = &bf5xx_board_dai,
+ .name = "bfin-ad1980",
+ .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
index 732fb8bad076..732a247f2527 100644
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -145,16 +145,6 @@ static int bf5xx_probe(struct platform_device *pdev)
return 0;
}
-static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -176,24 +166,34 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
static struct snd_soc_ops bf5xx_ad73311_ops = {
- .startup = bf5xx_ad73311_startup,
.hw_params = bf5xx_ad73311_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad73311_dai = {
- .name = "ad73311",
- .stream_name = "AD73311",
- .cpu_dai_name = "bf5xx-i2s",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad73311-codec",
- .ops = &bf5xx_ad73311_ops,
+static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
+ {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "ad73311-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ad73311",
+ .ops = &bf5xx_ad73311_ops,
+ },
+ {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai_name = "bfin-i2s.1",
+ .codec_dai_name = "ad73311-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ad73311",
+ .ops = &bf5xx_ad73311_ops,
+ },
};
static struct snd_soc_card bf5xx_ad73311 = {
- .name = "bf5xx_ad73311",
+ .name = "bfin-ad73311",
.probe = bf5xx_probe,
- .dai_link = &bf5xx_ad73311_dai,
+ .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 890a0dccf902..b5101efd1c87 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -148,10 +148,15 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret;
pr_debug("%s enter\n", __func__);
+
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime, \
@@ -159,9 +164,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
- if (sport_handle != NULL)
+ if (sport_handle != NULL) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
runtime->private_data = sport_handle;
- else {
+ } else {
pr_err("sport_handle is NULL\n");
return -1;
}
@@ -214,11 +224,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
buf->area, buf->bytes);
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
return 0;
}
@@ -239,8 +244,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@@ -292,7 +295,7 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_i2s_pcm_driver = {
.driver = {
- .name = "bfin-pcm-audio",
+ .name = "bfin-i2s-pcm-audio",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index d453b1e9d607..00cc3e00b2fe 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -51,59 +51,24 @@ struct bf5xx_i2s_port {
int configured;
};
-static struct bf5xx_i2s_port bf5xx_i2s;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-static struct sport_param sport_params[2] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
-
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
/* interface format:support I2S,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- bf5xx_i2s.tcr1 |= TFSR | TCKFE;
- bf5xx_i2s.rcr1 |= RFSR | RCKFE;
- bf5xx_i2s.tcr2 |= TSFSE;
- bf5xx_i2s.rcr2 |= RSFSE;
+ bf5xx_i2s->tcr1 |= TFSR | TCKFE;
+ bf5xx_i2s->rcr1 |= RFSR | RCKFE;
+ bf5xx_i2s->tcr2 |= TSFSE;
+ bf5xx_i2s->rcr2 |= RSFSE;
break;
case SND_SOC_DAIFMT_DSP_A:
- bf5xx_i2s.tcr1 |= TFSR;
- bf5xx_i2s.rcr1 |= RFSR;
+ bf5xx_i2s->tcr1 |= TFSR;
+ bf5xx_i2s->rcr1 |= RFSR;
break;
case SND_SOC_DAIFMT_LEFT_J:
ret = -EINVAL;
@@ -135,29 +100,35 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
- bf5xx_i2s.tcr2 &= ~0x1f;
- bf5xx_i2s.rcr2 &= ~0x1f;
+ bf5xx_i2s->tcr2 &= ~0x1f;
+ bf5xx_i2s->rcr2 &= ~0x1f;
switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ bf5xx_i2s->tcr2 |= 7;
+ bf5xx_i2s->rcr2 |= 7;
+ sport_handle->wdsize = 1;
case SNDRV_PCM_FORMAT_S16_LE:
- bf5xx_i2s.tcr2 |= 15;
- bf5xx_i2s.rcr2 |= 15;
+ bf5xx_i2s->tcr2 |= 15;
+ bf5xx_i2s->rcr2 |= 15;
sport_handle->wdsize = 2;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- bf5xx_i2s.tcr2 |= 23;
- bf5xx_i2s.rcr2 |= 23;
+ bf5xx_i2s->tcr2 |= 23;
+ bf5xx_i2s->rcr2 |= 23;
sport_handle->wdsize = 3;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_i2s.tcr2 |= 31;
- bf5xx_i2s.rcr2 |= 31;
+ bf5xx_i2s->tcr2 |= 31;
+ bf5xx_i2s->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
}
- if (!bf5xx_i2s.configured) {
+ if (!bf5xx_i2s->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
@@ -166,16 +137,16 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
*
* CPU DAI:slave mode.
*/
- bf5xx_i2s.configured = 1;
- ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
- bf5xx_i2s.rcr2, 0, 0);
+ bf5xx_i2s->configured = 1;
+ ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
+ bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
- bf5xx_i2s.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
+ bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
@@ -188,41 +159,19 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+
pr_debug("%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
- bf5xx_i2s.configured = 0;
-}
-
-static int bf5xx_i2s_probe(struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
- }
-
- /* request DMA for SPORT */
- sport_handle = sport_init(&sport_params[sport_num], 4, \
- 2 * sizeof(u32), NULL);
- if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int bf5xx_i2s_remove(struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- peripheral_free_list(&sport_req[sport_num][0]);
- return 0;
+ bf5xx_i2s->configured = 0;
}
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id);
@@ -235,19 +184,21 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret;
pr_debug("%s : sport %d\n", __func__, dai->id);
- ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
- bf5xx_i2s.rcr2, 0, 0);
+ ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
+ bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
- bf5xx_i2s.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
+ bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
@@ -266,8 +217,11 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000)
-#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
- SNDRV_PCM_FMTBIT_S32_LE)
+#define BF5XX_I2S_FORMATS \
+ (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.shutdown = bf5xx_i2s_shutdown,
@@ -276,8 +230,6 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
};
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
- .probe = bf5xx_i2s_probe,
- .remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
@@ -293,23 +245,45 @@ static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.ops = &bf5xx_i2s_dai_ops,
};
-static int bfin_i2s_drv_probe(struct platform_device *pdev)
+static int __devinit bf5xx_i2s_probe(struct platform_device *pdev)
{
- return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
+ struct sport_device *sport_handle;
+ int ret;
+
+ /* configure SPORT for I2S */
+ sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
+ sizeof(struct bf5xx_i2s_port));
+ if (!sport_handle)
+ return -ENODEV;
+
+ /* register with the ASoC layers */
+ ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ sport_done(sport_handle);
+ return ret;
+ }
+
+ return 0;
}
-static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev)
+static int __devexit bf5xx_i2s_remove(struct platform_device *pdev)
{
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
+ pr_debug("%s enter\n", __func__);
+
snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
+
return 0;
}
static struct platform_driver bfin_i2s_driver = {
- .probe = bfin_i2s_drv_probe,
- .remove = __devexit_p(bfin_i2s_drv_remove),
-
+ .probe = bf5xx_i2s_probe,
+ .remove = __devexit_p(bf5xx_i2s_remove),
.driver = {
- .name = "bf5xx-i2s",
+ .name = "bfin-i2s",
.owner = THIS_MODULE,
},
};
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
index 99051ff0954e..a2d40349fcc4 100644
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -42,8 +42,6 @@
/* delay between frame sync pulse and first data bit in multichannel mode */
#define FRAME_DELAY (1<<12)
-struct sport_device *sport_handle;
-EXPORT_SYMBOL(sport_handle);
/* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport,
@@ -798,86 +796,164 @@ int sport_set_err_callback(struct sport_device *sport,
}
EXPORT_SYMBOL(sport_set_err_callback);
-struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
- unsigned dummy_count, void *private_data)
+static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
{
- int ret;
+ /* Extract settings from platform data */
+ struct device *dev = &pdev->dev;
+ struct bfin_snd_platform_data *pdata = dev->platform_data;
+ struct resource *res;
+
+ param->num = pdev->id;
+
+ if (!pdata) {
+ dev_err(dev, "no platform_data\n");
+ return -ENODEV;
+ }
+ param->pin_req = pdata->pin_req;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no MEM resource\n");
+ return -ENODEV;
+ }
+ param->regs = (struct sport_register *)res->start;
+
+ /* first RX, then TX */
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(dev, "no rx DMA resource\n");
+ return -ENODEV;
+ }
+ param->dma_rx_chan = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!res) {
+ dev_err(dev, "no tx DMA resource\n");
+ return -ENODEV;
+ }
+ param->dma_tx_chan = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no irq resource\n");
+ return -ENODEV;
+ }
+ param->err_irq = res->start;
+
+ return 0;
+}
+
+struct sport_device *sport_init(struct platform_device *pdev,
+ unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
+{
+ struct device *dev = &pdev->dev;
+ struct sport_param param;
struct sport_device *sport;
- pr_debug("%s enter\n", __func__);
- BUG_ON(param == NULL);
- BUG_ON(wdsize == 0 || dummy_count == 0);
- sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
- if (!sport) {
- pr_err("Failed to allocate for sport device\n");
+ int ret;
+
+ dev_dbg(dev, "%s enter\n", __func__);
+
+ param.wdsize = wdsize;
+ param.dummy_count = dummy_count;
+ BUG_ON(param.wdsize == 0 || param.dummy_count == 0);
+
+ ret = sport_config_pdev(pdev, &param);
+ if (ret)
+ return NULL;
+
+ if (peripheral_request_list(param.pin_req, "soc-audio")) {
+ dev_err(dev, "requesting Peripherals failed\n");
return NULL;
}
- memset(sport, 0, sizeof(struct sport_device));
- sport->dma_rx_chan = param->dma_rx_chan;
- sport->dma_tx_chan = param->dma_tx_chan;
- sport->err_irq = param->err_irq;
- sport->regs = param->regs;
- sport->private_data = private_data;
+ sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+ if (!sport) {
+ dev_err(dev, "failed to allocate for sport device\n");
+ goto __init_err0;
+ }
+
+ sport->num = param.num;
+ sport->dma_rx_chan = param.dma_rx_chan;
+ sport->dma_tx_chan = param.dma_tx_chan;
+ sport->err_irq = param.err_irq;
+ sport->regs = param.regs;
+ sport->pin_req = param.pin_req;
if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
- pr_err("Failed to request RX dma %d\n", \
- sport->dma_rx_chan);
+ dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
goto __init_err1;
}
if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
- pr_err("Failed to request RX irq %d\n", \
- sport->dma_rx_chan);
+ dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
goto __init_err2;
}
if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
- pr_err("Failed to request TX dma %d\n", \
- sport->dma_tx_chan);
+ dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
goto __init_err2;
}
if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
- pr_err("Failed to request TX irq %d\n", \
- sport->dma_tx_chan);
+ dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
goto __init_err3;
}
if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
sport) < 0) {
- pr_err("Failed to request err irq:%d\n", \
- sport->err_irq);
+ dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
goto __init_err3;
}
- pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
+ dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n",
sport->dma_rx_chan, sport->dma_tx_chan,
sport->err_irq, sport->regs);
- sport->wdsize = wdsize;
- sport->dummy_count = dummy_count;
+ sport->wdsize = param.wdsize;
+ sport->dummy_count = param.dummy_count;
+
+ sport->private_data = kzalloc(priv_size, GFP_KERNEL);
+ if (!sport->private_data) {
+ dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
+ goto __init_err4;
+ }
if (L1_DATA_A_LENGTH)
- sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
+ sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
else
- sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
+ sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
if (sport->dummy_buf == NULL) {
- pr_err("Failed to allocate dummy buffer\n");
- goto __error;
+ dev_err(dev, "failed to allocate dummy buffer\n");
+ goto __error1;
}
ret = sport_config_rx_dummy(sport);
if (ret) {
- pr_err("Failed to config rx dummy ring\n");
- goto __error;
+ dev_err(dev, "failed to config rx dummy ring\n");
+ goto __error2;
}
ret = sport_config_tx_dummy(sport);
if (ret) {
- pr_err("Failed to config tx dummy ring\n");
- goto __error;
+ dev_err(dev, "failed to config tx dummy ring\n");
+ goto __error3;
}
+ platform_set_drvdata(pdev, sport);
+
return sport;
-__error:
+__error3:
+ if (L1_DATA_A_LENGTH)
+ l1_data_sram_free(sport->dummy_rx_desc);
+ else
+ dma_free_coherent(NULL, 2*sizeof(struct dmasg),
+ sport->dummy_rx_desc, 0);
+__error2:
+ if (L1_DATA_A_LENGTH)
+ l1_data_sram_free(sport->dummy_buf);
+ else
+ kfree(sport->dummy_buf);
+__error1:
+ kfree(sport->private_data);
+__init_err4:
free_irq(sport->err_irq, sport);
__init_err3:
free_dma(sport->dma_tx_chan);
@@ -885,6 +961,8 @@ __init_err2:
free_dma(sport->dma_rx_chan);
__init_err1:
kfree(sport);
+__init_err0:
+ peripheral_free_list(param.pin_req);
return NULL;
}
EXPORT_SYMBOL(sport_init);
@@ -917,8 +995,9 @@ void sport_done(struct sport_device *sport)
free_dma(sport->dma_tx_chan);
free_irq(sport->err_irq, sport);
+ kfree(sport->private_data);
+ peripheral_free_list(sport->pin_req);
kfree(sport);
- sport = NULL;
}
EXPORT_SYMBOL(sport_done);
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
index a86e8cc0b2d3..5ab60bd613ea 100644
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -1,5 +1,5 @@
/*
- * File: bf5xx_ac97_sport.h
+ * File: bf5xx_sport.h
* Based on:
* Author: Roy Huang <roy.huang@analog.com>
*
@@ -33,15 +33,18 @@
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <linux/platform_device.h>
#include <asm/dma.h>
#include <asm/bfin_sport.h>
#define DESC_ELEMENT_COUNT 9
struct sport_device {
+ int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
+ const unsigned short *pin_req;
struct sport_register *regs;
unsigned char *rx_buf;
@@ -103,17 +106,20 @@ struct sport_device {
void *private_data;
};
-extern struct sport_device *sport_handle;
-
struct sport_param {
+ int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
+ const unsigned short *pin_req;
struct sport_register *regs;
+ unsigned int wdsize;
+ unsigned int dummy_count;
+ void *private_data;
};
-struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
- unsigned dummy_count, void *private_data);
+struct sport_device *sport_init(struct platform_device *pdev,
+ unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
void sport_done(struct sport_device *sport);
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index ad28663f5bbd..767e772a815d 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -44,16 +44,6 @@
static struct snd_soc_card bf5xx_ssm2602;
-static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -109,23 +99,33 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ssm2602_ops = {
- .startup = bf5xx_ssm2602_startup,
.hw_params = bf5xx_ssm2602_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
- .name = "ssm2602",
- .stream_name = "SSM2602",
- .cpu_dai_name = "bf5xx-i2s",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bf5xx-pcm-audio",
- .codec_name = "ssm2602-codec.0-001b",
- .ops = &bf5xx_ssm2602_ops,
+static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
+ {
+ .name = "ssm2602",
+ .stream_name = "SSM2602",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "ssm2602-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ssm2602.0-001b",
+ .ops = &bf5xx_ssm2602_ops,
+ },
+ {
+ .name = "ssm2602",
+ .stream_name = "SSM2602",
+ .cpu_dai_name = "bfin-i2s.1",
+ .codec_dai_name = "ssm2602-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ssm2602.0-001b",
+ .ops = &bf5xx_ssm2602_ops,
+ },
};
static struct snd_soc_card bf5xx_ssm2602 = {
- .name = "bf5xx_ssm2602",
- .dai_link = &bf5xx_ssm2602_dai,
+ .name = "bfin-ssm2602",
+ .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index 74cf759b78a6..07cfc7a9e49a 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -154,7 +154,12 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
int ret = 0;
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
@@ -164,9 +169,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
- if (sport_handle != NULL)
+ if (sport_handle != NULL) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
runtime->private_data = sport_handle;
- else {
+ } else {
pr_err("sport_handle is NULL\n");
ret = -ENODEV;
}
@@ -249,11 +259,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
}
buf->bytes = size;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
return 0;
}
@@ -274,8 +279,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@@ -326,7 +329,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_tdm_driver = {
.driver = {
- .name = "bf5xx-tdm-pcm-audio",
+ .name = "bfin-tdm-pcm-audio",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
index 5515ac9e05c7..a822d1ee1380 100644
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -46,43 +46,6 @@
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
-static struct bf5xx_tdm_port bf5xx_tdm;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-static struct sport_param sport_params[2] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
-
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
@@ -119,14 +82,16 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int ret = 0;
- bf5xx_tdm.tcr2 &= ~0x1f;
- bf5xx_tdm.rcr2 &= ~0x1f;
+ bf5xx_tdm->tcr2 &= ~0x1f;
+ bf5xx_tdm->rcr2 &= ~0x1f;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_tdm.tcr2 |= 31;
- bf5xx_tdm.rcr2 |= 31;
+ bf5xx_tdm->tcr2 |= 31;
+ bf5xx_tdm->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
/* at present, we only support 32bit transfer */
@@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
break;
}
- if (!bf5xx_tdm.configured) {
+ if (!bf5xx_tdm->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
@@ -145,21 +110,21 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
*
* CPU DAI:slave mode.
*/
- ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
- bf5xx_tdm.rcr2, 0, 0);
+ ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
+ bf5xx_tdm->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
- bf5xx_tdm.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
+ bf5xx_tdm->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- bf5xx_tdm.configured = 1;
+ bf5xx_tdm->configured = 1;
}
return 0;
@@ -168,15 +133,20 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
+
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
- bf5xx_tdm.configured = 0;
+ bf5xx_tdm->configured = 0;
}
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int i;
unsigned int slot;
unsigned int tx_mapped = 0, rx_mapped = 0;
@@ -189,7 +159,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) {
- bf5xx_tdm.tx_map[i] = slot;
+ bf5xx_tdm->tx_map[i] = slot;
tx_mapped |= 1 << slot;
} else
return -EINVAL;
@@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) {
- bf5xx_tdm.rx_map[i] = slot;
+ bf5xx_tdm->rx_map[i] = slot;
rx_mapped |= 1 << slot;
} else
return -EINVAL;
@@ -212,12 +182,14 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- if (!dai->active)
- return 0;
- if (dai->capture_active)
- sport_rx_stop(sport);
if (dai->playback_active)
sport_tx_stop(sport);
+ if (dai->capture_active)
+ sport_rx_stop(sport);
+
+ /* isolate sync/clock pins from codec while sports resume */
+ peripheral_free_list(sport->pin_req);
+
return 0;
}
@@ -226,9 +198,6 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
int ret;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- if (!dai->active)
- return 0;
-
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
@@ -247,6 +216,8 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
ret = -EBUSY;
}
+ peripheral_request_list(sport->pin_req, "soc-audio");
+
return 0;
}
@@ -280,20 +251,14 @@ static struct snd_soc_dai_driver bf5xx_tdm_dai = {
static int __devinit bfin_tdm_probe(struct platform_device *pdev)
{
- int ret = 0;
-
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
- }
+ struct sport_device *sport_handle;
+ int ret;
- /* request DMA for SPORT */
- sport_handle = sport_init(&sport_params[sport_num], 4, \
- 8 * sizeof(u32), NULL);
- if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
+ /* configure SPORT for TDM */
+ sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
+ sizeof(struct bf5xx_tdm_port));
+ if (!sport_handle)
return -ENODEV;
- }
/* SPORT works in TDM mode */
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
@@ -323,18 +288,19 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
goto sport_config_err;
}
- sport_handle->private_data = &bf5xx_tdm;
return 0;
sport_config_err:
- peripheral_free_list(&sport_req[sport_num][0]);
+ sport_done(sport_handle);
return ret;
}
static int __devexit bfin_tdm_remove(struct platform_device *pdev)
{
- peripheral_free_list(&sport_req[sport_num][0]);
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
return 0;
}
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 06b6981b8d6d..19241576b6b5 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -120,7 +120,7 @@
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = 0, .invert = 0, .kcontrols = NULL, \
+ .shift = 0, .invert = 0, .kcontrol_news = NULL, \
.num_kcontrols = 0, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 6943e24a74a1..98175a096df2 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -16,10 +16,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+ select SND_SOC_AD73311
select SND_SOC_ADS117X
- select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
+ select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
select SND_SOC_ALC5623 if I2C
@@ -33,13 +34,14 @@ config SND_SOC_ALL_CODECS
select SND_SOC_JZ4740_CODEC if SOC_JZ4740
select SND_SOC_LM4857 if I2C
select SND_SOC_MAX98088 if I2C
+ select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
- select SND_SOC_SSM2602 if I2C
+ select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
@@ -52,6 +54,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WL1273 if MFD_WL1273_CORE
+ select SND_SOC_WM1250_EV1 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
@@ -72,6 +75,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
+ select SND_SOC_WM8915 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C
@@ -136,6 +140,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535
tristate
+config SND_SOC_AK4641
+ tristate
+
config SND_SOC_AK4642
tristate
@@ -187,6 +194,9 @@ config SND_SOC_DMIC
config SND_SOC_MAX98088
tristate
+config SND_SOC_MAX98095
+ tristate
+
config SND_SOC_MAX9850
tristate
@@ -241,6 +251,9 @@ config SND_SOC_UDA1380
config SND_SOC_WL1273
tristate
+config SND_SOC_WM1250_EV1
+ tristate
+
config SND_SOC_WM8350
tristate
@@ -298,6 +311,9 @@ config SND_SOC_WM8903
config SND_SOC_WM8904
tristate
+config SND_SOC_WM8915
+ tristate
+
config SND_SOC_WM8940
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 379bc55f0723..fd8558406ef0 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -7,6 +7,7 @@ snd-soc-ad73311-objs := ad73311.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o
@@ -19,6 +20,7 @@ snd-soc-dfbmcs320-objs := dfbmcs320.o
snd-soc-dmic-objs := dmic.o
snd-soc-l3-objs := l3.o
snd-soc-max98088-objs := max98088.o
+snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-sgtl5000-objs := sgtl5000.o
@@ -37,6 +39,7 @@ snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
@@ -56,6 +59,7 @@ snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
+snd-soc-wm8915-objs := wm8915.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
@@ -69,7 +73,7 @@ snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
-snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
+snd-soc-wm8994-objs := wm8994.o wm8994-tables.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
@@ -94,6 +98,7 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
@@ -108,6 +113,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
+obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
@@ -125,6 +131,7 @@ obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
@@ -144,6 +151,7 @@ obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
+obj-$(CONFIG_SND_SOC_WM8915) += snd-soc-wm8915.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index da46479bfcfa..2374ca5ffe68 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -23,8 +23,7 @@
/* codec private data */
struct ad193x_priv {
- enum snd_soc_control_type bus_type;
- void *control_data;
+ enum snd_soc_control_type control_type;
int sysclk;
};
@@ -354,14 +353,12 @@ static int ad193x_probe(struct snd_soc_codec *codec)
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
- codec->control_data = ad193x->control_data;
- if (ad193x->bus_type == SND_SOC_I2C)
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type);
+ if (ad193x->control_type == SND_SOC_I2C)
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
else
- ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->bus_type);
+ ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type);
if (ret < 0) {
- dev_err(codec->dev, "failed to set cache I/O: %d\n",
- ret);
+ dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
return ret;
}
@@ -408,8 +405,7 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, ad193x);
- ad193x->control_data = spi;
- ad193x->bus_type = SND_SOC_SPI;
+ ad193x->control_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
@@ -427,7 +423,7 @@ static int __devexit ad193x_spi_remove(struct spi_device *spi)
static struct spi_driver ad193x_spi_driver = {
.driver = {
- .name = "ad193x-codec",
+ .name = "ad193x",
.owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
@@ -454,8 +450,7 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, ad193x);
- ad193x->control_data = client;
- ad193x->bus_type = SND_SOC_I2C;
+ ad193x->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&client->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
@@ -473,7 +468,7 @@ static int __devexit ad193x_i2c_remove(struct i2c_client *client)
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
- .name = "ad193x-codec",
+ .name = "ad193x",
},
.probe = ad193x_i2c_probe,
.remove = __devexit_p(ad193x_i2c_remove),
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 34cb51ef2156..923b364a3e41 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -266,7 +266,7 @@ static int __devexit ad1980_remove(struct platform_device *pdev)
static struct platform_driver ad1980_codec_driver = {
.driver = {
- .name = "ad1980-codec",
+ .name = "ad1980",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index de799cd1ba72..8d793e993e9a 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -55,7 +55,7 @@ static int __devexit ad73311_remove(struct platform_device *pdev)
static struct platform_driver ad73311_codec_driver = {
.driver = {
- .name = "ad73311-codec",
+ .name = "ad73311",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 8b38739c88f8..e1a214ee757f 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -230,7 +230,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AIN"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route ak4535_audio_map[] = {
/*stereo mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
@@ -287,17 +287,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Input Mixer", "Aux Capture Switch", "Aux In"},
};
-static int ak4535_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ak4535_dapm_widgets,
- ARRAY_SIZE(ak4535_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -457,8 +446,6 @@ static int ak4535_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
- ak4535_add_widgets(codec);
-
return 0;
}
@@ -480,6 +467,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
.reg_cache_size = ARRAY_SIZE(ak4535_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4535_reg,
+ .dapm_widgets = ak4535_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
+ .dapm_routes = ak4535_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
new file mode 100644
index 000000000000..ed96f247c2da
--- /dev/null
+++ b/sound/soc/codecs/ak4641.c
@@ -0,0 +1,664 @@
+/*
+ * ak4641.c -- AK4641 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
+ * Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
+ *
+ * Based on ak4535.c by Richard Purdie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/ak4641.h>
+
+#include "ak4641.h"
+
+/* codec private data */
+struct ak4641_priv {
+ struct snd_soc_codec *codec;
+ unsigned int sysclk;
+ int deemph;
+ int playback_fs;
+};
+
+/*
+ * ak4641 register cache
+ */
+static const u8 ak4641_reg[AK4641_CACHEREGNUM] = {
+ 0x00, 0x80, 0x00, 0x80,
+ 0x02, 0x00, 0x11, 0x05,
+ 0x00, 0x00, 0x36, 0x10,
+ 0x00, 0x00, 0x57, 0x00,
+ 0x88, 0x88, 0x08, 0x08
+};
+
+static const int deemph_settings[] = {44100, 0, 48000, 32000};
+
+static int ak4641_set_deemph(struct snd_soc_codec *codec)
+{
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int i, best = 0;
+
+ for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
+ /* if deemphasis is on, select the nearest available rate */
+ if (ak4641->deemph && deemph_settings[i] != 0 &&
+ abs(deemph_settings[i] - ak4641->playback_fs) <
+ abs(deemph_settings[best] - ak4641->playback_fs))
+ best = i;
+
+ if (!ak4641->deemph && deemph_settings[i] == 0)
+ best = i;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", best);
+
+ return snd_soc_update_bits(codec, AK4641_DAC, 0x3, best);
+}
+
+static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.enumerated.item[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ ak4641->deemph = deemph;
+
+ return ak4641_set_deemph(codec);
+}
+
+static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4641->deemph;
+ return 0;
+};
+
+static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4641_mic_select[] = {"Internal", "External"};
+static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
+
+
+static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
+
+
+static const struct soc_enum ak4641_mono_out_enum =
+ SOC_ENUM_SINGLE(AK4641_SIG1, 6, 2, ak4641_mono_out);
+static const struct soc_enum ak4641_hp_out_enum =
+ SOC_ENUM_SINGLE(AK4641_MODE2, 2, 2, ak4641_hp_out);
+static const struct soc_enum ak4641_mic_select_enum =
+ SOC_ENUM_SINGLE(AK4641_MIC, 1, 2, ak4641_mic_select);
+static const struct soc_enum ak4641_mic_or_dac_enum =
+ SOC_ENUM_SINGLE(AK4641_BTIF, 4, 2, ak4641_mic_or_dac);
+
+static const struct snd_kcontrol_new ak4641_snd_controls[] = {
+ SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
+ SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
+ mono_gain_tlv),
+ SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
+ SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ ak4641_get_deemph, ak4641_put_deemph),
+
+ SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
+
+ SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
+
+ SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
+
+ SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
+ SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
+ SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
+
+ SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
+
+ SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
+ AK4641_RATT, 0, 255, 1, master_tlv),
+
+ SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
+
+ SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
+ SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
+};
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
+ mic_mono_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
+ mic_stereo_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
+};
+
+/* Mic mux */
+static const struct snd_kcontrol_new ak4641_mic_mux_control =
+ SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4641_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4641_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
+
+/* ak4641 dapm widgets */
+static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4641_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4641_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mixer_controls[0],
+ ARRAY_SIZE(ak4641_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_mic_mux_control),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mux_control),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono2_control),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
+ SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
+ SND_SOC_DAPM_ADC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route ak4641_audio_map[] = {
+ /* Stereo Mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* Mono 1 Mixer */
+ {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Mic Mux", "Internal", "Mic Int Bias"},
+ {"Mic Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Mic Mux"},
+
+ /* Input Mux */
+ {"Input Mux", "Microphone", "Mic"},
+ {"Input Mux", "Voice DAC", "Voice DAC"},
+
+ /* Line Out */
+ {"LOUT", NULL, "Line Out"},
+ {"ROUT", NULL, "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* Mono 1 Out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* Mono 2 Out */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Mono Out 2"},
+ {"Mono Out 2", NULL, "Stereo Mixer"},
+
+ {"Voice ADC", NULL, "Mono 2 Enable"},
+
+ /* Aux In */
+ {"AUX In", NULL, "AUX"},
+
+ /* ADC */
+ {"ADC", NULL, "Input Mixer"},
+ {"Input Mixer", "Mic Capture Switch", "Mic"},
+ {"Input Mixer", "Aux Capture Switch", "AUX In"},
+};
+
+static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+
+ ak4641->sysclk = freq;
+ return 0;
+}
+
+static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int rate = params_rate(params), fs = 256;
+ u8 mode2;
+
+ if (rate)
+ fs = ak4641->sysclk / rate;
+ else
+ return -EINVAL;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 = (0x2 << 5);
+ break;
+ case 512:
+ mode2 = (0x1 << 5);
+ break;
+ case 256:
+ mode2 = (0x0 << 5);
+ break;
+ default:
+ dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
+
+ /* Update de-emphasis filter for the new rate */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ak4641->playback_fs = rate;
+ ak4641_set_deemph(codec);
+ };
+
+ return 0;
+}
+
+static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 btif;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ btif = (0x3 << 5);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ btif = (0x2 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
+ btif = (0x0 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
+ btif = (0x1 << 5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
+}
+
+static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x02;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, AK4641_MODE1, mode1);
+}
+
+static int ak4641_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+}
+
+static int ak4641_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* unmute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* mute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 1);
+ mdelay(1);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 1);
+ mdelay(1);
+
+ ret = snd_soc_cache_sync(codec);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
+ snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ codec->cache_sync = 1;
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
+#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000)
+#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
+ .hw_params = ak4641_i2s_hw_params,
+ .set_fmt = ak4641_i2s_set_dai_fmt,
+ .digital_mute = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
+ .hw_params = NULL, /* rates are controlled by BT chip */
+ .set_fmt = ak4641_pcm_set_dai_fmt,
+ .digital_mute = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+struct snd_soc_dai_driver ak4641_dai[] = {
+{
+ .name = "ak4641-hifi",
+ .id = 1,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_i2s_dai_ops,
+ .symmetric_rates = 1,
+},
+{
+ .name = "ak4641-voice",
+ .id = 1,
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_pcm_dai_ops,
+ .symmetric_rates = 1,
+},
+};
+
+static int ak4641_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak4641_resume(struct snd_soc_codec *codec)
+{
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+
+static int ak4641_probe(struct snd_soc_codec *codec)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ int ret;
+
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request_one(pdata->gpio_power,
+ GPIOF_OUT_INIT_LOW, "ak4641 power");
+ if (ret)
+ goto err_out;
+ }
+ if (gpio_is_valid(pdata->gpio_npdn)) {
+ ret = gpio_request_one(pdata->gpio_npdn,
+ GPIOF_OUT_INIT_LOW, "ak4641 npdn");
+ if (ret)
+ goto err_gpio;
+
+ udelay(1); /* > 150 ns */
+ gpio_set_value(pdata->gpio_npdn, 1);
+ }
+ }
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err_register;
+ }
+
+ /* power on device */
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+
+err_register:
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+err_gpio:
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+err_out:
+ return ret;
+}
+
+static int ak4641_remove(struct snd_soc_codec *codec)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_power);
+ }
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+ return 0;
+}
+
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
+ .probe = ak4641_probe,
+ .remove = ak4641_remove,
+ .suspend = ak4641_suspend,
+ .resume = ak4641_resume,
+ .controls = ak4641_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4641_snd_controls),
+ .dapm_widgets = ak4641_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
+ .dapm_routes = ak4641_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
+ .set_bias_level = ak4641_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(ak4641_reg),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = ak4641_reg,
+ .reg_cache_step = 1,
+};
+
+
+static int __devinit ak4641_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ak4641_priv *ak4641;
+ int ret;
+
+ ak4641 = kzalloc(sizeof(struct ak4641_priv), GFP_KERNEL);
+ if (!ak4641)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ak4641);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
+ ak4641_dai, ARRAY_SIZE(ak4641_dai));
+ if (ret < 0)
+ kfree(ak4641);
+
+ return ret;
+}
+
+static int __devexit ak4641_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ kfree(i2c_get_clientdata(i2c));
+ return 0;
+}
+
+static const struct i2c_device_id ak4641_i2c_id[] = {
+ { "ak4641", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
+
+static struct i2c_driver ak4641_i2c_driver = {
+ .driver = {
+ .name = "ak4641",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4641_i2c_probe,
+ .remove = __devexit_p(ak4641_i2c_remove),
+ .id_table = ak4641_i2c_id,
+};
+
+static int __init ak4641_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&ak4641_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register AK4641 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(ak4641_modinit);
+
+static void __exit ak4641_exit(void)
+{
+ i2c_del_driver(&ak4641_i2c_driver);
+}
+module_exit(ak4641_exit);
+
+MODULE_DESCRIPTION("SoC AK4641 driver");
+MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4641.h b/sound/soc/codecs/ak4641.h
new file mode 100644
index 000000000000..4a263248efea
--- /dev/null
+++ b/sound/soc/codecs/ak4641.h
@@ -0,0 +1,47 @@
+/*
+ * ak4641.h -- AK4641 SoC Audio driver
+ *
+ * Copyright 2008 Harald Welte <laforge@gnufiish.org>
+ *
+ * Based on ak4535.h
+ *
+ * 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 _AK4641_H
+#define _AK4641_H
+
+/* AK4641 register space */
+
+#define AK4641_PM1 0x00
+#define AK4641_PM2 0x01
+#define AK4641_SIG1 0x02
+#define AK4641_SIG2 0x03
+#define AK4641_MODE1 0x04
+#define AK4641_MODE2 0x05
+#define AK4641_DAC 0x06
+#define AK4641_MIC 0x07
+#define AK4641_TIMER 0x08
+#define AK4641_ALC1 0x09
+#define AK4641_ALC2 0x0a
+#define AK4641_PGA 0x0b
+#define AK4641_LATT 0x0c
+#define AK4641_RATT 0x0d
+#define AK4641_VOL 0x0e
+#define AK4641_STATUS 0x0f
+#define AK4641_EQLO 0x10
+#define AK4641_EQMID 0x11
+#define AK4641_EQHI 0x12
+#define AK4641_BTIF 0x13
+
+#define AK4641_CACHEREGNUM 0x14
+
+
+
+#define AK4641_DAI_HIFI 0
+#define AK4641_DAI_VOICE 1
+
+
+#endif
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 2ec75abfa3e9..88b29f8c748b 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -352,7 +352,7 @@ static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route ak4671_intercon[] = {
{"DAC Left", "NULL", "PMPLL"},
{"DAC Right", "NULL", "PMPLL"},
{"ADC Left", "NULL", "PMPLL"},
@@ -433,17 +433,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
};
-static int ak4671_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ak4671_dapm_widgets,
- ARRAY_SIZE(ak4671_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -650,7 +639,6 @@ static int ak4671_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, ak4671_snd_controls,
ARRAY_SIZE(ak4671_snd_controls));
- ak4671_add_widgets(codec);
ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -670,6 +658,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
.reg_cache_size = AK4671_CACHEREGNUM,
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4671_reg,
+ .dapm_widgets = ak4671_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
+ .dapm_routes = ak4671_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
};
static int __devinit ak4671_i2c_probe(struct i2c_client *client,
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 0bb424af956f..d68ea532cc7f 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -86,18 +86,6 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"ADC", NULL, "Input Mixer"},
};
-static int cx20442_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, cx20442_dapm_widgets,
- ARRAY_SIZE(cx20442_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, cx20442_audio_map,
- ARRAY_SIZE(cx20442_audio_map));
-
- return 0;
-}
-
static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -344,8 +332,6 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, cx20442);
- cx20442_add_widgets(codec);
-
cx20442->control_data = NULL;
codec->hw_write = NULL;
codec->card->pop_time = 0;
@@ -377,6 +363,10 @@ static struct snd_soc_codec_driver cx20442_codec_dev = {
.reg_word_size = sizeof(u8),
.read = cx20442_read_reg_cache,
.write = cx20442_write,
+ .dapm_widgets = cx20442_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
+ .dapm_routes = cx20442_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
};
static int cx20442_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 57e9dac88d38..f9a87737ec16 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -39,7 +39,31 @@ static struct snd_soc_dai_driver dmic_dai = {
},
};
-static struct snd_soc_codec_driver soc_dmic = {};
+static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("DMIC AIF", "Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("DMic"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"DMIC AIF", NULL, "DMic"},
+};
+
+static int dmic_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
+ ARRAY_SIZE(dmic_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+ snd_soc_dapm_new_widgets(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_dmic = {
+ .probe = dmic_probe,
+};
static int __devinit dmic_dev_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index f5ccdbf7ebc6..e373f8f06907 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -294,20 +294,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
- snd_soc_add_controls(codec, jz4740_codec_controls,
- ARRAY_SIZE(jz4740_codec_controls));
-
- snd_soc_dapm_new_controls(dapm, jz4740_codec_dapm_widgets,
- ARRAY_SIZE(jz4740_codec_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, jz4740_codec_dapm_routes,
- ARRAY_SIZE(jz4740_codec_dapm_routes));
-
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
@@ -348,6 +337,13 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
.reg_cache_default = jz4740_codec_regs,
.reg_word_size = sizeof(u32),
.reg_cache_size = 2,
+
+ .controls = jz4740_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4740_codec_controls),
+ .dapm_widgets = jz4740_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
+ .dapm_routes = jz4740_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
};
static int __devinit jz4740_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index bd0517cb7980..4173b67c94d1 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -656,8 +656,6 @@ static const struct soc_enum max98088_exmode_enum =
ARRAY_SIZE(max98088_exmode_texts),
max98088_exmode_texts,
max98088_exmode_values);
-static const struct snd_kcontrol_new max98088_exmode_controls =
- SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
static const char *max98088_ex_thresh[] = { /* volts PP */
"0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
@@ -783,6 +781,7 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+ SOC_ENUM("EX Limiter Mode", max98088_exmode_enum),
SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
@@ -808,10 +807,10 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
/* Left speaker mixer switch */
static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
@@ -836,10 +835,10 @@ static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
/* Left headphone mixer switch */
static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
@@ -864,10 +863,10 @@ static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
/* Left earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
@@ -1094,9 +1093,6 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
- SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
- &max98088_exmode_controls),
-
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPKL"),
@@ -1112,7 +1108,7 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("INB2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route max98088_audio_map[] = {
/* Left headphone output mixer */
{"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
{"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
@@ -1226,22 +1222,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"MIC2 Input", NULL, "MIC2"},
};
-static int max98088_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, max98088_dapm_widgets,
- ARRAY_SIZE(max98088_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- snd_soc_add_controls(codec, max98088_snd_controls,
- ARRAY_SIZE(max98088_snd_controls));
-
- snd_soc_dapm_new_widgets(dapm);
- return 0;
-}
-
/* codec mclk clock divider coefficients */
static const struct {
u32 rate;
@@ -1586,6 +1566,36 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
+static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
+static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
static void max98088_sync_cache(struct snd_soc_codec *codec)
{
u16 *reg_cache = codec->reg_cache;
@@ -1647,12 +1657,14 @@ static struct snd_soc_dai_ops max98088_dai1_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params,
+ .digital_mute = max98088_dai1_digital_mute,
};
static struct snd_soc_dai_ops max98088_dai2_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params,
+ .digital_mute = max98088_dai2_digital_mute,
};
static struct snd_soc_dai_driver max98088_dai[] = {
@@ -2010,7 +2022,8 @@ static int max98088_probe(struct snd_soc_codec *codec)
max98088_handle_pdata(codec);
- max98088_add_widgets(codec);
+ snd_soc_add_controls(codec, max98088_snd_controls,
+ ARRAY_SIZE(max98088_snd_controls));
err_access:
return ret;
@@ -2036,6 +2049,10 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
.reg_word_size = sizeof(u8),
.reg_cache_default = max98088_reg,
.volatile_register = max98088_volatile_register,
+ .dapm_widgets = max98088_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
+ .dapm_routes = max98088_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
};
static int max98088_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
index 56554c797fef..be89a4f4aab8 100644
--- a/sound/soc/codecs/max98088.h
+++ b/sound/soc/codecs/max98088.h
@@ -133,6 +133,19 @@
#define M98088_REC_LINEMODE (1<<7)
#define M98088_REC_LINEMODE_MASK (1<<7)
+/* M98088_REG_2D_MIX_SPK_CNTL */
+ #define M98088_MIX_SPKR_GAIN_MASK (3<<2)
+ #define M98088_MIX_SPKR_GAIN_SHIFT 2
+ #define M98088_MIX_SPKL_GAIN_MASK (3<<0)
+ #define M98088_MIX_SPKL_GAIN_SHIFT 0
+
+/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */
+ #define M98088_DAI_MUTE (1<<7)
+ #define M98088_DAI_MUTE_MASK (1<<7)
+ #define M98088_DAI_VOICE_GAIN_MASK (3<<4)
+ #define M98088_DAI_ATTENUATION_MASK (0xF<<0)
+ #define M98088_DAI_ATTENUATION_SHIFT 0
+
/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
#define M98088_MICPRE_MASK (3<<5)
#define M98088_MICPRE_SHIFT 5
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
new file mode 100644
index 000000000000..e1d282d477da
--- /dev/null
+++ b/sound/soc/codecs/max98095.c
@@ -0,0 +1,2396 @@
+/*
+ * max98095.c -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <sound/max98095.h>
+#include "max98095.h"
+
+enum max98095_type {
+ MAX98095,
+};
+
+struct max98095_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+ int eq_sel;
+ int bq_sel;
+};
+
+struct max98095_priv {
+ enum max98095_type devtype;
+ void *control_data;
+ struct max98095_pdata *pdata;
+ unsigned int sysclk;
+ struct max98095_cdata dai[3];
+ const char **eq_texts;
+ const char **bq_texts;
+ struct soc_enum eq_enum;
+ struct soc_enum bq_enum;
+ int eq_textcnt;
+ int bq_textcnt;
+ u8 lin_state;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+};
+
+static const u8 max98095_reg_def[M98095_REG_CNT] = {
+ 0x00, /* 00 */
+ 0x00, /* 01 */
+ 0x00, /* 02 */
+ 0x00, /* 03 */
+ 0x00, /* 04 */
+ 0x00, /* 05 */
+ 0x00, /* 06 */
+ 0x00, /* 07 */
+ 0x00, /* 08 */
+ 0x00, /* 09 */
+ 0x00, /* 0A */
+ 0x00, /* 0B */
+ 0x00, /* 0C */
+ 0x00, /* 0D */
+ 0x00, /* 0E */
+ 0x00, /* 0F */
+ 0x00, /* 10 */
+ 0x00, /* 11 */
+ 0x00, /* 12 */
+ 0x00, /* 13 */
+ 0x00, /* 14 */
+ 0x00, /* 15 */
+ 0x00, /* 16 */
+ 0x00, /* 17 */
+ 0x00, /* 18 */
+ 0x00, /* 19 */
+ 0x00, /* 1A */
+ 0x00, /* 1B */
+ 0x00, /* 1C */
+ 0x00, /* 1D */
+ 0x00, /* 1E */
+ 0x00, /* 1F */
+ 0x00, /* 20 */
+ 0x00, /* 21 */
+ 0x00, /* 22 */
+ 0x00, /* 23 */
+ 0x00, /* 24 */
+ 0x00, /* 25 */
+ 0x00, /* 26 */
+ 0x00, /* 27 */
+ 0x00, /* 28 */
+ 0x00, /* 29 */
+ 0x00, /* 2A */
+ 0x00, /* 2B */
+ 0x00, /* 2C */
+ 0x00, /* 2D */
+ 0x00, /* 2E */
+ 0x00, /* 2F */
+ 0x00, /* 30 */
+ 0x00, /* 31 */
+ 0x00, /* 32 */
+ 0x00, /* 33 */
+ 0x00, /* 34 */
+ 0x00, /* 35 */
+ 0x00, /* 36 */
+ 0x00, /* 37 */
+ 0x00, /* 38 */
+ 0x00, /* 39 */
+ 0x00, /* 3A */
+ 0x00, /* 3B */
+ 0x00, /* 3C */
+ 0x00, /* 3D */
+ 0x00, /* 3E */
+ 0x00, /* 3F */
+ 0x00, /* 40 */
+ 0x00, /* 41 */
+ 0x00, /* 42 */
+ 0x00, /* 43 */
+ 0x00, /* 44 */
+ 0x00, /* 45 */
+ 0x00, /* 46 */
+ 0x00, /* 47 */
+ 0x00, /* 48 */
+ 0x00, /* 49 */
+ 0x00, /* 4A */
+ 0x00, /* 4B */
+ 0x00, /* 4C */
+ 0x00, /* 4D */
+ 0x00, /* 4E */
+ 0x00, /* 4F */
+ 0x00, /* 50 */
+ 0x00, /* 51 */
+ 0x00, /* 52 */
+ 0x00, /* 53 */
+ 0x00, /* 54 */
+ 0x00, /* 55 */
+ 0x00, /* 56 */
+ 0x00, /* 57 */
+ 0x00, /* 58 */
+ 0x00, /* 59 */
+ 0x00, /* 5A */
+ 0x00, /* 5B */
+ 0x00, /* 5C */
+ 0x00, /* 5D */
+ 0x00, /* 5E */
+ 0x00, /* 5F */
+ 0x00, /* 60 */
+ 0x00, /* 61 */
+ 0x00, /* 62 */
+ 0x00, /* 63 */
+ 0x00, /* 64 */
+ 0x00, /* 65 */
+ 0x00, /* 66 */
+ 0x00, /* 67 */
+ 0x00, /* 68 */
+ 0x00, /* 69 */
+ 0x00, /* 6A */
+ 0x00, /* 6B */
+ 0x00, /* 6C */
+ 0x00, /* 6D */
+ 0x00, /* 6E */
+ 0x00, /* 6F */
+ 0x00, /* 70 */
+ 0x00, /* 71 */
+ 0x00, /* 72 */
+ 0x00, /* 73 */
+ 0x00, /* 74 */
+ 0x00, /* 75 */
+ 0x00, /* 76 */
+ 0x00, /* 77 */
+ 0x00, /* 78 */
+ 0x00, /* 79 */
+ 0x00, /* 7A */
+ 0x00, /* 7B */
+ 0x00, /* 7C */
+ 0x00, /* 7D */
+ 0x00, /* 7E */
+ 0x00, /* 7F */
+ 0x00, /* 80 */
+ 0x00, /* 81 */
+ 0x00, /* 82 */
+ 0x00, /* 83 */
+ 0x00, /* 84 */
+ 0x00, /* 85 */
+ 0x00, /* 86 */
+ 0x00, /* 87 */
+ 0x00, /* 88 */
+ 0x00, /* 89 */
+ 0x00, /* 8A */
+ 0x00, /* 8B */
+ 0x00, /* 8C */
+ 0x00, /* 8D */
+ 0x00, /* 8E */
+ 0x00, /* 8F */
+ 0x00, /* 90 */
+ 0x00, /* 91 */
+ 0x30, /* 92 */
+ 0xF0, /* 93 */
+ 0x00, /* 94 */
+ 0x00, /* 95 */
+ 0x3F, /* 96 */
+ 0x00, /* 97 */
+ 0x00, /* 98 */
+ 0x00, /* 99 */
+ 0x00, /* 9A */
+ 0x00, /* 9B */
+ 0x00, /* 9C */
+ 0x00, /* 9D */
+ 0x00, /* 9E */
+ 0x00, /* 9F */
+ 0x00, /* A0 */
+ 0x00, /* A1 */
+ 0x00, /* A2 */
+ 0x00, /* A3 */
+ 0x00, /* A4 */
+ 0x00, /* A5 */
+ 0x00, /* A6 */
+ 0x00, /* A7 */
+ 0x00, /* A8 */
+ 0x00, /* A9 */
+ 0x00, /* AA */
+ 0x00, /* AB */
+ 0x00, /* AC */
+ 0x00, /* AD */
+ 0x00, /* AE */
+ 0x00, /* AF */
+ 0x00, /* B0 */
+ 0x00, /* B1 */
+ 0x00, /* B2 */
+ 0x00, /* B3 */
+ 0x00, /* B4 */
+ 0x00, /* B5 */
+ 0x00, /* B6 */
+ 0x00, /* B7 */
+ 0x00, /* B8 */
+ 0x00, /* B9 */
+ 0x00, /* BA */
+ 0x00, /* BB */
+ 0x00, /* BC */
+ 0x00, /* BD */
+ 0x00, /* BE */
+ 0x00, /* BF */
+ 0x00, /* C0 */
+ 0x00, /* C1 */
+ 0x00, /* C2 */
+ 0x00, /* C3 */
+ 0x00, /* C4 */
+ 0x00, /* C5 */
+ 0x00, /* C6 */
+ 0x00, /* C7 */
+ 0x00, /* C8 */
+ 0x00, /* C9 */
+ 0x00, /* CA */
+ 0x00, /* CB */
+ 0x00, /* CC */
+ 0x00, /* CD */
+ 0x00, /* CE */
+ 0x00, /* CF */
+ 0x00, /* D0 */
+ 0x00, /* D1 */
+ 0x00, /* D2 */
+ 0x00, /* D3 */
+ 0x00, /* D4 */
+ 0x00, /* D5 */
+ 0x00, /* D6 */
+ 0x00, /* D7 */
+ 0x00, /* D8 */
+ 0x00, /* D9 */
+ 0x00, /* DA */
+ 0x00, /* DB */
+ 0x00, /* DC */
+ 0x00, /* DD */
+ 0x00, /* DE */
+ 0x00, /* DF */
+ 0x00, /* E0 */
+ 0x00, /* E1 */
+ 0x00, /* E2 */
+ 0x00, /* E3 */
+ 0x00, /* E4 */
+ 0x00, /* E5 */
+ 0x00, /* E6 */
+ 0x00, /* E7 */
+ 0x00, /* E8 */
+ 0x00, /* E9 */
+ 0x00, /* EA */
+ 0x00, /* EB */
+ 0x00, /* EC */
+ 0x00, /* ED */
+ 0x00, /* EE */
+ 0x00, /* EF */
+ 0x00, /* F0 */
+ 0x00, /* F1 */
+ 0x00, /* F2 */
+ 0x00, /* F3 */
+ 0x00, /* F4 */
+ 0x00, /* F5 */
+ 0x00, /* F6 */
+ 0x00, /* F7 */
+ 0x00, /* F8 */
+ 0x00, /* F9 */
+ 0x00, /* FA */
+ 0x00, /* FB */
+ 0x00, /* FC */
+ 0x00, /* FD */
+ 0x00, /* FE */
+ 0x00, /* FF */
+};
+
+static struct {
+ int readable;
+ int writable;
+} max98095_access[M98095_REG_CNT] = {
+ { 0x00, 0x00 }, /* 00 */
+ { 0xFF, 0x00 }, /* 01 */
+ { 0xFF, 0x00 }, /* 02 */
+ { 0xFF, 0x00 }, /* 03 */
+ { 0xFF, 0x00 }, /* 04 */
+ { 0xFF, 0x00 }, /* 05 */
+ { 0xFF, 0x00 }, /* 06 */
+ { 0xFF, 0x00 }, /* 07 */
+ { 0xFF, 0x00 }, /* 08 */
+ { 0xFF, 0x00 }, /* 09 */
+ { 0xFF, 0x00 }, /* 0A */
+ { 0xFF, 0x00 }, /* 0B */
+ { 0xFF, 0x00 }, /* 0C */
+ { 0xFF, 0x00 }, /* 0D */
+ { 0xFF, 0x00 }, /* 0E */
+ { 0xFF, 0x9F }, /* 0F */
+ { 0xFF, 0xFF }, /* 10 */
+ { 0xFF, 0xFF }, /* 11 */
+ { 0xFF, 0xFF }, /* 12 */
+ { 0xFF, 0xFF }, /* 13 */
+ { 0xFF, 0xFF }, /* 14 */
+ { 0xFF, 0xFF }, /* 15 */
+ { 0xFF, 0xFF }, /* 16 */
+ { 0xFF, 0xFF }, /* 17 */
+ { 0xFF, 0xFF }, /* 18 */
+ { 0xFF, 0xFF }, /* 19 */
+ { 0xFF, 0xFF }, /* 1A */
+ { 0xFF, 0xFF }, /* 1B */
+ { 0xFF, 0xFF }, /* 1C */
+ { 0xFF, 0xFF }, /* 1D */
+ { 0xFF, 0x77 }, /* 1E */
+ { 0xFF, 0x77 }, /* 1F */
+ { 0xFF, 0x77 }, /* 20 */
+ { 0xFF, 0x77 }, /* 21 */
+ { 0xFF, 0x77 }, /* 22 */
+ { 0xFF, 0x77 }, /* 23 */
+ { 0xFF, 0xFF }, /* 24 */
+ { 0xFF, 0x7F }, /* 25 */
+ { 0xFF, 0x31 }, /* 26 */
+ { 0xFF, 0xFF }, /* 27 */
+ { 0xFF, 0xFF }, /* 28 */
+ { 0xFF, 0xFF }, /* 29 */
+ { 0xFF, 0xF7 }, /* 2A */
+ { 0xFF, 0x2F }, /* 2B */
+ { 0xFF, 0xEF }, /* 2C */
+ { 0xFF, 0xFF }, /* 2D */
+ { 0xFF, 0xFF }, /* 2E */
+ { 0xFF, 0xFF }, /* 2F */
+ { 0xFF, 0xFF }, /* 30 */
+ { 0xFF, 0xFF }, /* 31 */
+ { 0xFF, 0xFF }, /* 32 */
+ { 0xFF, 0xFF }, /* 33 */
+ { 0xFF, 0xF7 }, /* 34 */
+ { 0xFF, 0x2F }, /* 35 */
+ { 0xFF, 0xCF }, /* 36 */
+ { 0xFF, 0xFF }, /* 37 */
+ { 0xFF, 0xFF }, /* 38 */
+ { 0xFF, 0xFF }, /* 39 */
+ { 0xFF, 0xFF }, /* 3A */
+ { 0xFF, 0xFF }, /* 3B */
+ { 0xFF, 0xFF }, /* 3C */
+ { 0xFF, 0xFF }, /* 3D */
+ { 0xFF, 0xF7 }, /* 3E */
+ { 0xFF, 0x2F }, /* 3F */
+ { 0xFF, 0xCF }, /* 40 */
+ { 0xFF, 0xFF }, /* 41 */
+ { 0xFF, 0x77 }, /* 42 */
+ { 0xFF, 0xFF }, /* 43 */
+ { 0xFF, 0xFF }, /* 44 */
+ { 0xFF, 0xFF }, /* 45 */
+ { 0xFF, 0xFF }, /* 46 */
+ { 0xFF, 0xFF }, /* 47 */
+ { 0xFF, 0xFF }, /* 48 */
+ { 0xFF, 0x0F }, /* 49 */
+ { 0xFF, 0xFF }, /* 4A */
+ { 0xFF, 0xFF }, /* 4B */
+ { 0xFF, 0x3F }, /* 4C */
+ { 0xFF, 0x3F }, /* 4D */
+ { 0xFF, 0x3F }, /* 4E */
+ { 0xFF, 0xFF }, /* 4F */
+ { 0xFF, 0x7F }, /* 50 */
+ { 0xFF, 0x7F }, /* 51 */
+ { 0xFF, 0x0F }, /* 52 */
+ { 0xFF, 0x3F }, /* 53 */
+ { 0xFF, 0x3F }, /* 54 */
+ { 0xFF, 0x3F }, /* 55 */
+ { 0xFF, 0xFF }, /* 56 */
+ { 0xFF, 0xFF }, /* 57 */
+ { 0xFF, 0xBF }, /* 58 */
+ { 0xFF, 0x1F }, /* 59 */
+ { 0xFF, 0xBF }, /* 5A */
+ { 0xFF, 0x1F }, /* 5B */
+ { 0xFF, 0xBF }, /* 5C */
+ { 0xFF, 0x3F }, /* 5D */
+ { 0xFF, 0x3F }, /* 5E */
+ { 0xFF, 0x7F }, /* 5F */
+ { 0xFF, 0x7F }, /* 60 */
+ { 0xFF, 0x47 }, /* 61 */
+ { 0xFF, 0x9F }, /* 62 */
+ { 0xFF, 0x9F }, /* 63 */
+ { 0xFF, 0x9F }, /* 64 */
+ { 0xFF, 0x9F }, /* 65 */
+ { 0xFF, 0x9F }, /* 66 */
+ { 0xFF, 0xBF }, /* 67 */
+ { 0xFF, 0xBF }, /* 68 */
+ { 0xFF, 0xFF }, /* 69 */
+ { 0xFF, 0xFF }, /* 6A */
+ { 0xFF, 0x7F }, /* 6B */
+ { 0xFF, 0xF7 }, /* 6C */
+ { 0xFF, 0xFF }, /* 6D */
+ { 0xFF, 0xFF }, /* 6E */
+ { 0xFF, 0x1F }, /* 6F */
+ { 0xFF, 0xF7 }, /* 70 */
+ { 0xFF, 0xFF }, /* 71 */
+ { 0xFF, 0xFF }, /* 72 */
+ { 0xFF, 0x1F }, /* 73 */
+ { 0xFF, 0xF7 }, /* 74 */
+ { 0xFF, 0xFF }, /* 75 */
+ { 0xFF, 0xFF }, /* 76 */
+ { 0xFF, 0x1F }, /* 77 */
+ { 0xFF, 0xF7 }, /* 78 */
+ { 0xFF, 0xFF }, /* 79 */
+ { 0xFF, 0xFF }, /* 7A */
+ { 0xFF, 0x1F }, /* 7B */
+ { 0xFF, 0xF7 }, /* 7C */
+ { 0xFF, 0xFF }, /* 7D */
+ { 0xFF, 0xFF }, /* 7E */
+ { 0xFF, 0x1F }, /* 7F */
+ { 0xFF, 0xF7 }, /* 80 */
+ { 0xFF, 0xFF }, /* 81 */
+ { 0xFF, 0xFF }, /* 82 */
+ { 0xFF, 0x1F }, /* 83 */
+ { 0xFF, 0x7F }, /* 84 */
+ { 0xFF, 0x0F }, /* 85 */
+ { 0xFF, 0xD8 }, /* 86 */
+ { 0xFF, 0xFF }, /* 87 */
+ { 0xFF, 0xEF }, /* 88 */
+ { 0xFF, 0xFE }, /* 89 */
+ { 0xFF, 0xFE }, /* 8A */
+ { 0xFF, 0xFF }, /* 8B */
+ { 0xFF, 0xFF }, /* 8C */
+ { 0xFF, 0x3F }, /* 8D */
+ { 0xFF, 0xFF }, /* 8E */
+ { 0xFF, 0x3F }, /* 8F */
+ { 0xFF, 0x8F }, /* 90 */
+ { 0xFF, 0xFF }, /* 91 */
+ { 0xFF, 0x3F }, /* 92 */
+ { 0xFF, 0xFF }, /* 93 */
+ { 0xFF, 0xFF }, /* 94 */
+ { 0xFF, 0x0F }, /* 95 */
+ { 0xFF, 0x3F }, /* 96 */
+ { 0xFF, 0x8C }, /* 97 */
+ { 0x00, 0x00 }, /* 98 */
+ { 0x00, 0x00 }, /* 99 */
+ { 0x00, 0x00 }, /* 9A */
+ { 0x00, 0x00 }, /* 9B */
+ { 0x00, 0x00 }, /* 9C */
+ { 0x00, 0x00 }, /* 9D */
+ { 0x00, 0x00 }, /* 9E */
+ { 0x00, 0x00 }, /* 9F */
+ { 0x00, 0x00 }, /* A0 */
+ { 0x00, 0x00 }, /* A1 */
+ { 0x00, 0x00 }, /* A2 */
+ { 0x00, 0x00 }, /* A3 */
+ { 0x00, 0x00 }, /* A4 */
+ { 0x00, 0x00 }, /* A5 */
+ { 0x00, 0x00 }, /* A6 */
+ { 0x00, 0x00 }, /* A7 */
+ { 0x00, 0x00 }, /* A8 */
+ { 0x00, 0x00 }, /* A9 */
+ { 0x00, 0x00 }, /* AA */
+ { 0x00, 0x00 }, /* AB */
+ { 0x00, 0x00 }, /* AC */
+ { 0x00, 0x00 }, /* AD */
+ { 0x00, 0x00 }, /* AE */
+ { 0x00, 0x00 }, /* AF */
+ { 0x00, 0x00 }, /* B0 */
+ { 0x00, 0x00 }, /* B1 */
+ { 0x00, 0x00 }, /* B2 */
+ { 0x00, 0x00 }, /* B3 */
+ { 0x00, 0x00 }, /* B4 */
+ { 0x00, 0x00 }, /* B5 */
+ { 0x00, 0x00 }, /* B6 */
+ { 0x00, 0x00 }, /* B7 */
+ { 0x00, 0x00 }, /* B8 */
+ { 0x00, 0x00 }, /* B9 */
+ { 0x00, 0x00 }, /* BA */
+ { 0x00, 0x00 }, /* BB */
+ { 0x00, 0x00 }, /* BC */
+ { 0x00, 0x00 }, /* BD */
+ { 0x00, 0x00 }, /* BE */
+ { 0x00, 0x00 }, /* BF */
+ { 0x00, 0x00 }, /* C0 */
+ { 0x00, 0x00 }, /* C1 */
+ { 0x00, 0x00 }, /* C2 */
+ { 0x00, 0x00 }, /* C3 */
+ { 0x00, 0x00 }, /* C4 */
+ { 0x00, 0x00 }, /* C5 */
+ { 0x00, 0x00 }, /* C6 */
+ { 0x00, 0x00 }, /* C7 */
+ { 0x00, 0x00 }, /* C8 */
+ { 0x00, 0x00 }, /* C9 */
+ { 0x00, 0x00 }, /* CA */
+ { 0x00, 0x00 }, /* CB */
+ { 0x00, 0x00 }, /* CC */
+ { 0x00, 0x00 }, /* CD */
+ { 0x00, 0x00 }, /* CE */
+ { 0x00, 0x00 }, /* CF */
+ { 0x00, 0x00 }, /* D0 */
+ { 0x00, 0x00 }, /* D1 */
+ { 0x00, 0x00 }, /* D2 */
+ { 0x00, 0x00 }, /* D3 */
+ { 0x00, 0x00 }, /* D4 */
+ { 0x00, 0x00 }, /* D5 */
+ { 0x00, 0x00 }, /* D6 */
+ { 0x00, 0x00 }, /* D7 */
+ { 0x00, 0x00 }, /* D8 */
+ { 0x00, 0x00 }, /* D9 */
+ { 0x00, 0x00 }, /* DA */
+ { 0x00, 0x00 }, /* DB */
+ { 0x00, 0x00 }, /* DC */
+ { 0x00, 0x00 }, /* DD */
+ { 0x00, 0x00 }, /* DE */
+ { 0x00, 0x00 }, /* DF */
+ { 0x00, 0x00 }, /* E0 */
+ { 0x00, 0x00 }, /* E1 */
+ { 0x00, 0x00 }, /* E2 */
+ { 0x00, 0x00 }, /* E3 */
+ { 0x00, 0x00 }, /* E4 */
+ { 0x00, 0x00 }, /* E5 */
+ { 0x00, 0x00 }, /* E6 */
+ { 0x00, 0x00 }, /* E7 */
+ { 0x00, 0x00 }, /* E8 */
+ { 0x00, 0x00 }, /* E9 */
+ { 0x00, 0x00 }, /* EA */
+ { 0x00, 0x00 }, /* EB */
+ { 0x00, 0x00 }, /* EC */
+ { 0x00, 0x00 }, /* ED */
+ { 0x00, 0x00 }, /* EE */
+ { 0x00, 0x00 }, /* EF */
+ { 0x00, 0x00 }, /* F0 */
+ { 0x00, 0x00 }, /* F1 */
+ { 0x00, 0x00 }, /* F2 */
+ { 0x00, 0x00 }, /* F3 */
+ { 0x00, 0x00 }, /* F4 */
+ { 0x00, 0x00 }, /* F5 */
+ { 0x00, 0x00 }, /* F6 */
+ { 0x00, 0x00 }, /* F7 */
+ { 0x00, 0x00 }, /* F8 */
+ { 0x00, 0x00 }, /* F9 */
+ { 0x00, 0x00 }, /* FA */
+ { 0x00, 0x00 }, /* FB */
+ { 0x00, 0x00 }, /* FC */
+ { 0x00, 0x00 }, /* FD */
+ { 0x00, 0x00 }, /* FE */
+ { 0xFF, 0x00 }, /* FF */
+};
+
+static int max98095_readable(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if (reg >= M98095_REG_CNT)
+ return 0;
+ return max98095_access[reg].readable != 0;
+}
+
+static int max98095_volatile(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if (reg > M98095_REG_MAX_CACHED)
+ return 1;
+
+ switch (reg) {
+ case M98095_000_HOST_DATA:
+ case M98095_001_HOST_INT_STS:
+ case M98095_002_HOST_RSP_STS:
+ case M98095_003_HOST_CMD_STS:
+ case M98095_004_CODEC_STS:
+ case M98095_005_DAI1_ALC_STS:
+ case M98095_006_DAI2_ALC_STS:
+ case M98095_007_JACK_AUTO_STS:
+ case M98095_008_JACK_MANUAL_STS:
+ case M98095_009_JACK_VBAT_STS:
+ case M98095_00A_ACC_ADC_STS:
+ case M98095_00B_MIC_NG_AGC_STS:
+ case M98095_00C_SPK_L_VOLT_STS:
+ case M98095_00D_SPK_R_VOLT_STS:
+ case M98095_00E_TEMP_SENSOR_STS:
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Filter coefficients are in a separate register segment
+ * and they share the address space of the normal registers.
+ * The coefficient registers do not need or share the cache.
+ */
+static int max98095_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ data[0] = reg;
+ data[1] = value;
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ BUG_ON(band > 4);
+ BUG_ON(dai > 1);
+
+ /* Load the base register address */
+ eq_reg = dai ? M98095_142_DAI2_EQ_BASE : M98095_110_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ max98095_hw_write(codec, eq_reg++, M98095_BYTE1(coefs[i]));
+ max98095_hw_write(codec, eq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+/*
+ * Load biquad filter coefficient configurations registers
+ */
+static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int bq_reg;
+ unsigned int i;
+
+ BUG_ON(band > 1);
+ BUG_ON(dai > 1);
+
+ /* Load the base register address */
+ bq_reg = dai ? M98095_17E_DAI2_BQ_BASE : M98095_174_DAI1_BQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ bq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ max98095_hw_write(codec, bq_reg++, M98095_BYTE1(coefs[i]));
+ max98095_hw_write(codec, bq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+static const char * const max98095_fltr_mode[] = { "Voice", "Music" };
+static const struct soc_enum max98095_dai1_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 7, 2, max98095_fltr_mode),
+};
+static const struct soc_enum max98095_dai2_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 7, 2, max98095_fltr_mode),
+};
+
+static const char * const max98095_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static const struct soc_enum max98095_extmic_enum =
+ SOC_ENUM_SINGLE(M98095_087_CFG_MIC, 0, 3, max98095_extmic_text);
+
+static const struct snd_kcontrol_new max98095_extmic_mux =
+ SOC_DAPM_ENUM("External MIC Mux", max98095_extmic_enum);
+
+static const char * const max98095_linein_text[] = { "INA", "INB" };
+
+static const struct soc_enum max98095_linein_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 6, 2, max98095_linein_text);
+
+static const struct snd_kcontrol_new max98095_linein_mux =
+ SOC_DAPM_ENUM("Linein Input Mux", max98095_linein_enum);
+
+static const char * const max98095_line_mode_text[] = {
+ "Stereo", "Differential"};
+
+static const struct soc_enum max98095_linein_mode_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 7, 2, max98095_line_mode_text);
+
+static const struct soc_enum max98095_lineout_mode_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 4, 2, max98095_line_mode_text);
+
+static const char * const max98095_dai_fltr[] = {
+ "Off", "Elliptical-HPF-16k", "Butterworth-HPF-16k",
+ "Elliptical-HPF-8k", "Butterworth-HPF-8k", "Butterworth-HPF-Fs/240"};
+static const struct soc_enum max98095_dai1_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 0, 6, max98095_dai_fltr),
+};
+static const struct soc_enum max98095_dai2_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 0, 6, max98095_dai_fltr),
+};
+static const struct soc_enum max98095_dai3_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_042_DAI3_FILTERS, 0, 6, max98095_dai_fltr),
+};
+
+static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic1pre = sel;
+ snd_soc_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98095->mic1pre;
+ return 0;
+}
+
+static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic2pre = sel;
+ snd_soc_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98095->mic2pre;
+ return 0;
+}
+
+static const unsigned int max98095_micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0);
+
+static const unsigned int max98095_hp_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
+};
+
+static const unsigned int max98095_spk_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0),
+ 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0),
+ 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_rcv_lout_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_lin_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
+};
+
+static const struct snd_kcontrol_new max98095_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 0, 31, 0, max98095_hp_tlv),
+
+ SOC_DOUBLE_R_TLV("Speaker Volume", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 0, 39, 0, max98095_spk_tlv),
+
+ SOC_SINGLE_TLV("Receiver Volume", M98095_066_LVL_RCV,
+ 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R_TLV("Lineout Volume", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R("Headphone Switch", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 7, 1, 1),
+
+ SOC_DOUBLE_R("Speaker Switch", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 7, 1, 1),
+
+ SOC_SINGLE("Receiver Switch", M98095_066_LVL_RCV, 7, 1, 1),
+
+ SOC_DOUBLE_R("Lineout Switch", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 7, 1, 1),
+
+ SOC_SINGLE_TLV("MIC1 Volume", M98095_05F_LVL_MIC1, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_TLV("MIC2 Volume", M98095_060_LVL_MIC2, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98095_05F_LVL_MIC1, 5, 2, 0,
+ max98095_mic1pre_get, max98095_mic1pre_set,
+ max98095_micboost_tlv),
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98095_060_LVL_MIC2, 5, 2, 0,
+ max98095_mic2pre_get, max98095_mic2pre_set,
+ max98095_micboost_tlv),
+
+ SOC_SINGLE_TLV("Linein Volume", M98095_061_LVL_LINEIN, 0, 5, 1,
+ max98095_lin_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", M98095_05D_LVL_ADC_L, 0, 15, 1,
+ max98095_adc_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", M98095_05E_LVL_ADC_R, 0, 15, 1,
+ max98095_adc_tlv),
+
+ SOC_SINGLE_TLV("ADCL Boost Volume", M98095_05D_LVL_ADC_L, 4, 3, 0,
+ max98095_adcboost_tlv),
+ SOC_SINGLE_TLV("ADCR Boost Volume", M98095_05E_LVL_ADC_R, 4, 3, 0,
+ max98095_adcboost_tlv),
+
+ SOC_SINGLE("EQ1 Switch", M98095_088_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 Switch", M98095_088_CFG_LEVEL, 1, 1, 0),
+
+ SOC_SINGLE("Biquad1 Switch", M98095_088_CFG_LEVEL, 2, 1, 0),
+ SOC_SINGLE("Biquad2 Switch", M98095_088_CFG_LEVEL, 3, 1, 0),
+
+ SOC_ENUM("DAI1 Filter Mode", max98095_dai1_filter_mode_enum),
+ SOC_ENUM("DAI2 Filter Mode", max98095_dai2_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC Filter", max98095_dai1_dac_filter_enum),
+ SOC_ENUM("DAI2 DAC Filter", max98095_dai2_dac_filter_enum),
+ SOC_ENUM("DAI3 DAC Filter", max98095_dai3_dac_filter_enum),
+
+ SOC_ENUM("Linein Mode", max98095_linein_mode_enum),
+ SOC_ENUM("Lineout Mode", max98095_lineout_mode_enum),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98095_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_050_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_050_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_050_MIX_SPK_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_050_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_050_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_050_MIX_SPK_LEFT, 2, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98095_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_051_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_051_MIX_SPK_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_051_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_051_MIX_SPK_RIGHT, 2, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98095_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04C_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04C_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04C_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04C_MIX_HP_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04C_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04C_MIX_HP_LEFT, 2, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98095_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04D_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04D_MIX_HP_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04D_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04D_MIX_HP_RIGHT, 2, 1, 0),
+};
+
+/* Receiver earpiece mixer switch */
+static const struct snd_kcontrol_new max98095_mono_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04F_MIX_RCV, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04F_MIX_RCV, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04F_MIX_RCV, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04F_MIX_RCV, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04F_MIX_RCV, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04F_MIX_RCV, 2, 1, 0),
+};
+
+/* Left lineout mixer switch */
+static const struct snd_kcontrol_new max98095_left_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_053_MIX_LINEOUT1, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_053_MIX_LINEOUT1, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_053_MIX_LINEOUT1, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_053_MIX_LINEOUT1, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_053_MIX_LINEOUT1, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_053_MIX_LINEOUT1, 2, 1, 0),
+};
+
+/* Right lineout mixer switch */
+static const struct snd_kcontrol_new max98095_right_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_054_MIX_LINEOUT2, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_054_MIX_LINEOUT2, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_054_MIX_LINEOUT2, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_054_MIX_LINEOUT2, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_054_MIX_LINEOUT2, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_054_MIX_LINEOUT2, 2, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98095_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04A_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04A_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04A_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04A_MIX_ADC_LEFT, 2, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04B_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04B_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04B_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04B_MIX_ADC_RIGHT, 2, 1, 0),
+};
+
+static int max98095_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->reg == M98095_05F_LVL_MIC1) {
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
+ } else {
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic2pre)<<M98095_MICPRE_SHIFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The line inputs are stereo inputs with the left and right
+ * channels sharing a common PGA power control signal.
+ */
+static int max98095_line_pga(struct snd_soc_dapm_widget *w,
+ int event, u8 channel)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ u8 *state;
+
+ BUG_ON(!((channel == 1) || (channel == 2)));
+
+ state = &max98095->lin_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ *state |= channel;
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), (1 << w->shift));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ *state &= ~channel;
+ if (*state == 0) {
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max98095_pga_in1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 1);
+}
+
+static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 2);
+}
+
+/*
+ * The stereo line out mixer outputs to two stereo line outs.
+ * The 2nd pair has a separate set of enables.
+ */
+static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), (1 << (w->shift+2)));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98095_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98095_090_PWR_EN_IN, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98095_090_PWR_EN_IN, 1, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACM2", "Aux Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+ SND_SOC_DAPM_DAC("DACM3", "Voice Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98095_091_PWR_EN_OUT,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98095_091_PWR_EN_OUT,
+ 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98095_091_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98095_091_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RCV Mono Out", M98095_091_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("LINE Left Out", M98095_092_PWR_EN_OUT,
+ 0, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LINE Right Out", M98095_092_PWR_EN_OUT,
+ 1, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+ &max98095_extmic_mux),
+
+ SND_SOC_DAPM_MUX("Linein Mux", SND_SOC_NOPM, 0, 0,
+ &max98095_linein_mux),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_mono_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98095_mono_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98095_05F_LVL_MIC1,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98095_060_LVL_MIC2,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN1 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN2 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("MICBIAS1", M98095_090_PWR_EN_IN, 2, 0),
+ SND_SOC_DAPM_MICBIAS("MICBIAS2", M98095_090_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RCV"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route max98095_audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Left Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Right Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Receiver Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Receiver Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Receiver Mixer", "IN1 Switch", "IN1 Input"},
+ {"Receiver Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left Lineout output mixer */
+ {"Left Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right lineout output mixer */
+ {"Right Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ {"HP Left Out", NULL, "Left Headphone Mixer"},
+ {"HP Right Out", NULL, "Right Headphone Mixer"},
+ {"SPK Left Out", NULL, "Left Speaker Mixer"},
+ {"SPK Right Out", NULL, "Right Speaker Mixer"},
+ {"RCV Mono Out", NULL, "Receiver Mixer"},
+ {"LINE Left Out", NULL, "Left Lineout Mixer"},
+ {"LINE Right Out", NULL, "Right Lineout Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RCV", NULL, "RCV Mono Out"},
+ {"OUT1", NULL, "LINE Left Out"},
+ {"OUT2", NULL, "LINE Right Out"},
+ {"OUT3", NULL, "LINE Left Out"},
+ {"OUT4", NULL, "LINE Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+
+ {"IN1 Input", NULL, "INA1"},
+ {"IN2 Input", NULL, "INA2"},
+
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98095_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_add_controls(codec, max98095_snd_controls,
+ ARRAY_SIZE(max98095_snd_controls));
+
+ return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 rate;
+ u8 sr;
+} rate_table[] = {
+ {8000, 0x01},
+ {11025, 0x02},
+ {16000, 0x03},
+ {22050, 0x04},
+ {24000, 0x05},
+ {32000, 0x06},
+ {44100, 0x07},
+ {48000, 0x08},
+ {88200, 0x09},
+ {96000, 0x0A},
+};
+
+static int rate_value(int rate, u8 *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ return 0;
+ }
+ }
+ *value = rate_table[0].sr;
+ return -EINVAL;
+}
+
+static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[0];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_027_DAI1_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[1];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_031_DAI2_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai3_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 max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[2];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_03B_DAI3_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98095->sysclk)
+ return 0;
+
+ max98095->sysclk = freq; /* remember current sysclk */
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 40MHz)..
+ * 0x03 (when master clk is 40MHz to 60MHz)..
+ */
+ if ((freq >= 10000000) && (freq < 20000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x10);
+ } else if ((freq >= 20000000) && (freq < 40000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x20);
+ } else if ((freq >= 40000000) && (freq < 60000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x30);
+ } else {
+ dev_err(codec->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98095->sysclk = freq;
+ return 0;
+}
+
+static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[1];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_035_DAI2_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[2];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_03F_DAI3_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = snd_soc_cache_sync(codec);
+
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ M98095_MBEN, M98095_MBEN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ M98095_MBEN, 0);
+ codec->cache_sync = 1;
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98095_dai1_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai1_set_fmt,
+ .hw_params = max98095_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98095_dai2_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai2_set_fmt,
+ .hw_params = max98095_dai2_hw_params,
+};
+
+static struct snd_soc_dai_ops max98095_dai3_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai3_set_fmt,
+ .hw_params = max98095_dai3_hw_params,
+};
+
+static struct snd_soc_dai_driver max98095_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai2_ops,
+},
+{
+ .name = "Voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai3_ops,
+}
+
+};
+
+static int max98095_get_eq_channel(const char *name)
+{
+ if (strcmp(name, "EQ1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "EQ2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+ struct max98095_eq_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ BUG_ON(channel > 1);
+
+ if (!pdata || !max98095->eq_textcnt)
+ return 0;
+
+ if (sel >= pdata->eq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->eq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98095->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq_cfg[best].name,
+ pdata->eq_cfg[best].rate, fs);
+
+ coef_set = &pdata->eq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&codec->mutex);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_eq_band(codec, channel, 0, coef_set->band1);
+ m98095_eq_band(codec, channel, 1, coef_set->band2);
+ m98095_eq_band(codec, channel, 2, coef_set->band3);
+ m98095_eq_band(codec, channel, 3, coef_set->band4);
+ m98095_eq_band(codec, channel, 4, coef_set->band5);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&codec->mutex);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_eq_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("EQ1 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ SOC_ENUM_EXT("EQ2 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ };
+
+ cfg = pdata->eq_cfg;
+ cfgcnt = pdata->eq_cfgcnt;
+
+ /* Setup an array of texts for the equalizer enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->eq_textcnt = 0;
+ max98095->eq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->eq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->eq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->eq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->eq_texts,
+ sizeof(char *) * (max98095->eq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->eq_textcnt] = cfg[i].name;
+ max98095->eq_textcnt++;
+ max98095->eq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->eq_enum.texts = max98095->eq_texts;
+ max98095->eq_enum.max = max98095->eq_textcnt;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static int max98095_get_bq_channel(const char *name)
+{
+ if (strcmp(name, "Biquad1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "Biquad2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_bq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+ struct max98095_biquad_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ BUG_ON(channel > 1);
+
+ if (!pdata || !max98095->bq_textcnt)
+ return 0;
+
+ if (sel >= pdata->bq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->bq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->bq_cfgcnt; i++) {
+ if (strcmp(pdata->bq_cfg[i].name, max98095->bq_texts[sel]) == 0 &&
+ abs(pdata->bq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->bq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->bq_cfg[best].name,
+ pdata->bq_cfg[best].rate, fs);
+
+ coef_set = &pdata->bq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&codec->mutex);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_biquad_band(codec, channel, 0, coef_set->band1);
+ m98095_biquad_band(codec, channel, 1, coef_set->band2);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&codec->mutex);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int channel = max98095_get_bq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->bq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_biquad_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("Biquad1 Mode",
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ SOC_ENUM_EXT("Biquad2 Mode",
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ };
+
+ cfg = pdata->bq_cfg;
+ cfgcnt = pdata->bq_cfgcnt;
+
+ /* Setup an array of texts for the biquad enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->bq_textcnt = 0;
+ max98095->bq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->bq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->bq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->bq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->bq_texts,
+ sizeof(char *) * (max98095->bq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->bq_textcnt] = cfg[i].name;
+ max98095->bq_textcnt++;
+ max98095->bq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->bq_enum.texts = max98095->bq_texts;
+ max98095->bq_enum.max = max98095->bq_textcnt;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add Biquad control: %d\n", ret);
+}
+
+static void max98095_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ u8 regval = 0;
+
+ if (!pdata) {
+ dev_dbg(codec->dev, "No platform data\n");
+ return;
+ }
+
+ /* Configure mic for analog/digital mic mode */
+ if (pdata->digmic_left_mode)
+ regval |= M98095_DIGMIC_L;
+
+ if (pdata->digmic_right_mode)
+ regval |= M98095_DIGMIC_R;
+
+ snd_soc_write(codec, M98095_087_CFG_MIC, regval);
+
+ /* Configure equalizers */
+ if (pdata->eq_cfgcnt)
+ max98095_handle_eq_pdata(codec);
+
+ /* Configure bi-quad filters */
+ if (pdata->bq_cfgcnt)
+ max98095_handle_bq_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98095_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98095_resume(struct snd_soc_codec *codec)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define max98095_suspend NULL
+#define max98095_resume NULL
+#endif
+
+static int max98095_reset(struct snd_soc_codec *codec)
+{
+ int i, ret;
+
+ /* Gracefully reset the DSP core and the codec hardware
+ * in a proper sequence */
+ ret = snd_soc_write(codec, M98095_00F_HOST_CFG, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset DSP: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset to hardware default for registers, as there is not
+ * a soft reset hardware control register */
+ for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) {
+ ret = snd_soc_write(codec, i, max98095_reg_def[i]);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int max98095_probe(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ int ret = 0;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* reset the codec, the DSP core, and disable all interrupts */
+ max98095_reset(codec);
+
+ /* initialize private data */
+
+ max98095->sysclk = (unsigned)-1;
+ max98095->eq_textcnt = 0;
+ max98095->bq_textcnt = 0;
+
+ cdata = &max98095->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[2];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ max98095->lin_state = 0;
+ max98095->mic1pre = 0;
+ max98095->mic2pre = 0;
+
+ ret = snd_soc_read(codec, M98095_0FF_REV_ID);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_access;
+ }
+ dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+ snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
+
+ /* initialize registers cache to hardware default */
+ max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ snd_soc_write(codec, M98095_048_MIX_DAC_LR,
+ M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
+
+ snd_soc_write(codec, M98095_049_MIX_DAC_M,
+ M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM);
+
+ snd_soc_write(codec, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
+ snd_soc_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL);
+ snd_soc_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL);
+
+ snd_soc_write(codec, M98095_02C_DAI1_IOCFG,
+ M98095_S1NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_036_DAI2_IOCFG,
+ M98095_S2NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_040_DAI3_IOCFG,
+ M98095_S3NORMAL|M98095_SDATA);
+
+ max98095_handle_pdata(codec);
+
+ /* take the codec out of the shut down */
+ snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ M98095_SHDNRUN);
+
+ max98095_add_widgets(codec);
+
+err_access:
+ return ret;
+}
+
+static int max98095_remove(struct snd_soc_codec *codec)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98095 = {
+ .probe = max98095_probe,
+ .remove = max98095_remove,
+ .suspend = max98095_suspend,
+ .resume = max98095_resume,
+ .set_bias_level = max98095_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(max98095_reg_def),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = max98095_reg_def,
+ .readable_register = max98095_readable,
+ .volatile_register = max98095_volatile,
+ .dapm_widgets = max98095_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets),
+ .dapm_routes = max98095_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98095_audio_map),
+};
+
+static int max98095_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98095_priv *max98095;
+ int ret;
+
+ max98095 = kzalloc(sizeof(struct max98095_priv), GFP_KERNEL);
+ if (max98095 == NULL)
+ return -ENOMEM;
+
+ max98095->devtype = id->driver_data;
+ i2c_set_clientdata(i2c, max98095);
+ max98095->control_data = i2c;
+ max98095->pdata = i2c->dev.platform_data;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_max98095, &max98095_dai[0], 3);
+ if (ret < 0)
+ kfree(max98095);
+ return ret;
+}
+
+static int __devexit max98095_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 max98095_i2c_id[] = {
+ { "max98095", MAX98095 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
+
+static struct i2c_driver max98095_i2c_driver = {
+ .driver = {
+ .name = "max98095",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98095_i2c_probe,
+ .remove = __devexit_p(max98095_i2c_remove),
+ .id_table = max98095_i2c_id,
+};
+
+static int __init max98095_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&max98095_i2c_driver);
+ if (ret)
+ pr_err("Failed to register max98095 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(max98095_init);
+
+static void __exit max98095_exit(void)
+{
+ i2c_del_driver(&max98095_i2c_driver);
+}
+module_exit(max98095_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98095 driver");
+MODULE_AUTHOR("Peter Hsiang");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
new file mode 100644
index 000000000000..891584a0eb03
--- /dev/null
+++ b/sound/soc/codecs/max98095.h
@@ -0,0 +1,299 @@
+/*
+ * max98095.h -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 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 _MAX98095_H
+#define _MAX98095_H
+
+/*
+ * MAX98095 Registers Definition
+ */
+
+#define M98095_000_HOST_DATA 0x00
+#define M98095_001_HOST_INT_STS 0x01
+#define M98095_002_HOST_RSP_STS 0x02
+#define M98095_003_HOST_CMD_STS 0x03
+#define M98095_004_CODEC_STS 0x04
+#define M98095_005_DAI1_ALC_STS 0x05
+#define M98095_006_DAI2_ALC_STS 0x06
+#define M98095_007_JACK_AUTO_STS 0x07
+#define M98095_008_JACK_MANUAL_STS 0x08
+#define M98095_009_JACK_VBAT_STS 0x09
+#define M98095_00A_ACC_ADC_STS 0x0A
+#define M98095_00B_MIC_NG_AGC_STS 0x0B
+#define M98095_00C_SPK_L_VOLT_STS 0x0C
+#define M98095_00D_SPK_R_VOLT_STS 0x0D
+#define M98095_00E_TEMP_SENSOR_STS 0x0E
+#define M98095_00F_HOST_CFG 0x0F
+#define M98095_010_HOST_INT_CFG 0x10
+#define M98095_011_HOST_INT_EN 0x11
+#define M98095_012_CODEC_INT_EN 0x12
+#define M98095_013_JACK_INT_EN 0x13
+#define M98095_014_JACK_INT_EN 0x14
+#define M98095_015_DEC 0x15
+#define M98095_016_RESERVED 0x16
+#define M98095_017_RESERVED 0x17
+#define M98095_018_KEYCODE3 0x18
+#define M98095_019_KEYCODE2 0x19
+#define M98095_01A_KEYCODE1 0x1A
+#define M98095_01B_KEYCODE0 0x1B
+#define M98095_01C_OEMCODE1 0x1C
+#define M98095_01D_OEMCODE0 0x1D
+#define M98095_01E_XCFG1 0x1E
+#define M98095_01F_XCFG2 0x1F
+#define M98095_020_XCFG3 0x20
+#define M98095_021_XCFG4 0x21
+#define M98095_022_XCFG5 0x22
+#define M98095_023_XCFG6 0x23
+#define M98095_024_XGPIO 0x24
+#define M98095_025_XCLKCFG 0x25
+#define M98095_026_SYS_CLK 0x26
+#define M98095_027_DAI1_CLKMODE 0x27
+#define M98095_028_DAI1_CLKCFG_HI 0x28
+#define M98095_029_DAI1_CLKCFG_LO 0x29
+#define M98095_02A_DAI1_FORMAT 0x2A
+#define M98095_02B_DAI1_CLOCK 0x2B
+#define M98095_02C_DAI1_IOCFG 0x2C
+#define M98095_02D_DAI1_TDM 0x2D
+#define M98095_02E_DAI1_FILTERS 0x2E
+#define M98095_02F_DAI1_LVL1 0x2F
+#define M98095_030_DAI1_LVL2 0x30
+#define M98095_031_DAI2_CLKMODE 0x31
+#define M98095_032_DAI2_CLKCFG_HI 0x32
+#define M98095_033_DAI2_CLKCFG_LO 0x33
+#define M98095_034_DAI2_FORMAT 0x34
+#define M98095_035_DAI2_CLOCK 0x35
+#define M98095_036_DAI2_IOCFG 0x36
+#define M98095_037_DAI2_TDM 0x37
+#define M98095_038_DAI2_FILTERS 0x38
+#define M98095_039_DAI2_LVL1 0x39
+#define M98095_03A_DAI2_LVL2 0x3A
+#define M98095_03B_DAI3_CLKMODE 0x3B
+#define M98095_03C_DAI3_CLKCFG_HI 0x3C
+#define M98095_03D_DAI3_CLKCFG_LO 0x3D
+#define M98095_03E_DAI3_FORMAT 0x3E
+#define M98095_03F_DAI3_CLOCK 0x3F
+#define M98095_040_DAI3_IOCFG 0x40
+#define M98095_041_DAI3_TDM 0x41
+#define M98095_042_DAI3_FILTERS 0x42
+#define M98095_043_DAI3_LVL1 0x43
+#define M98095_044_DAI3_LVL2 0x44
+#define M98095_045_CFG_DSP 0x45
+#define M98095_046_DAC_CTRL1 0x46
+#define M98095_047_DAC_CTRL2 0x47
+#define M98095_048_MIX_DAC_LR 0x48
+#define M98095_049_MIX_DAC_M 0x49
+#define M98095_04A_MIX_ADC_LEFT 0x4A
+#define M98095_04B_MIX_ADC_RIGHT 0x4B
+#define M98095_04C_MIX_HP_LEFT 0x4C
+#define M98095_04D_MIX_HP_RIGHT 0x4D
+#define M98095_04E_CFG_HP 0x4E
+#define M98095_04F_MIX_RCV 0x4F
+#define M98095_050_MIX_SPK_LEFT 0x50
+#define M98095_051_MIX_SPK_RIGHT 0x51
+#define M98095_052_MIX_SPK_CFG 0x52
+#define M98095_053_MIX_LINEOUT1 0x53
+#define M98095_054_MIX_LINEOUT2 0x54
+#define M98095_055_MIX_LINEOUT_CFG 0x55
+#define M98095_056_LVL_SIDETONE_DAI12 0x56
+#define M98095_057_LVL_SIDETONE_DAI3 0x57
+#define M98095_058_LVL_DAI1_PLAY 0x58
+#define M98095_059_LVL_DAI1_EQ 0x59
+#define M98095_05A_LVL_DAI2_PLAY 0x5A
+#define M98095_05B_LVL_DAI2_EQ 0x5B
+#define M98095_05C_LVL_DAI3_PLAY 0x5C
+#define M98095_05D_LVL_ADC_L 0x5D
+#define M98095_05E_LVL_ADC_R 0x5E
+#define M98095_05F_LVL_MIC1 0x5F
+#define M98095_060_LVL_MIC2 0x60
+#define M98095_061_LVL_LINEIN 0x61
+#define M98095_062_LVL_LINEOUT1 0x62
+#define M98095_063_LVL_LINEOUT2 0x63
+#define M98095_064_LVL_HP_L 0x64
+#define M98095_065_LVL_HP_R 0x65
+#define M98095_066_LVL_RCV 0x66
+#define M98095_067_LVL_SPK_L 0x67
+#define M98095_068_LVL_SPK_R 0x68
+#define M98095_069_MICAGC_CFG 0x69
+#define M98095_06A_MICAGC_THRESH 0x6A
+#define M98095_06B_SPK_NOISEGATE 0x6B
+#define M98095_06C_DAI1_ALC1_TIME 0x6C
+#define M98095_06D_DAI1_ALC1_COMP 0x6D
+#define M98095_06E_DAI1_ALC1_EXPN 0x6E
+#define M98095_06F_DAI1_ALC1_GAIN 0x6F
+#define M98095_070_DAI1_ALC2_TIME 0x70
+#define M98095_071_DAI1_ALC2_COMP 0x71
+#define M98095_072_DAI1_ALC2_EXPN 0x72
+#define M98095_073_DAI1_ALC2_GAIN 0x73
+#define M98095_074_DAI1_ALC3_TIME 0x74
+#define M98095_075_DAI1_ALC3_COMP 0x75
+#define M98095_076_DAI1_ALC3_EXPN 0x76
+#define M98095_077_DAI1_ALC3_GAIN 0x77
+#define M98095_078_DAI2_ALC1_TIME 0x78
+#define M98095_079_DAI2_ALC1_COMP 0x79
+#define M98095_07A_DAI2_ALC1_EXPN 0x7A
+#define M98095_07B_DAI2_ALC1_GAIN 0x7B
+#define M98095_07C_DAI2_ALC2_TIME 0x7C
+#define M98095_07D_DAI2_ALC2_COMP 0x7D
+#define M98095_07E_DAI2_ALC2_EXPN 0x7E
+#define M98095_07F_DAI2_ALC2_GAIN 0x7F
+#define M98095_080_DAI2_ALC3_TIME 0x80
+#define M98095_081_DAI2_ALC3_COMP 0x81
+#define M98095_082_DAI2_ALC3_EXPN 0x82
+#define M98095_083_DAI2_ALC3_GAIN 0x83
+#define M98095_084_HP_NOISE_GATE 0x84
+#define M98095_085_AUX_ADC 0x85
+#define M98095_086_CFG_LINE 0x86
+#define M98095_087_CFG_MIC 0x87
+#define M98095_088_CFG_LEVEL 0x88
+#define M98095_089_JACK_DET_AUTO 0x89
+#define M98095_08A_JACK_DET_MANUAL 0x8A
+#define M98095_08B_JACK_KEYSCAN_DBC 0x8B
+#define M98095_08C_JACK_KEYSCAN_DLY 0x8C
+#define M98095_08D_JACK_KEY_THRESH 0x8D
+#define M98095_08E_JACK_DC_SLEW 0x8E
+#define M98095_08F_JACK_TEST_CFG 0x8F
+#define M98095_090_PWR_EN_IN 0x90
+#define M98095_091_PWR_EN_OUT 0x91
+#define M98095_092_PWR_EN_OUT 0x92
+#define M98095_093_BIAS_CTRL 0x93
+#define M98095_094_PWR_DAC_21 0x94
+#define M98095_095_PWR_DAC_03 0x95
+#define M98095_096_PWR_DAC_CK 0x96
+#define M98095_097_PWR_SYS 0x97
+
+#define M98095_0FF_REV_ID 0xFF
+
+#define M98095_REG_CNT (0xFF+1)
+#define M98095_REG_MAX_CACHED 0X97
+
+/* MAX98095 Registers Bit Fields */
+
+/* M98095_00F_HOST_CFG */
+ #define M98095_SEG (1<<0)
+ #define M98095_XTEN (1<<1)
+ #define M98095_MDLLEN (1<<2)
+
+/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
+ #define M98095_CLKMODE_MASK 0xFF
+
+/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */
+ #define M98095_DAI_MAS (1<<7)
+ #define M98095_DAI_WCI (1<<6)
+ #define M98095_DAI_BCI (1<<5)
+ #define M98095_DAI_DLY (1<<4)
+ #define M98095_DAI_TDM (1<<2)
+ #define M98095_DAI_FSW (1<<1)
+ #define M98095_DAI_WS (1<<0)
+
+/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */
+ #define M98095_DAI_BSEL64 (1<<0)
+ #define M98095_DAI_DOSR_DIV2 (0<<5)
+ #define M98095_DAI_DOSR_DIV4 (1<<5)
+
+/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */
+ #define M98095_S1NORMAL (1<<6)
+ #define M98095_S2NORMAL (2<<6)
+ #define M98095_S3NORMAL (3<<6)
+ #define M98095_SDATA (3<<0)
+
+/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */
+ #define M98095_DAI_DHF (1<<3)
+
+/* M98095_045_DSP_CFG */
+ #define M98095_DSPNORMAL (5<<4)
+
+/* M98095_048_MIX_DAC_LR */
+ #define M98095_DAI1L_TO_DACR (1<<7)
+ #define M98095_DAI1R_TO_DACR (1<<6)
+ #define M98095_DAI2M_TO_DACR (1<<5)
+ #define M98095_DAI1L_TO_DACL (1<<3)
+ #define M98095_DAI1R_TO_DACL (1<<2)
+ #define M98095_DAI2M_TO_DACL (1<<1)
+ #define M98095_DAI3M_TO_DACL (1<<0)
+
+/* M98095_049_MIX_DAC_M */
+ #define M98095_DAI1L_TO_DACM (1<<3)
+ #define M98095_DAI1R_TO_DACM (1<<2)
+ #define M98095_DAI2M_TO_DACM (1<<1)
+ #define M98095_DAI3M_TO_DACM (1<<0)
+
+/* M98095_04E_MIX_HP_CFG */
+ #define M98095_HPNORMAL (3<<4)
+
+/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */
+ #define M98095_MICPRE_MASK (3<<5)
+ #define M98095_MICPRE_SHIFT 5
+
+/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */
+ #define M98095_HP_MUTE (1<<7)
+
+/* M98095_066_LVL_RCV */
+ #define M98095_REC_MUTE (1<<7)
+
+/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */
+ #define M98095_SP_MUTE (1<<7)
+
+/* M98095_087_CFG_MIC */
+ #define M98095_MICSEL_MASK (3<<0)
+ #define M98095_DIGMIC_L (1<<2)
+ #define M98095_DIGMIC_R (1<<3)
+ #define M98095_DIGMIC2L (1<<4)
+ #define M98095_DIGMIC2R (1<<5)
+
+/* M98095_088_CFG_LEVEL */
+ #define M98095_VSEN (1<<6)
+ #define M98095_ZDEN (1<<5)
+ #define M98095_BQ2EN (1<<3)
+ #define M98095_BQ1EN (1<<2)
+ #define M98095_EQ2EN (1<<1)
+ #define M98095_EQ1EN (1<<0)
+
+/* M98095_090_PWR_EN_IN */
+ #define M98095_INEN (1<<7)
+ #define M98095_MB2EN (1<<3)
+ #define M98095_MB1EN (1<<2)
+ #define M98095_MBEN (3<<2)
+ #define M98095_ADREN (1<<1)
+ #define M98095_ADLEN (1<<0)
+
+/* M98095_091_PWR_EN_OUT */
+ #define M98095_HPLEN (1<<7)
+ #define M98095_HPREN (1<<6)
+ #define M98095_SPLEN (1<<5)
+ #define M98095_SPREN (1<<4)
+ #define M98095_RECEN (1<<3)
+ #define M98095_DALEN (1<<1)
+ #define M98095_DAREN (1<<0)
+
+/* M98095_092_PWR_EN_OUT */
+ #define M98095_SPK_FIXEDSPECTRUM (0<<4)
+ #define M98095_SPK_SPREADSPECTRUM (1<<4)
+
+/* M98095_097_PWR_SYS */
+ #define M98095_SHDNRUN (1<<7)
+ #define M98095_PERFMODE (1<<3)
+ #define M98095_HPPLYBACK (1<<2)
+ #define M98095_PWRSV8K (1<<1)
+ #define M98095_PWRSV (1<<0)
+
+#define M98095_COEFS_PER_BAND 5
+
+#define M98095_BYTE1(w) ((w >> 8) & 0xff)
+#define M98095_BYTE0(w) (w & 0xff)
+
+/* Equalizer filter coefficients */
+#define M98095_110_DAI1_EQ_BASE 0x10
+#define M98095_142_DAI2_EQ_BASE 0x42
+
+/* Biquad filter coefficients */
+#define M98095_174_DAI1_BQ_BASE 0x74
+#define M98095_17E_DAI2_BQ_BASE 0x7E
+
+#endif
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index 4d9fb279e146..84ffdebb8a8b 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -827,8 +827,6 @@ EXPORT_SYMBOL_GPL(sn95031_jack_detection);
/* codec registration */
static int sn95031_codec_probe(struct snd_soc_codec *codec)
{
- int ret;
-
pr_debug("codec_probe called\n");
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
@@ -879,16 +877,7 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, sn95031_snd_controls,
ARRAY_SIZE(sn95031_snd_controls));
- ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
- ARRAY_SIZE(sn95031_dapm_widgets));
- if (ret)
- pr_err("soc_dapm_new_control failed %d", ret);
- ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
- ARRAY_SIZE(sn95031_audio_map));
- if (ret)
- pr_err("soc_dapm_add_routes failed %d", ret);
-
- return ret;
+ return 0;
}
static int sn95031_codec_remove(struct snd_soc_codec *codec)
@@ -905,6 +894,10 @@ struct snd_soc_codec_driver sn95031_codec = {
.read = sn95031_read,
.write = sn95031_write,
.set_bias_level = sn95031_set_vaud_bias,
+ .dapm_widgets = sn95031_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets),
+ .dapm_routes = sn95031_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map),
};
static int __devinit sn95031_device_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
index 4c32b54913ad..6a1a7e705cd7 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transciever.c
@@ -21,7 +21,7 @@
#include <sound/pcm.h>
#include <sound/initval.h>
-MODULE_LICENSE("GPL");
+#define DRV_NAME "spdif-dit"
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
@@ -56,7 +56,7 @@ static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
.remove = spdif_dit_remove,
.driver = {
- .name = "spdif-dit",
+ .name = DRV_NAME,
.owner = THIS_MODULE,
},
};
@@ -74,3 +74,7 @@ static void __exit dit_exit(void)
module_init(dit_modinit);
module_exit(dit_exit);
+MODULE_AUTHOR("Steve Chen <schen@mvista.com>");
+MODULE_DESCRIPTION("SPDIF dummy codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 2727befd158e..84f4ad568556 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -32,6 +32,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -39,18 +40,25 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include "ssm2602.h"
#define SSM2602_VERSION "0.1"
+enum ssm2602_type {
+ SSM2602,
+ SSM2604,
+};
+
/* codec private data */
struct ssm2602_priv {
unsigned int sysclk;
enum snd_soc_control_type control_type;
- void *control_data;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
+
+ enum ssm2602_type type;
};
/*
@@ -60,60 +68,12 @@ struct ssm2602_priv {
* There is no point in caching the reset register
*/
static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0017, 0x0017, 0x0079, 0x0079,
- 0x0000, 0x0000, 0x0000, 0x000a,
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
0x0000, 0x0000
};
-/*
- * read ssm2602 register cache
- */
-static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == SSM2602_RESET)
- return 0;
- if (reg >= SSM2602_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write ssm2602 register cache
- */
-static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= SSM2602_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * write to the ssm2602 register space
- */
-static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 ssm2602 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- ssm2602_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define ssm2602_reset(c) ssm2602_write(c, SSM2602_RESET, 0)
+#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0)
/*Appending several "None"s just for OSS mixer use*/
static const char *ssm2602_input_select[] = {
@@ -128,174 +88,187 @@ static const struct soc_enum ssm2602_enum[] = {
SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
};
-static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+static const unsigned int ssm260x_outmix_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
+};
-SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
- 0, 127, 0),
-SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
- 7, 1, 0),
+static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
-SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0),
+static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
+ ssm260x_inpga_tlv),
SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
-SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
-SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0),
-SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
-
-SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
-
SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
-SOC_ENUM("Capture Source", ssm2602_enum[0]),
-
SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
};
+static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 0, 127, 0, ssm260x_outmix_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 7, 1, 0),
+SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
+ ssm260x_sidetone_tlv),
+
+SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
+SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
+};
+
/* Output Mixer */
-static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
+static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
-SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
};
/* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
-static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
-SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
- &ssm2602_output_mixer_controls[0],
- ARRAY_SIZE(ssm2602_output_mixer_controls)),
+static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0),
+
SND_SOC_DAPM_OUTPUT("LOUT"),
-SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
-SND_SOC_DAPM_OUTPUT("RHPOUT"),
-SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls)),
+
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
-SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
-SND_SOC_DAPM_INPUT("RLINEIN"),
-SND_SOC_DAPM_INPUT("LLINEIN"),
};
-static const struct snd_soc_dapm_route audio_conn[] = {
- /* output mixer */
+static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
+};
+
+static const struct snd_soc_dapm_route ssm260x_routes[] = {
+ {"DAC", NULL, "Digital Core Power"},
+ {"ADC", NULL, "Digital Core Power"},
+
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+ {"ROUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+};
+
+static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
- /* outputs */
{"RHPOUT", NULL, "Output Mixer"},
- {"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
- {"LOUT", NULL, "Output Mixer"},
- /* input mux */
{"Input Mux", "Line", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"},
- /* inputs */
- {"Line Input", NULL, "LLINEIN"},
- {"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
};
-static int ssm2602_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
- ARRAY_SIZE(ssm2602_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn));
-
- return 0;
-}
+static const struct snd_soc_dapm_route ssm2604_routes[] = {
+ {"ADC", NULL, "Line Input"},
+};
-struct _coeff_div {
+struct ssm2602_coeff {
u32 mclk;
u32 rate;
- u16 fs;
- u8 sr:4;
- u8 bosr:1;
- u8 usb:1;
+ u8 srate;
};
-/* codec mclk clock divider coefficients */
-static const struct _coeff_div coeff_div[] = {
+#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
+
+/* codec mclk clock coefficients */
+static const struct ssm2602_coeff ssm2602_coeff_table[] = {
/* 48k */
- {12288000, 48000, 256, 0x0, 0x0, 0x0},
- {18432000, 48000, 384, 0x0, 0x1, 0x0},
- {12000000, 48000, 250, 0x0, 0x0, 0x1},
+ {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
+ {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
+ {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
/* 32k */
- {12288000, 32000, 384, 0x6, 0x0, 0x0},
- {18432000, 32000, 576, 0x6, 0x1, 0x0},
- {12000000, 32000, 375, 0x6, 0x0, 0x1},
+ {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
+ {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
+ {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
/* 8k */
- {12288000, 8000, 1536, 0x3, 0x0, 0x0},
- {18432000, 8000, 2304, 0x3, 0x1, 0x0},
- {11289600, 8000, 1408, 0xb, 0x0, 0x0},
- {16934400, 8000, 2112, 0xb, 0x1, 0x0},
- {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+ {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
+ {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
+ {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
+ {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
+ {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
/* 96k */
- {12288000, 96000, 128, 0x7, 0x0, 0x0},
- {18432000, 96000, 192, 0x7, 0x1, 0x0},
- {12000000, 96000, 125, 0x7, 0x0, 0x1},
+ {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
+ {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
+ {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
/* 44.1k */
- {11289600, 44100, 256, 0x8, 0x0, 0x0},
- {16934400, 44100, 384, 0x8, 0x1, 0x0},
- {12000000, 44100, 272, 0x8, 0x1, 0x1},
+ {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
+ {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
+ {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
/* 88.2k */
- {11289600, 88200, 128, 0xf, 0x0, 0x0},
- {16934400, 88200, 192, 0xf, 0x1, 0x0},
- {12000000, 88200, 136, 0xf, 0x1, 0x1},
+ {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
+ {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
+ {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
};
-static inline int get_coeff(int mclk, int rate)
+static inline int ssm2602_get_coeff(int mclk, int rate)
{
int i;
- for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
- if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
- return i;
+ for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
+ if (ssm2602_coeff_table[i].rate == rate &&
+ ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
}
- return i;
+ return -EINVAL;
}
static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = codec->control_data;
- u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
- int i = get_coeff(ssm2602->sysclk, params_rate(params));
+ u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
+ int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
if (substream == ssm2602->slave_substream) {
- dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+ dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
return 0;
}
- /*no match is found*/
- if (i == ARRAY_SIZE(coeff_div))
- return -EINVAL;
-
- srate = (coeff_div[i].sr << 2) |
- (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+ if (srate < 0)
+ return srate;
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
- ssm2602_write(codec, SSM2602_SRATE, srate);
+ snd_soc_write(codec, SSM2602_SRATE, srate);
/* bit size */
switch (params_format(params)) {
@@ -311,8 +284,7 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
iface |= 0x000c;
break;
}
- ssm2602_write(codec, SSM2602_IFACE, iface);
- ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
+ snd_soc_write(codec, SSM2602_IFACE, iface);
return 0;
}
@@ -354,17 +326,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0;
}
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- /* set active */
- ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
-
- return 0;
-}
-
static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -372,25 +333,22 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- /* deactivate */
- if (!codec->active)
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
-
if (ssm2602->master_substream == substream)
ssm2602->master_substream = ssm2602->slave_substream;
ssm2602->slave_substream = NULL;
}
+
static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
+ u16 mute_reg = snd_soc_read(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
if (mute)
- ssm2602_write(codec, SSM2602_APDIGI,
+ snd_soc_write(codec, SSM2602_APDIGI,
mute_reg | APDIGI_ENABLE_DAC_MUTE);
else
- ssm2602_write(codec, SSM2602_APDIGI, mute_reg);
+ snd_soc_write(codec, SSM2602_APDIGI, mute_reg);
return 0;
}
@@ -466,30 +424,29 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- ssm2602_write(codec, SSM2602_IFACE, iface);
+ snd_soc_write(codec, SSM2602_IFACE, iface);
return 0;
}
static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f;
+ u16 reg = snd_soc_read(codec, SSM2602_PWR) & 0xff7f;
switch (level) {
case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
- ssm2602_write(codec, SSM2602_PWR, reg);
+ snd_soc_write(codec, SSM2602_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
+ snd_soc_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
break;
case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
- ssm2602_write(codec, SSM2602_PWR, 0xffff);
+ snd_soc_write(codec, SSM2602_PWR, 0xffff);
break;
}
@@ -506,7 +463,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
- .prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute,
@@ -539,50 +495,87 @@ static int ssm2602_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int ssm2602_resume(struct snd_soc_codec *codec)
{
- int i;
- u8 data[2];
- u16 *cache = codec->reg_cache;
-
- /* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) {
- data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
- data[1] = cache[i] & 0x00ff;
- codec->hw_write(codec->control_data, data, 2);
- }
+ snd_soc_cache_sync(codec);
+
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
return 0;
}
static int ssm2602_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret, reg;
+
+ reg = snd_soc_read(codec, SSM2602_LOUT1V);
+ snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
+ reg = snd_soc_read(codec, SSM2602_ROUT1V);
+ snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+
+ ret = snd_soc_add_controls(codec, ssm2602_snd_controls,
+ ARRAY_SIZE(ssm2602_snd_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
+ ARRAY_SIZE(ssm2602_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
+ ARRAY_SIZE(ssm2602_routes));
+}
+
+static int ssm2604_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
+ ARRAY_SIZE(ssm2604_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
+ ARRAY_SIZE(ssm2604_routes));
+}
+
+static int ssm260x_probe(struct snd_soc_codec *codec)
+{
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- int ret = 0, reg;
+ int ret, reg;
pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
- codec->control_data = ssm2602->control_data;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
- ssm2602_reset(codec);
+ ret = ssm2602_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
- /*power on device*/
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
/* set the update bits */
- reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL);
- ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL);
- ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V);
- ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
- ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+ reg = snd_soc_read(codec, SSM2602_LINVOL);
+ snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
+ reg = snd_soc_read(codec, SSM2602_RINVOL);
+ snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
/*select Line in as default input*/
- ssm2602_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
+ snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
APANA_ENABLE_MIC_BOOST);
- ssm2602_write(codec, SSM2602_PWR, 0);
- snd_soc_add_controls(codec, ssm2602_snd_controls,
- ARRAY_SIZE(ssm2602_snd_controls));
- ssm2602_add_widgets(codec);
+ switch (ssm2602->type) {
+ case SSM2602:
+ ret = ssm2602_probe(codec);
+ break;
+ case SSM2604:
+ ret = ssm2604_probe(codec);
+ break;
+ }
return ret;
}
@@ -595,18 +588,61 @@ static int ssm2602_remove(struct snd_soc_codec *codec)
}
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
- .probe = ssm2602_probe,
+ .probe = ssm260x_probe,
.remove = ssm2602_remove,
.suspend = ssm2602_suspend,
.resume = ssm2602_resume,
- .read = ssm2602_read_reg_cache,
- .write = ssm2602_write,
.set_bias_level = ssm2602_set_bias_level,
- .reg_cache_size = sizeof(ssm2602_reg),
+ .reg_cache_size = ARRAY_SIZE(ssm2602_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = ssm2602_reg,
+
+ .controls = ssm260x_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
+ .dapm_widgets = ssm260x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
+ .dapm_routes = ssm260x_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
};
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit ssm2602_spi_probe(struct spi_device *spi)
+{
+ struct ssm2602_priv *ssm2602;
+ int ret;
+
+ ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
+ if (ssm2602 == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, ssm2602);
+ ssm2602->control_type = SND_SOC_SPI;
+ ssm2602->type = SSM2602;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
+ if (ret < 0)
+ kfree(ssm2602);
+ return ret;
+}
+
+static int __devexit ssm2602_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ kfree(spi_get_drvdata(spi));
+ return 0;
+}
+
+static struct spi_driver ssm2602_spi_driver = {
+ .driver = {
+ .name = "ssm2602",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm2602_spi_probe,
+ .remove = __devexit_p(ssm2602_spi_remove),
+};
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* ssm2602 2 wire address is determined by GPIO5
@@ -614,7 +650,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
* low = 0x1a
* high = 0x1b
*/
-static int ssm2602_i2c_probe(struct i2c_client *i2c,
+static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ssm2602_priv *ssm2602;
@@ -625,8 +661,8 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, ssm2602);
- ssm2602->control_data = i2c;
ssm2602->control_type = SND_SOC_I2C;
+ ssm2602->type = id->driver_data;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
@@ -635,7 +671,7 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
return ret;
}
-static int ssm2602_i2c_remove(struct i2c_client *client)
+static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
@@ -643,7 +679,9 @@ static int ssm2602_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id ssm2602_i2c_id[] = {
- { "ssm2602", 0 },
+ { "ssm2602", SSM2602 },
+ { "ssm2603", SSM2602 },
+ { "ssm2604", SSM2604 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
@@ -651,11 +689,11 @@ MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
/* corgi i2c codec control layer */
static struct i2c_driver ssm2602_i2c_driver = {
.driver = {
- .name = "ssm2602-codec",
+ .name = "ssm2602",
.owner = THIS_MODULE,
},
.probe = ssm2602_i2c_probe,
- .remove = ssm2602_i2c_remove,
+ .remove = __devexit_p(ssm2602_i2c_remove),
.id_table = ssm2602_i2c_id,
};
#endif
@@ -664,25 +702,35 @@ static struct i2c_driver ssm2602_i2c_driver = {
static int __init ssm2602_modinit(void)
{
int ret = 0;
+
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&ssm2602_spi_driver);
+ if (ret)
+ return ret;
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&ssm2602_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register SSM2602 I2C driver: %d\n",
- ret);
- }
+ if (ret)
+ return ret;
#endif
+
return ret;
}
module_init(ssm2602_modinit);
static void __exit ssm2602_exit(void)
{
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&ssm2602_spi_driver);
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&ssm2602_i2c_driver);
#endif
}
module_exit(ssm2602_exit);
-MODULE_DESCRIPTION("ASoC ssm2602 driver");
+MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
MODULE_AUTHOR("Cliff Cai");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
index 42a47d0f8e25..b98c69168036 100644
--- a/sound/soc/codecs/ssm2602.h
+++ b/sound/soc/codecs/ssm2602.h
@@ -117,11 +117,5 @@
#define SSM2602_CACHEREGNUM 10
#define SSM2602_SYSCLK 0
-#define SSM2602_DAI 0
-
-struct ssm2602_setup_data {
- int i2c_bus;
- unsigned short i2c_address;
-};
#endif
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 54a30ef0ec8b..33bb52f3f683 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MICIN"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
/* Output Mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "Playback Switch", "DAC"},
@@ -388,18 +388,6 @@ static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
return 0;
}
-static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets,
- ARRAY_SIZE(tlv320aic23_dapm_widgets));
- /* set up audio path interconnects */
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -676,7 +664,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, tlv320aic23_snd_controls,
ARRAY_SIZE(tlv320aic23_snd_controls));
- tlv320aic23_add_widgets(codec);
return 0;
}
@@ -698,6 +685,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
.read = tlv320aic23_read_reg_cache,
.write = tlv320aic23_write,
.set_bias_level = tlv320aic23_set_bias_level,
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = tlv320aic23_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 6c43c13f0430..c3d96fc8c267 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -157,7 +157,8 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 082e9d51963f..faa5e9fb1471 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -1,7 +1,7 @@
/*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
@@ -587,6 +587,9 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Right DAC Power",
DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Codec Power",
+ DAC33_PWR_CTRL, 4, 0, NULL, 0),
+
SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event),
SND_SOC_DAPM_POST("Post Playback", dac33_playback_event),
};
@@ -619,6 +622,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
/* output */
{"LEFT_LO", NULL, "Output Left Amplifier"},
{"RIGHT_LO", NULL, "Output Right Amplifier"},
+
+ {"LEFT_LO", NULL, "Codec Power"},
+ {"RIGHT_LO", NULL, "Codec Power"},
};
static int dac33_add_widgets(struct snd_soc_codec *codec)
@@ -636,13 +642,10 @@ static int dac33_add_widgets(struct snd_soc_codec *codec)
static int dac33_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
- if (!dac33->substream)
- dac33_soft_power(codec, 1);
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -943,8 +946,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
/* Write registers 0x08 and 0x09 (MSB, LSB) */
dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
- /* calib time: 128 is a nice number ;) */
- dac33_write(codec, DAC33_CALIB_TIME, 128);
+ /* OSC calibration time */
+ dac33_write(codec, DAC33_CALIB_TIME, 96);
/* adjustment treshold & step */
dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
@@ -1655,5 +1658,5 @@ module_exit(dac33_module_exit);
MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h
index 7c318b5da437..ed69670747bf 100644
--- a/sound/soc/codecs/tlv320dac33.h
+++ b/sound/soc/codecs/tlv320dac33.h
@@ -1,7 +1,7 @@
/*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 1f1ac8110bef..239e0c461068 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Nokia Corporation
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -495,7 +495,7 @@ static void __exit tpa6130a2_exit(void)
i2c_del_driver(&tpa6130a2_i2c_driver);
}
-MODULE_AUTHOR("Peter Ujfalusi");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
index 5df49c8756b2..417444020ba6 100644
--- a/sound/soc/codecs/tpa6130a2.h
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -3,7 +3,7 @@
*
* Copyright (C) Nokia Corporation
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 255901c4460d..4c336636d4f5 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -960,9 +960,9 @@ static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0);
/*
* AFMGAIN volume control:
- * from 18 to 24 dB in 6 dB steps
+ * from -18 to 24 dB in 6 dB steps
*/
-static DECLARE_TLV_DB_SCALE(afm_amp_tlv, 1800, 600, 0);
+static DECLARE_TLV_DB_SCALE(afm_amp_tlv, -1800, 600, 0);
/*
* HSGAIN volume control:
@@ -1049,7 +1049,7 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
/* AFM gains */
SOC_DOUBLE_TLV("Aux FM Volume",
- TWL6040_REG_LINEGAIN, 0, 4, 0xF, 0, afm_amp_tlv),
+ TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
/* Playback gains */
SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume",
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 48ffd406a71d..a7b8f301bad3 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -601,9 +601,7 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.reg_cache_step = 1,
.read = uda134x_read_reg_cache,
.write = uda134x_write,
-#ifdef POWER_OFF_ON_STANDBY
.set_bias_level = uda134x_set_bias_level,
-#endif
};
static int __devinit uda134x_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
new file mode 100644
index 000000000000..14d0716bf009
--- /dev/null
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -0,0 +1,108 @@
+/*
+ * Driver for the 1250-EV1 audio I/O module
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+static const struct snd_soc_dapm_widget wm1250_ev1_dapm_widgets[] = {
+SND_SOC_DAPM_ADC("ADC", "wm1250-ev1 Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DAC", "wm1250-ev1 Playback", SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_INPUT("WM1250 Input"),
+SND_SOC_DAPM_INPUT("WM1250 Output"),
+};
+
+static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = {
+ { "ADC", NULL, "WM1250 Input" },
+ { "WM1250 Output", NULL, "DAC" },
+};
+
+static struct snd_soc_dai_driver wm1250_ev1_dai = {
+ .name = "wm1250-ev1",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
+ .dapm_widgets = wm1250_ev1_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets),
+ .dapm_routes = wm1250_ev1_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes),
+};
+
+static int __devinit wm1250_ev1_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
+ &wm1250_ev1_dai, 1);
+}
+
+static int __devexit wm1250_ev1_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm1250_ev1_i2c_id[] = {
+ { "wm1250-ev1", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
+
+static struct i2c_driver wm1250_ev1_i2c_driver = {
+ .driver = {
+ .name = "wm1250-ev1",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm1250_ev1_probe,
+ .remove = __devexit_p(wm1250_ev1_remove),
+ .id_table = wm1250_ev1_i2c_id,
+};
+
+static int __init wm1250_ev1_modinit(void)
+{
+ int ret = 0;
+
+ ret = i2c_add_driver(&wm1250_ev1_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM1250-EV1 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(wm1250_ev1_modinit);
+
+static void __exit wm1250_ev1_exit(void)
+{
+ i2c_del_driver(&wm1250_ev1_i2c_driver);
+}
+module_exit(wm1250_ev1_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 97c30382d3ff..a537e4af6ae7 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -77,7 +77,7 @@ SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8711_intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -89,17 +89,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LOUT", NULL, "Output Mixer"},
};
-static int wm8711_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8711_dapm_widgets,
- ARRAY_SIZE(wm8711_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
struct _coeff_div {
u32 mclk;
u32 rate;
@@ -398,7 +387,6 @@ static int wm8711_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8711_snd_controls,
ARRAY_SIZE(wm8711_snd_controls));
- wm8711_add_widgets(codec);
return ret;
@@ -420,6 +408,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
.reg_cache_size = ARRAY_SIZE(wm8711_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8711_reg,
+ .dapm_widgets = wm8711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
+ .dapm_routes = wm8711_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 736b0352d0a7..86d4718d3a76 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -65,22 +65,11 @@ SND_SOC_DAPM_OUTPUT("VOUTL"),
SND_SOC_DAPM_OUTPUT("VOUTR"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8728_intercon[] = {
{"VOUTL", NULL, "DAC"},
{"VOUTR", NULL, "DAC"},
};
-static int wm8728_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8728_dapm_widgets,
- ARRAY_SIZE(wm8728_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int wm8728_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
@@ -255,7 +244,6 @@ static int wm8728_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8728_snd_controls,
ARRAY_SIZE(wm8728_snd_controls));
- wm8728_add_widgets(codec);
return ret;
}
@@ -275,6 +263,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
.reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8728_reg_defaults,
+ .dapm_widgets = wm8728_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets),
+ .dapm_routes = wm8728_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 0a67c31b2663..6dec7cee2cb4 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -201,7 +201,7 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
return wm8731->sysclk_type == WM8731_SYSCLK_MCLK;
}
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8731_intercon[] = {
{"DAC", NULL, "OSC", wm8731_check_osc},
{"ADC", NULL, "OSC", wm8731_check_osc},
@@ -227,17 +227,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Mic Bias", NULL, "MICIN"},
};
-static int wm8731_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets,
- ARRAY_SIZE(wm8731_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
struct _coeff_div {
u32 mclk;
u32 rate;
@@ -599,7 +588,6 @@ static int wm8731_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8731_snd_controls,
ARRAY_SIZE(wm8731_snd_controls));
- wm8731_add_widgets(codec);
/* Regulators will have been enabled by bias management */
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
@@ -636,6 +624,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
.reg_cache_size = ARRAY_SIZE(wm8731_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8731_reg,
+ .dapm_widgets = wm8731_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+ .dapm_routes = wm8731_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
};
#if defined(CONFIG_SPI_MASTER)
@@ -667,7 +659,7 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
static struct spi_driver wm8731_spi_driver = {
.driver = {
- .name = "wm8731-codec",
+ .name = "wm8731",
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
@@ -711,7 +703,7 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
- .name = "wm8731-codec",
+ .name = "wm8731",
.owner = THIS_MODULE,
},
.probe = wm8731_i2c_probe,
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index f52b623bb692..43e3d760766f 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -382,7 +382,8 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
u16 reg;
@@ -634,6 +635,13 @@ static const struct soc_enum lsidetone_enum =
static const struct soc_enum rsidetone_enum =
SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
+static const char *adcinput_text[] = {
+ "ADC", "DMIC"
+};
+
+static const struct soc_enum adcinput_enum =
+ SOC_ENUM_SINGLE(WM8903_CLOCK_RATE_TEST_4, 9, 2, adcinput_text);
+
static const char *aif_text[] = {
"Left", "Right"
};
@@ -692,7 +700,7 @@ SOC_ENUM("DRC Smoothing Threshold", drc_smoothing),
SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup),
SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
- WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv),
+ WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
SOC_ENUM("ADC Companding Mode", adc_companding),
SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
@@ -767,6 +775,9 @@ static const struct snd_kcontrol_new lsidetone_mux =
static const struct snd_kcontrol_new rsidetone_mux =
SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
+static const struct snd_kcontrol_new adcinput_mux =
+ SOC_DAPM_ENUM("ADC Input", adcinput_enum);
+
static const struct snd_kcontrol_new lcapture_mux =
SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
@@ -817,6 +828,7 @@ SND_SOC_DAPM_INPUT("IN2L"),
SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
@@ -842,6 +854,9 @@ SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
+SND_SOC_DAPM_MUX("Left ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+SND_SOC_DAPM_MUX("Right ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+
SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
@@ -930,7 +945,7 @@ SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8903_intercon[] = {
{ "CLK_DSP", NULL, "CLK_SYS" },
{ "Mic Bias", NULL, "CLK_SYS" },
@@ -979,6 +994,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Left Input PGA", NULL, "Left Input Mode Mux" },
{ "Right Input PGA", NULL, "Right Input Mode Mux" },
+ { "Left ADC Input", "ADC", "Left Input PGA" },
+ { "Left ADC Input", "DMIC", "DMICDAT" },
+ { "Right ADC Input", "ADC", "Right Input PGA" },
+ { "Right ADC Input", "DMIC", "DMICDAT" },
+
{ "Left Capture Mux", "Left", "ADCL" },
{ "Left Capture Mux", "Right", "ADCR" },
@@ -988,9 +1008,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIFTXL", NULL, "Left Capture Mux" },
{ "AIFTXR", NULL, "Right Capture Mux" },
- { "ADCL", NULL, "Left Input PGA" },
+ { "ADCL", NULL, "Left ADC Input" },
{ "ADCL", NULL, "CLK_DSP" },
- { "ADCR", NULL, "Right Input PGA" },
+ { "ADCR", NULL, "Right ADC Input" },
{ "ADCR", NULL, "CLK_DSP" },
{ "Left Playback Mux", "Left", "AIFRXL" },
@@ -1087,17 +1107,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Right Line Output PGA", NULL, "Charge Pump" },
};
-static int wm8903_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8903_dapm_widgets,
- ARRAY_SIZE(wm8903_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int wm8903_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -2028,7 +2037,6 @@ static int wm8903_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls));
- wm8903_add_widgets(codec);
wm8903_init_gpio(codec);
@@ -2054,6 +2062,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
.reg_cache_default = wm8903_reg_defaults,
.volatile_register = wm8903_volatile_register,
.seq_notifier = wm8903_seq_notifier,
+ .dapm_widgets = wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets),
+ .dapm_routes = wm8903_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8903_intercon),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c
new file mode 100644
index 000000000000..ccc9bd832794
--- /dev/null
+++ b/sound/soc/codecs/wm8915.c
@@ -0,0 +1,2931 @@
+/*
+ * wm8915.c - WM8915 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <sound/wm8915.h>
+#include "wm8915.h"
+
+#define WM8915_AIFS 2
+
+#define HPOUT1L 1
+#define HPOUT1R 2
+#define HPOUT2L 4
+#define HPOUT2R 8
+
+#define WM8915_NUM_SUPPLIES 6
+static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "MICVDD",
+};
+
+struct wm8915_priv {
+ struct snd_soc_codec *codec;
+
+ int ldo1ena;
+
+ int sysclk;
+
+ int fll_src;
+ int fll_fref;
+ int fll_fout;
+
+ struct completion fll_lock;
+
+ u16 dcs_pending;
+ struct completion dcs_done;
+
+ u16 hpout_ena;
+ u16 hpout_pending;
+
+ struct regulator_bulk_data supplies[WM8915_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8915_NUM_SUPPLIES];
+
+ struct wm8915_pdata pdata;
+
+ int rx_rate[WM8915_AIFS];
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[2];
+ struct soc_enum retune_mobile_enum;
+
+ struct snd_soc_jack *jack;
+ bool detecting;
+ bool jack_mic;
+ wm8915_polarity_fn polarity_cb;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+};
+
+/* We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8915_REGULATOR_EVENT(n) \
+static int wm8915_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8915_priv *wm8915 = container_of(nb, struct wm8915_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ wm8915->codec->cache_sync = 1; \
+ } \
+ return 0; \
+}
+
+WM8915_REGULATOR_EVENT(0)
+WM8915_REGULATOR_EVENT(1)
+WM8915_REGULATOR_EVENT(2)
+WM8915_REGULATOR_EVENT(3)
+WM8915_REGULATOR_EVENT(4)
+WM8915_REGULATOR_EVENT(5)
+
+static const u16 wm8915_reg[WM8915_MAX_REGISTER] = {
+ [WM8915_SOFTWARE_RESET] = 0x8915,
+ [WM8915_POWER_MANAGEMENT_7] = 0x10,
+ [WM8915_DAC1_HPOUT1_VOLUME] = 0x88,
+ [WM8915_DAC2_HPOUT2_VOLUME] = 0x88,
+ [WM8915_DAC1_LEFT_VOLUME] = 0x2c0,
+ [WM8915_DAC1_RIGHT_VOLUME] = 0x2c0,
+ [WM8915_DAC2_LEFT_VOLUME] = 0x2c0,
+ [WM8915_DAC2_RIGHT_VOLUME] = 0x2c0,
+ [WM8915_OUTPUT1_LEFT_VOLUME] = 0x80,
+ [WM8915_OUTPUT1_RIGHT_VOLUME] = 0x80,
+ [WM8915_OUTPUT2_LEFT_VOLUME] = 0x80,
+ [WM8915_OUTPUT2_RIGHT_VOLUME] = 0x80,
+ [WM8915_MICBIAS_1] = 0x39,
+ [WM8915_MICBIAS_2] = 0x39,
+ [WM8915_LDO_1] = 0x3,
+ [WM8915_LDO_2] = 0x13,
+ [WM8915_ACCESSORY_DETECT_MODE_1] = 0x4,
+ [WM8915_HEADPHONE_DETECT_1] = 0x20,
+ [WM8915_MIC_DETECT_1] = 0x7600,
+ [WM8915_MIC_DETECT_2] = 0xbf,
+ [WM8915_CHARGE_PUMP_1] = 0x1f25,
+ [WM8915_CHARGE_PUMP_2] = 0xab19,
+ [WM8915_DC_SERVO_5] = 0x2a2a,
+ [WM8915_CONTROL_INTERFACE_1] = 0x8004,
+ [WM8915_CLOCKING_1] = 0x10,
+ [WM8915_AIF_RATE] = 0x83,
+ [WM8915_FLL_CONTROL_4] = 0x5dc0,
+ [WM8915_FLL_CONTROL_5] = 0xc84,
+ [WM8915_FLL_EFS_2] = 0x2,
+ [WM8915_AIF1_TX_LRCLK_1] = 0x80,
+ [WM8915_AIF1_TX_LRCLK_2] = 0x8,
+ [WM8915_AIF1_RX_LRCLK_1] = 0x80,
+ [WM8915_AIF1TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8915_AIF1RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8915_AIF1TX_TEST] = 0x7,
+ [WM8915_AIF2_TX_LRCLK_1] = 0x80,
+ [WM8915_AIF2_TX_LRCLK_2] = 0x8,
+ [WM8915_AIF2_RX_LRCLK_1] = 0x80,
+ [WM8915_AIF2TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8915_AIF2RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8915_AIF2TX_TEST] = 0x1,
+ [WM8915_DSP1_TX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP1_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP1_RX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP1_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP1_TX_FILTERS] = 0x2000,
+ [WM8915_DSP1_RX_FILTERS_1] = 0x200,
+ [WM8915_DSP1_RX_FILTERS_2] = 0x10,
+ [WM8915_DSP1_DRC_1] = 0x98,
+ [WM8915_DSP1_DRC_2] = 0x845,
+ [WM8915_DSP1_RX_EQ_GAINS_1] = 0x6318,
+ [WM8915_DSP1_RX_EQ_GAINS_2] = 0x6300,
+ [WM8915_DSP1_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8915_DSP1_RX_EQ_BAND_1_B] = 0x400,
+ [WM8915_DSP1_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8915_DSP1_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8915_DSP1_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8915_DSP1_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8915_DSP1_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8915_DSP1_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8915_DSP1_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8915_DSP1_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8915_DSP1_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8915_DSP1_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8915_DSP1_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8915_DSP1_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8915_DSP1_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8915_DSP1_RX_EQ_BAND_5_A] = 0x564,
+ [WM8915_DSP1_RX_EQ_BAND_5_B] = 0x559,
+ [WM8915_DSP1_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8915_DSP2_TX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP2_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP2_RX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP2_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP2_TX_FILTERS] = 0x2000,
+ [WM8915_DSP2_RX_FILTERS_1] = 0x200,
+ [WM8915_DSP2_RX_FILTERS_2] = 0x10,
+ [WM8915_DSP2_DRC_1] = 0x98,
+ [WM8915_DSP2_DRC_2] = 0x845,
+ [WM8915_DSP2_RX_EQ_GAINS_1] = 0x6318,
+ [WM8915_DSP2_RX_EQ_GAINS_2] = 0x6300,
+ [WM8915_DSP2_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8915_DSP2_RX_EQ_BAND_1_B] = 0x400,
+ [WM8915_DSP2_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8915_DSP2_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8915_DSP2_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8915_DSP2_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8915_DSP2_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8915_DSP2_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8915_DSP2_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8915_DSP2_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8915_DSP2_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8915_DSP2_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8915_DSP2_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8915_DSP2_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8915_DSP2_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8915_DSP2_RX_EQ_BAND_5_A] = 0x564,
+ [WM8915_DSP2_RX_EQ_BAND_5_B] = 0x559,
+ [WM8915_DSP2_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8915_OVERSAMPLING] = 0xd,
+ [WM8915_SIDETONE] = 0x1040,
+ [WM8915_GPIO_1] = 0xa101,
+ [WM8915_GPIO_2] = 0xa101,
+ [WM8915_GPIO_3] = 0xa101,
+ [WM8915_GPIO_4] = 0xa101,
+ [WM8915_GPIO_5] = 0xa101,
+ [WM8915_PULL_CONTROL_2] = 0x140,
+ [WM8915_INTERRUPT_STATUS_1_MASK] = 0x1f,
+ [WM8915_INTERRUPT_STATUS_2_MASK] = 0x1ecf,
+ [WM8915_RIGHT_PDM_SPEAKER] = 0x1,
+ [WM8915_PDM_SPEAKER_MUTE_SEQUENCE] = 0x69,
+ [WM8915_PDM_SPEAKER_VOLUME] = 0x66,
+ [WM8915_WRITE_SEQUENCER_0] = 0x1,
+ [WM8915_WRITE_SEQUENCER_1] = 0x1,
+ [WM8915_WRITE_SEQUENCER_3] = 0x6,
+ [WM8915_WRITE_SEQUENCER_4] = 0x40,
+ [WM8915_WRITE_SEQUENCER_5] = 0x1,
+ [WM8915_WRITE_SEQUENCER_6] = 0xf,
+ [WM8915_WRITE_SEQUENCER_7] = 0x6,
+ [WM8915_WRITE_SEQUENCER_8] = 0x1,
+ [WM8915_WRITE_SEQUENCER_9] = 0x3,
+ [WM8915_WRITE_SEQUENCER_10] = 0x104,
+ [WM8915_WRITE_SEQUENCER_12] = 0x60,
+ [WM8915_WRITE_SEQUENCER_13] = 0x11,
+ [WM8915_WRITE_SEQUENCER_14] = 0x401,
+ [WM8915_WRITE_SEQUENCER_16] = 0x50,
+ [WM8915_WRITE_SEQUENCER_17] = 0x3,
+ [WM8915_WRITE_SEQUENCER_18] = 0x100,
+ [WM8915_WRITE_SEQUENCER_20] = 0x51,
+ [WM8915_WRITE_SEQUENCER_21] = 0x3,
+ [WM8915_WRITE_SEQUENCER_22] = 0x104,
+ [WM8915_WRITE_SEQUENCER_23] = 0xa,
+ [WM8915_WRITE_SEQUENCER_24] = 0x60,
+ [WM8915_WRITE_SEQUENCER_25] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_26] = 0x502,
+ [WM8915_WRITE_SEQUENCER_27] = 0x100,
+ [WM8915_WRITE_SEQUENCER_28] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_32] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_36] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_40] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_44] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_48] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_52] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_56] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_60] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_64] = 0x1,
+ [WM8915_WRITE_SEQUENCER_65] = 0x1,
+ [WM8915_WRITE_SEQUENCER_67] = 0x6,
+ [WM8915_WRITE_SEQUENCER_68] = 0x40,
+ [WM8915_WRITE_SEQUENCER_69] = 0x1,
+ [WM8915_WRITE_SEQUENCER_70] = 0xf,
+ [WM8915_WRITE_SEQUENCER_71] = 0x6,
+ [WM8915_WRITE_SEQUENCER_72] = 0x1,
+ [WM8915_WRITE_SEQUENCER_73] = 0x3,
+ [WM8915_WRITE_SEQUENCER_74] = 0x104,
+ [WM8915_WRITE_SEQUENCER_76] = 0x60,
+ [WM8915_WRITE_SEQUENCER_77] = 0x11,
+ [WM8915_WRITE_SEQUENCER_78] = 0x401,
+ [WM8915_WRITE_SEQUENCER_80] = 0x50,
+ [WM8915_WRITE_SEQUENCER_81] = 0x3,
+ [WM8915_WRITE_SEQUENCER_82] = 0x100,
+ [WM8915_WRITE_SEQUENCER_84] = 0x60,
+ [WM8915_WRITE_SEQUENCER_85] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_86] = 0x502,
+ [WM8915_WRITE_SEQUENCER_87] = 0x100,
+ [WM8915_WRITE_SEQUENCER_88] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_92] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_96] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_100] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_104] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_108] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_112] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_116] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_120] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_124] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_128] = 0x1,
+ [WM8915_WRITE_SEQUENCER_129] = 0x1,
+ [WM8915_WRITE_SEQUENCER_131] = 0x6,
+ [WM8915_WRITE_SEQUENCER_132] = 0x40,
+ [WM8915_WRITE_SEQUENCER_133] = 0x1,
+ [WM8915_WRITE_SEQUENCER_134] = 0xf,
+ [WM8915_WRITE_SEQUENCER_135] = 0x6,
+ [WM8915_WRITE_SEQUENCER_136] = 0x1,
+ [WM8915_WRITE_SEQUENCER_137] = 0x3,
+ [WM8915_WRITE_SEQUENCER_138] = 0x106,
+ [WM8915_WRITE_SEQUENCER_140] = 0x61,
+ [WM8915_WRITE_SEQUENCER_141] = 0x11,
+ [WM8915_WRITE_SEQUENCER_142] = 0x401,
+ [WM8915_WRITE_SEQUENCER_144] = 0x50,
+ [WM8915_WRITE_SEQUENCER_145] = 0x3,
+ [WM8915_WRITE_SEQUENCER_146] = 0x102,
+ [WM8915_WRITE_SEQUENCER_148] = 0x51,
+ [WM8915_WRITE_SEQUENCER_149] = 0x3,
+ [WM8915_WRITE_SEQUENCER_150] = 0x106,
+ [WM8915_WRITE_SEQUENCER_151] = 0xa,
+ [WM8915_WRITE_SEQUENCER_152] = 0x61,
+ [WM8915_WRITE_SEQUENCER_153] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_154] = 0x502,
+ [WM8915_WRITE_SEQUENCER_155] = 0x100,
+ [WM8915_WRITE_SEQUENCER_156] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_160] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_164] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_168] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_172] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_176] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_180] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_184] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_188] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_192] = 0x1,
+ [WM8915_WRITE_SEQUENCER_193] = 0x1,
+ [WM8915_WRITE_SEQUENCER_195] = 0x6,
+ [WM8915_WRITE_SEQUENCER_196] = 0x40,
+ [WM8915_WRITE_SEQUENCER_197] = 0x1,
+ [WM8915_WRITE_SEQUENCER_198] = 0xf,
+ [WM8915_WRITE_SEQUENCER_199] = 0x6,
+ [WM8915_WRITE_SEQUENCER_200] = 0x1,
+ [WM8915_WRITE_SEQUENCER_201] = 0x3,
+ [WM8915_WRITE_SEQUENCER_202] = 0x106,
+ [WM8915_WRITE_SEQUENCER_204] = 0x61,
+ [WM8915_WRITE_SEQUENCER_205] = 0x11,
+ [WM8915_WRITE_SEQUENCER_206] = 0x401,
+ [WM8915_WRITE_SEQUENCER_208] = 0x50,
+ [WM8915_WRITE_SEQUENCER_209] = 0x3,
+ [WM8915_WRITE_SEQUENCER_210] = 0x102,
+ [WM8915_WRITE_SEQUENCER_212] = 0x61,
+ [WM8915_WRITE_SEQUENCER_213] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_214] = 0x502,
+ [WM8915_WRITE_SEQUENCER_215] = 0x100,
+ [WM8915_WRITE_SEQUENCER_216] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_220] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_224] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_228] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_232] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_236] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_240] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_244] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_248] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_252] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_256] = 0x60,
+ [WM8915_WRITE_SEQUENCER_258] = 0x601,
+ [WM8915_WRITE_SEQUENCER_260] = 0x50,
+ [WM8915_WRITE_SEQUENCER_262] = 0x100,
+ [WM8915_WRITE_SEQUENCER_264] = 0x1,
+ [WM8915_WRITE_SEQUENCER_266] = 0x104,
+ [WM8915_WRITE_SEQUENCER_267] = 0x100,
+ [WM8915_WRITE_SEQUENCER_268] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_272] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_276] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_280] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_284] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_288] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_292] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_296] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_300] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_304] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_308] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_312] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_316] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_320] = 0x61,
+ [WM8915_WRITE_SEQUENCER_322] = 0x601,
+ [WM8915_WRITE_SEQUENCER_324] = 0x50,
+ [WM8915_WRITE_SEQUENCER_326] = 0x102,
+ [WM8915_WRITE_SEQUENCER_328] = 0x1,
+ [WM8915_WRITE_SEQUENCER_330] = 0x106,
+ [WM8915_WRITE_SEQUENCER_331] = 0x100,
+ [WM8915_WRITE_SEQUENCER_332] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_336] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_340] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_344] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_348] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_352] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_356] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_360] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_364] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_368] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_372] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_376] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_380] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_384] = 0x60,
+ [WM8915_WRITE_SEQUENCER_386] = 0x601,
+ [WM8915_WRITE_SEQUENCER_388] = 0x61,
+ [WM8915_WRITE_SEQUENCER_390] = 0x601,
+ [WM8915_WRITE_SEQUENCER_392] = 0x50,
+ [WM8915_WRITE_SEQUENCER_394] = 0x300,
+ [WM8915_WRITE_SEQUENCER_396] = 0x1,
+ [WM8915_WRITE_SEQUENCER_398] = 0x304,
+ [WM8915_WRITE_SEQUENCER_400] = 0x40,
+ [WM8915_WRITE_SEQUENCER_402] = 0xf,
+ [WM8915_WRITE_SEQUENCER_404] = 0x1,
+ [WM8915_WRITE_SEQUENCER_407] = 0x100,
+};
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const char *sidetone_hpf_text[] = {
+ "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
+};
+
+static const struct soc_enum sidetone_hpf =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 7, 6, sidetone_hpf_text);
+
+static const char *hpf_mode_text[] = {
+ "HiFi", "Custom", "Voice"
+};
+
+static const struct soc_enum dsp1tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const struct soc_enum dsp2tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const char *hpf_cutoff_text[] = {
+ "50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum dsp1tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static const struct soc_enum dsp2tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static void wm8915_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+ int base, best, best_val, save, i, cfg, iface;
+
+ if (!wm8915->num_retune_mobile_texts)
+ return;
+
+ switch (block) {
+ case 0:
+ base = WM8915_DSP1_RX_EQ_GAINS_1;
+ if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
+ WM8915_DSP1RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ case 1:
+ base = WM8915_DSP1_RX_EQ_GAINS_2;
+ if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
+ WM8915_DSP2RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ default:
+ return;
+ }
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8915->retune_mobile_cfg[block];
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8915->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8915->rx_rate[iface]) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8915->rx_rate[iface]);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ block,
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8915->rx_rate[iface]);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, base);
+ save &= WM8915_DSP1RX_EQ_ENA;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8915_DSP1RX_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8915_get_retune_mobile_block(const char *name)
+{
+ if (strcmp(name, "DSP1 EQ Mode") == 0)
+ return 0;
+ if (strcmp(name, "DSP2 EQ Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int wm8915_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+ int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (block < 0)
+ return block;
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8915->retune_mobile_cfg[block] = value;
+
+ wm8915_set_retune_mobile(codec, block);
+
+ return 0;
+}
+
+static int wm8915_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
+
+ ucontrol->value.enumerated.item[0] = wm8915->retune_mobile_cfg[block];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8915_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),
+
+SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8915_DAC1_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8915_DAC2_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_SINGLE("Sidetone LPF Switch", WM8915_SIDETONE, 12, 1, 0),
+SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8915_SIDETONE, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8915_DSP1_TX_LEFT_VOLUME,
+ WM8915_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8915_DSP2_TX_LEFT_VOLUME,
+ WM8915_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8915_DSP1_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP1 Capture HPF Switch", WM8915_DSP1_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
+SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),
+
+SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8915_DSP2_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP2 Capture HPF Switch", WM8915_DSP2_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
+SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),
+
+SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8915_DSP1_RX_LEFT_VOLUME,
+ WM8915_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP1 Playback Switch", WM8915_DSP1_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8915_DSP2_RX_LEFT_VOLUME,
+ WM8915_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP2 Playback Switch", WM8915_DSP2_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE("Speaker High Performance Switch", WM8915_OVERSAMPLING, 3, 1, 0),
+SOC_SINGLE("DMIC High Performance Switch", WM8915_OVERSAMPLING, 2, 1, 0),
+SOC_SINGLE("ADC High Performance Switch", WM8915_OVERSAMPLING, 1, 1, 0),
+SOC_SINGLE("DAC High Performance Switch", WM8915_OVERSAMPLING, 0, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8915_DAC_SOFTMUTE, 1, 1, 0),
+SOC_SINGLE("DAC Slow Soft Mute Switch", WM8915_DAC_SOFTMUTE, 0, 1, 0),
+
+SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8915_DAC1_HPOUT1_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8915_DAC2_HPOUT2_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+
+SOC_DOUBLE_R_TLV("Output 1 Volume", WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 1 ZC Switch", WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Output 2 Volume", WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 2 ZC Switch", WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_TLV("Speaker Volume", WM8915_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
+ spk_tlv),
+SOC_DOUBLE_R("Speaker Switch", WM8915_LEFT_PDM_SPEAKER,
+ WM8915_RIGHT_PDM_SPEAKER, 3, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8915_LEFT_PDM_SPEAKER,
+ WM8915_RIGHT_PDM_SPEAKER, 2, 1, 0),
+
+SOC_SINGLE("DSP1 EQ Switch", WM8915_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("DSP2 EQ Switch", WM8915_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8915_eq_controls[] = {
+SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(5);
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmv_short_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
+
+ /* Record which outputs we enabled */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ wm8915->hpout_pending &= ~w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8915->hpout_pending |= w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ unsigned long timeout = 200;
+
+ snd_soc_write(codec, WM8915_DC_SERVO_2, mask);
+
+ /* Use the interrupt if possible */
+ do {
+ if (i2c->irq) {
+ timeout = wait_for_completion_timeout(&wm8915->dcs_done,
+ msecs_to_jiffies(200));
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out\n");
+
+ } else {
+ msleep(1);
+ if (--i) {
+ timeout = 0;
+ break;
+ }
+ }
+
+ ret = snd_soc_read(codec, WM8915_DC_SERVO_2);
+ dev_dbg(codec->dev, "DC servo state: %x\n", ret);
+ } while (ret & mask);
+
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out for %x\n", mask);
+ else
+ dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
+}
+
+static void wm8915_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = container_of(dapm,
+ struct snd_soc_codec, dapm);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ u16 val, mask;
+
+ /* Complete any pending DC servo starts */
+ if (wm8915->dcs_pending) {
+ dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ wm8915->dcs_pending);
+
+ /* Trigger a startup sequence */
+ wait_for_dc_servo(codec, wm8915->dcs_pending
+ << WM8915_DCS_TRIG_STARTUP_0_SHIFT);
+
+ wm8915->dcs_pending = 0;
+ }
+
+ if (wm8915->hpout_pending != wm8915->hpout_ena) {
+ dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
+ wm8915->hpout_ena, wm8915->hpout_pending);
+
+ val = 0;
+ mask = 0;
+ if (wm8915->hpout_pending & HPOUT1L) {
+ val |= WM8915_HPOUT1L_RMV_SHORT;
+ mask |= WM8915_HPOUT1L_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT1L_RMV_SHORT |
+ WM8915_HPOUT1L_OUTP |
+ WM8915_HPOUT1L_DLY;
+ }
+
+ if (wm8915->hpout_pending & HPOUT1R) {
+ val |= WM8915_HPOUT1R_RMV_SHORT;
+ mask |= WM8915_HPOUT1R_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT1R_RMV_SHORT |
+ WM8915_HPOUT1R_OUTP |
+ WM8915_HPOUT1R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_1, mask, val);
+
+ val = 0;
+ mask = 0;
+ if (wm8915->hpout_pending & HPOUT2L) {
+ val |= WM8915_HPOUT2L_RMV_SHORT;
+ mask |= WM8915_HPOUT2L_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT2L_RMV_SHORT |
+ WM8915_HPOUT2L_OUTP |
+ WM8915_HPOUT2L_DLY;
+ }
+
+ if (wm8915->hpout_pending & HPOUT2R) {
+ val |= WM8915_HPOUT2R_RMV_SHORT;
+ mask |= WM8915_HPOUT2R_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT2R_RMV_SHORT |
+ WM8915_HPOUT2R_OUTP |
+ WM8915_HPOUT2R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_2, mask, val);
+
+ wm8915->hpout_ena = wm8915->hpout_pending;
+ }
+}
+
+static int dcs_start(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wm8915->dcs_pending |= 1 << w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *sidetone_text[] = {
+ "IN1", "IN2",
+};
+
+static const struct soc_enum left_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 0, 2, sidetone_text);
+
+static const struct snd_kcontrol_new left_sidetone =
+ SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);
+
+static const struct soc_enum right_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 1, 2, sidetone_text);
+
+static const struct snd_kcontrol_new right_sidetone =
+ SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);
+
+static const char *spk_text[] = {
+ "DAC1L", "DAC1R", "DAC2L", "DAC2R"
+};
+
+static const struct soc_enum spkl_enum =
+ SOC_ENUM_SINGLE(WM8915_LEFT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkl_mux =
+ SOC_DAPM_ENUM("SPKL", spkl_enum);
+
+static const struct soc_enum spkr_enum =
+ SOC_ENUM_SINGLE(WM8915_RIGHT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkr_mux =
+ SOC_DAPM_ENUM("SPKR", spkr_enum);
+
+static const char *dsp1rx_text[] = {
+ "AIF1", "AIF2"
+};
+
+static const struct soc_enum dsp1rx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);
+
+static const struct snd_kcontrol_new dsp1rx =
+ SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);
+
+static const char *dsp2rx_text[] = {
+ "AIF2", "AIF1"
+};
+
+static const struct soc_enum dsp2rx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);
+
+static const struct snd_kcontrol_new dsp2rx =
+ SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);
+
+static const char *aif2tx_text[] = {
+ "DSP2", "DSP1", "AIF1"
+};
+
+static const struct soc_enum aif2tx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);
+
+static const struct snd_kcontrol_new aif2tx =
+ SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);
+
+static const char *inmux_text[] = {
+ "ADC", "DMIC1", "DMIC2"
+};
+
+static const struct soc_enum in1_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 0, 3, inmux_text);
+
+static const struct snd_kcontrol_new in1_mux =
+ SOC_DAPM_ENUM("IN1 Mux", in1_enum);
+
+static const struct soc_enum in2_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 4, 3, inmux_text);
+
+static const struct snd_kcontrol_new in2_mux =
+ SOC_DAPM_ENUM("IN2 Mux", in2_enum);
+
+static const struct snd_kcontrol_new dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+
+static const struct snd_soc_dapm_widget wm8915_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8915_AIF_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8915_CLOCKING_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8915_CLOCKING_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8915_CHARGE_PUMP_1, 15, 0, cp_event,
+ SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("LDO2", WM8915_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("MICB2", WM8915_POWER_MANAGEMENT_1, 9, 0),
+SND_SOC_DAPM_MICBIAS("MICB1", WM8915_POWER_MANAGEMENT_1, 8, 0),
+
+SND_SOC_DAPM_PGA("IN1L PGA", WM8915_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R PGA", WM8915_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+
+SND_SOC_DAPM_PGA("IN1L", WM8915_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R", WM8915_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2L", WM8915_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2R", WM8915_POWER_MANAGEMENT_7, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DMIC2", WM8915_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DMIC1", WM8915_POWER_MANAGEMENT_7, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8915_POWER_MANAGEMENT_3, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8915_POWER_MANAGEMENT_3, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8915_POWER_MANAGEMENT_3, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8915_POWER_MANAGEMENT_3, 2, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8915_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8915_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),
+
+SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 11, 0),
+SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 10, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 9, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 8, 0),
+
+SND_SOC_DAPM_MIXER("DSP2TXL", WM8915_POWER_MANAGEMENT_5, 11, 0,
+ dsp2txl, ARRAY_SIZE(dsp2txl)),
+SND_SOC_DAPM_MIXER("DSP2TXR", WM8915_POWER_MANAGEMENT_5, 10, 0,
+ dsp2txr, ARRAY_SIZE(dsp2txr)),
+SND_SOC_DAPM_MIXER("DSP1TXL", WM8915_POWER_MANAGEMENT_5, 9, 0,
+ dsp1txl, ARRAY_SIZE(dsp1txl)),
+SND_SOC_DAPM_MIXER("DSP1TXR", WM8915_POWER_MANAGEMENT_5, 8, 0,
+ dsp1txr, ARRAY_SIZE(dsp1txr)),
+
+SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ dac2l_mix, ARRAY_SIZE(dac2l_mix)),
+SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ dac2r_mix, ARRAY_SIZE(dac2r_mix)),
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+ dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+ dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8915_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8915_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8915_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8915_POWER_MANAGEMENT_5, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
+ WM8915_POWER_MANAGEMENT_4, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
+ WM8915_POWER_MANAGEMENT_4, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
+ WM8915_POWER_MANAGEMENT_6, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
+ WM8915_POWER_MANAGEMENT_6, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
+ WM8915_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 4,
+ WM8915_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 3,
+ WM8915_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 2,
+ WM8915_POWER_MANAGEMENT_4, 2, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 1,
+ WM8915_POWER_MANAGEMENT_4, 1, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX0", "AIF1 Playback", 0,
+ WM8915_POWER_MANAGEMENT_4, 0, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 5,
+ WM8915_POWER_MANAGEMENT_6, 5, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 4,
+ WM8915_POWER_MANAGEMENT_6, 4, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 3,
+ WM8915_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 2,
+ WM8915_POWER_MANAGEMENT_6, 2, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 1,
+ WM8915_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX0", "AIF1 Capture", 0,
+ WM8915_POWER_MANAGEMENT_6, 0, 0),
+
+/* We route as stereo pairs so define some dummy widgets to squash
+ * things down for now. RXA = 0,1, RXB = 2,3 and so on */
+SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
+SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
+SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),
+
+SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
+SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
+SND_SOC_DAPM_PGA("SPKL PGA", WM8915_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKR PGA", WM8915_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8915_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8915_ANALOGUE_HP_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8915_DC_SERVO_1, 2, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2L_OUTP", 3, WM8915_ANALOGUE_HP_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8915_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8915_ANALOGUE_HP_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8915_DC_SERVO_1, 3, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2R_OUTP", 3, WM8915_ANALOGUE_HP_2, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8915_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8915_ANALOGUE_HP_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8915_DC_SERVO_1, 0, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1L_OUTP", 3, WM8915_ANALOGUE_HP_1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8915_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8915_ANALOGUE_HP_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8915_DC_SERVO_1, 1, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1R_OUTP", 3, WM8915_ANALOGUE_HP_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT"),
+};
+
+static const struct snd_soc_dapm_route wm8915_dapm_routes[] = {
+ { "AIFCLK", NULL, "SYSCLK" },
+ { "SYSDSPCLK", NULL, "SYSCLK" },
+ { "Charge Pump", NULL, "SYSCLK" },
+
+ { "MICB1", NULL, "LDO2" },
+ { "MICB2", NULL, "LDO2" },
+
+ { "IN1L PGA", NULL, "IN2LN" },
+ { "IN1L PGA", NULL, "IN2LP" },
+ { "IN1L PGA", NULL, "IN1LN" },
+ { "IN1L PGA", NULL, "IN1LP" },
+
+ { "IN1R PGA", NULL, "IN2RN" },
+ { "IN1R PGA", NULL, "IN2RP" },
+ { "IN1R PGA", NULL, "IN1RN" },
+ { "IN1R PGA", NULL, "IN1RP" },
+
+ { "ADCL", NULL, "IN1L PGA" },
+
+ { "ADCR", NULL, "IN1R PGA" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+
+ { "DMIC2L", NULL, "DMIC2" },
+ { "DMIC2R", NULL, "DMIC2" },
+ { "DMIC1L", NULL, "DMIC1" },
+ { "DMIC1R", NULL, "DMIC1" },
+
+ { "IN1L Mux", "ADC", "ADCL" },
+ { "IN1L Mux", "DMIC1", "DMIC1L" },
+ { "IN1L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN1R Mux", "ADC", "ADCR" },
+ { "IN1R Mux", "DMIC1", "DMIC1R" },
+ { "IN1R Mux", "DMIC2", "DMIC2R" },
+
+ { "IN2L Mux", "ADC", "ADCL" },
+ { "IN2L Mux", "DMIC1", "DMIC1L" },
+ { "IN2L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN2R Mux", "ADC", "ADCR" },
+ { "IN2R Mux", "DMIC1", "DMIC1R" },
+ { "IN2R Mux", "DMIC2", "DMIC2R" },
+
+ { "Left Sidetone", "IN1", "IN1L Mux" },
+ { "Left Sidetone", "IN2", "IN2L Mux" },
+
+ { "Right Sidetone", "IN1", "IN1R Mux" },
+ { "Right Sidetone", "IN2", "IN2R Mux" },
+
+ { "DSP1TXL", "IN1 Switch", "IN1L Mux" },
+ { "DSP1TXR", "IN1 Switch", "IN1R Mux" },
+
+ { "DSP2TXL", "IN1 Switch", "IN2L Mux" },
+ { "DSP2TXR", "IN1 Switch", "IN2R Mux" },
+
+ { "AIF1TX0", NULL, "DSP1TXL" },
+ { "AIF1TX1", NULL, "DSP1TXR" },
+ { "AIF1TX2", NULL, "DSP2TXL" },
+ { "AIF1TX3", NULL, "DSP2TXR" },
+ { "AIF1TX4", NULL, "AIF2RX0" },
+ { "AIF1TX5", NULL, "AIF2RX1" },
+
+ { "AIF1RX0", NULL, "AIFCLK" },
+ { "AIF1RX1", NULL, "AIFCLK" },
+ { "AIF1RX2", NULL, "AIFCLK" },
+ { "AIF1RX3", NULL, "AIFCLK" },
+ { "AIF1RX4", NULL, "AIFCLK" },
+ { "AIF1RX5", NULL, "AIFCLK" },
+
+ { "AIF2RX0", NULL, "AIFCLK" },
+ { "AIF2RX1", NULL, "AIFCLK" },
+
+ { "DSP1RXL", NULL, "SYSDSPCLK" },
+ { "DSP1RXR", NULL, "SYSDSPCLK" },
+ { "DSP2RXL", NULL, "SYSDSPCLK" },
+ { "DSP2RXR", NULL, "SYSDSPCLK" },
+ { "DSP1TXL", NULL, "SYSDSPCLK" },
+ { "DSP1TXR", NULL, "SYSDSPCLK" },
+ { "DSP2TXL", NULL, "SYSDSPCLK" },
+ { "DSP2TXR", NULL, "SYSDSPCLK" },
+
+ { "AIF1RXA", NULL, "AIF1RX0" },
+ { "AIF1RXA", NULL, "AIF1RX1" },
+ { "AIF1RXB", NULL, "AIF1RX2" },
+ { "AIF1RXB", NULL, "AIF1RX3" },
+ { "AIF1RXC", NULL, "AIF1RX4" },
+ { "AIF1RXC", NULL, "AIF1RX5" },
+
+ { "AIF2RX", NULL, "AIF2RX0" },
+ { "AIF2RX", NULL, "AIF2RX1" },
+
+ { "AIF2TX", "DSP2", "DSP2TX" },
+ { "AIF2TX", "DSP1", "DSP1RX" },
+ { "AIF2TX", "AIF1", "AIF1RXC" },
+
+ { "DSP1RXL", NULL, "DSP1RX" },
+ { "DSP1RXR", NULL, "DSP1RX" },
+ { "DSP2RXL", NULL, "DSP2RX" },
+ { "DSP2RXR", NULL, "DSP2RX" },
+
+ { "DSP2TX", NULL, "DSP2TXL" },
+ { "DSP2TX", NULL, "DSP2TXR" },
+
+ { "DSP1RX", "AIF1", "AIF1RXA" },
+ { "DSP1RX", "AIF2", "AIF2RX" },
+
+ { "DSP2RX", "AIF1", "AIF1RXB" },
+ { "DSP2RX", "AIF2", "AIF2RX" },
+
+ { "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC2L", NULL, "DAC2L Mixer" },
+ { "DAC2R", NULL, "DAC2R Mixer" },
+
+ { "HPOUT2L PGA", NULL, "Charge Pump" },
+ { "HPOUT2L PGA", NULL, "DAC2L" },
+ { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
+ { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
+ { "HPOUT2L_OUTP", NULL, "HPOUT2L_DCS" },
+ { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" },
+
+ { "HPOUT2R PGA", NULL, "Charge Pump" },
+ { "HPOUT2R PGA", NULL, "DAC2R" },
+ { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
+ { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
+ { "HPOUT2R_OUTP", NULL, "HPOUT2R_DCS" },
+ { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" },
+
+ { "HPOUT1L PGA", NULL, "Charge Pump" },
+ { "HPOUT1L PGA", NULL, "DAC1L" },
+ { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
+ { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
+ { "HPOUT1L_OUTP", NULL, "HPOUT1L_DCS" },
+ { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" },
+
+ { "HPOUT1R PGA", NULL, "Charge Pump" },
+ { "HPOUT1R PGA", NULL, "DAC1R" },
+ { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
+ { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
+ { "HPOUT1R_OUTP", NULL, "HPOUT1R_DCS" },
+ { "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_OUTP" },
+
+ { "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" },
+ { "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" },
+ { "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" },
+ { "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" },
+
+ { "SPKL", "DAC1L", "DAC1L" },
+ { "SPKL", "DAC1R", "DAC1R" },
+ { "SPKL", "DAC2L", "DAC2L" },
+ { "SPKL", "DAC2R", "DAC2R" },
+
+ { "SPKR", "DAC1L", "DAC1L" },
+ { "SPKR", "DAC1R", "DAC1R" },
+ { "SPKR", "DAC2L", "DAC2L" },
+ { "SPKR", "DAC2R", "DAC2R" },
+
+ { "SPKL PGA", NULL, "SPKL" },
+ { "SPKR PGA", NULL, "SPKR" },
+
+ { "SPKDAT", NULL, "SPKL PGA" },
+ { "SPKDAT", NULL, "SPKR PGA" },
+};
+
+static int wm8915_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ /* Due to the sparseness of the register map the compiler
+ * output from an explicit switch statement ends up being much
+ * more efficient than a table.
+ */
+ switch (reg) {
+ case WM8915_SOFTWARE_RESET:
+ case WM8915_POWER_MANAGEMENT_1:
+ case WM8915_POWER_MANAGEMENT_2:
+ case WM8915_POWER_MANAGEMENT_3:
+ case WM8915_POWER_MANAGEMENT_4:
+ case WM8915_POWER_MANAGEMENT_5:
+ case WM8915_POWER_MANAGEMENT_6:
+ case WM8915_POWER_MANAGEMENT_7:
+ case WM8915_POWER_MANAGEMENT_8:
+ case WM8915_LEFT_LINE_INPUT_VOLUME:
+ case WM8915_RIGHT_LINE_INPUT_VOLUME:
+ case WM8915_LINE_INPUT_CONTROL:
+ case WM8915_DAC1_HPOUT1_VOLUME:
+ case WM8915_DAC2_HPOUT2_VOLUME:
+ case WM8915_DAC1_LEFT_VOLUME:
+ case WM8915_DAC1_RIGHT_VOLUME:
+ case WM8915_DAC2_LEFT_VOLUME:
+ case WM8915_DAC2_RIGHT_VOLUME:
+ case WM8915_OUTPUT1_LEFT_VOLUME:
+ case WM8915_OUTPUT1_RIGHT_VOLUME:
+ case WM8915_OUTPUT2_LEFT_VOLUME:
+ case WM8915_OUTPUT2_RIGHT_VOLUME:
+ case WM8915_MICBIAS_1:
+ case WM8915_MICBIAS_2:
+ case WM8915_LDO_1:
+ case WM8915_LDO_2:
+ case WM8915_ACCESSORY_DETECT_MODE_1:
+ case WM8915_ACCESSORY_DETECT_MODE_2:
+ case WM8915_HEADPHONE_DETECT_1:
+ case WM8915_HEADPHONE_DETECT_2:
+ case WM8915_MIC_DETECT_1:
+ case WM8915_MIC_DETECT_2:
+ case WM8915_MIC_DETECT_3:
+ case WM8915_CHARGE_PUMP_1:
+ case WM8915_CHARGE_PUMP_2:
+ case WM8915_DC_SERVO_1:
+ case WM8915_DC_SERVO_2:
+ case WM8915_DC_SERVO_3:
+ case WM8915_DC_SERVO_5:
+ case WM8915_DC_SERVO_6:
+ case WM8915_DC_SERVO_7:
+ case WM8915_DC_SERVO_READBACK_0:
+ case WM8915_ANALOGUE_HP_1:
+ case WM8915_ANALOGUE_HP_2:
+ case WM8915_CHIP_REVISION:
+ case WM8915_CONTROL_INTERFACE_1:
+ case WM8915_WRITE_SEQUENCER_CTRL_1:
+ case WM8915_WRITE_SEQUENCER_CTRL_2:
+ case WM8915_AIF_CLOCKING_1:
+ case WM8915_AIF_CLOCKING_2:
+ case WM8915_CLOCKING_1:
+ case WM8915_CLOCKING_2:
+ case WM8915_AIF_RATE:
+ case WM8915_FLL_CONTROL_1:
+ case WM8915_FLL_CONTROL_2:
+ case WM8915_FLL_CONTROL_3:
+ case WM8915_FLL_CONTROL_4:
+ case WM8915_FLL_CONTROL_5:
+ case WM8915_FLL_CONTROL_6:
+ case WM8915_FLL_EFS_1:
+ case WM8915_FLL_EFS_2:
+ case WM8915_AIF1_CONTROL:
+ case WM8915_AIF1_BCLK:
+ case WM8915_AIF1_TX_LRCLK_1:
+ case WM8915_AIF1_TX_LRCLK_2:
+ case WM8915_AIF1_RX_LRCLK_1:
+ case WM8915_AIF1_RX_LRCLK_2:
+ case WM8915_AIF1TX_DATA_CONFIGURATION_1:
+ case WM8915_AIF1TX_DATA_CONFIGURATION_2:
+ case WM8915_AIF1RX_DATA_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_2_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_3_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_4_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_5_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_2_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_3_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_4_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_5_CONFIGURATION:
+ case WM8915_AIF1RX_MONO_CONFIGURATION:
+ case WM8915_AIF1TX_TEST:
+ case WM8915_AIF2_CONTROL:
+ case WM8915_AIF2_BCLK:
+ case WM8915_AIF2_TX_LRCLK_1:
+ case WM8915_AIF2_TX_LRCLK_2:
+ case WM8915_AIF2_RX_LRCLK_1:
+ case WM8915_AIF2_RX_LRCLK_2:
+ case WM8915_AIF2TX_DATA_CONFIGURATION_1:
+ case WM8915_AIF2TX_DATA_CONFIGURATION_2:
+ case WM8915_AIF2RX_DATA_CONFIGURATION:
+ case WM8915_AIF2TX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF2TX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF2RX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF2RX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF2RX_MONO_CONFIGURATION:
+ case WM8915_AIF2TX_TEST:
+ case WM8915_DSP1_TX_LEFT_VOLUME:
+ case WM8915_DSP1_TX_RIGHT_VOLUME:
+ case WM8915_DSP1_RX_LEFT_VOLUME:
+ case WM8915_DSP1_RX_RIGHT_VOLUME:
+ case WM8915_DSP1_TX_FILTERS:
+ case WM8915_DSP1_RX_FILTERS_1:
+ case WM8915_DSP1_RX_FILTERS_2:
+ case WM8915_DSP1_DRC_1:
+ case WM8915_DSP1_DRC_2:
+ case WM8915_DSP1_DRC_3:
+ case WM8915_DSP1_DRC_4:
+ case WM8915_DSP1_DRC_5:
+ case WM8915_DSP1_RX_EQ_GAINS_1:
+ case WM8915_DSP1_RX_EQ_GAINS_2:
+ case WM8915_DSP1_RX_EQ_BAND_1_A:
+ case WM8915_DSP1_RX_EQ_BAND_1_B:
+ case WM8915_DSP1_RX_EQ_BAND_1_PG:
+ case WM8915_DSP1_RX_EQ_BAND_2_A:
+ case WM8915_DSP1_RX_EQ_BAND_2_B:
+ case WM8915_DSP1_RX_EQ_BAND_2_C:
+ case WM8915_DSP1_RX_EQ_BAND_2_PG:
+ case WM8915_DSP1_RX_EQ_BAND_3_A:
+ case WM8915_DSP1_RX_EQ_BAND_3_B:
+ case WM8915_DSP1_RX_EQ_BAND_3_C:
+ case WM8915_DSP1_RX_EQ_BAND_3_PG:
+ case WM8915_DSP1_RX_EQ_BAND_4_A:
+ case WM8915_DSP1_RX_EQ_BAND_4_B:
+ case WM8915_DSP1_RX_EQ_BAND_4_C:
+ case WM8915_DSP1_RX_EQ_BAND_4_PG:
+ case WM8915_DSP1_RX_EQ_BAND_5_A:
+ case WM8915_DSP1_RX_EQ_BAND_5_B:
+ case WM8915_DSP1_RX_EQ_BAND_5_PG:
+ case WM8915_DSP2_TX_LEFT_VOLUME:
+ case WM8915_DSP2_TX_RIGHT_VOLUME:
+ case WM8915_DSP2_RX_LEFT_VOLUME:
+ case WM8915_DSP2_RX_RIGHT_VOLUME:
+ case WM8915_DSP2_TX_FILTERS:
+ case WM8915_DSP2_RX_FILTERS_1:
+ case WM8915_DSP2_RX_FILTERS_2:
+ case WM8915_DSP2_DRC_1:
+ case WM8915_DSP2_DRC_2:
+ case WM8915_DSP2_DRC_3:
+ case WM8915_DSP2_DRC_4:
+ case WM8915_DSP2_DRC_5:
+ case WM8915_DSP2_RX_EQ_GAINS_1:
+ case WM8915_DSP2_RX_EQ_GAINS_2:
+ case WM8915_DSP2_RX_EQ_BAND_1_A:
+ case WM8915_DSP2_RX_EQ_BAND_1_B:
+ case WM8915_DSP2_RX_EQ_BAND_1_PG:
+ case WM8915_DSP2_RX_EQ_BAND_2_A:
+ case WM8915_DSP2_RX_EQ_BAND_2_B:
+ case WM8915_DSP2_RX_EQ_BAND_2_C:
+ case WM8915_DSP2_RX_EQ_BAND_2_PG:
+ case WM8915_DSP2_RX_EQ_BAND_3_A:
+ case WM8915_DSP2_RX_EQ_BAND_3_B:
+ case WM8915_DSP2_RX_EQ_BAND_3_C:
+ case WM8915_DSP2_RX_EQ_BAND_3_PG:
+ case WM8915_DSP2_RX_EQ_BAND_4_A:
+ case WM8915_DSP2_RX_EQ_BAND_4_B:
+ case WM8915_DSP2_RX_EQ_BAND_4_C:
+ case WM8915_DSP2_RX_EQ_BAND_4_PG:
+ case WM8915_DSP2_RX_EQ_BAND_5_A:
+ case WM8915_DSP2_RX_EQ_BAND_5_B:
+ case WM8915_DSP2_RX_EQ_BAND_5_PG:
+ case WM8915_DAC1_MIXER_VOLUMES:
+ case WM8915_DAC1_LEFT_MIXER_ROUTING:
+ case WM8915_DAC1_RIGHT_MIXER_ROUTING:
+ case WM8915_DAC2_MIXER_VOLUMES:
+ case WM8915_DAC2_LEFT_MIXER_ROUTING:
+ case WM8915_DAC2_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP1_TX_LEFT_MIXER_ROUTING:
+ case WM8915_DSP1_TX_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP2_TX_LEFT_MIXER_ROUTING:
+ case WM8915_DSP2_TX_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP_TX_MIXER_SELECT:
+ case WM8915_DAC_SOFTMUTE:
+ case WM8915_OVERSAMPLING:
+ case WM8915_SIDETONE:
+ case WM8915_GPIO_1:
+ case WM8915_GPIO_2:
+ case WM8915_GPIO_3:
+ case WM8915_GPIO_4:
+ case WM8915_GPIO_5:
+ case WM8915_PULL_CONTROL_1:
+ case WM8915_PULL_CONTROL_2:
+ case WM8915_INTERRUPT_STATUS_1:
+ case WM8915_INTERRUPT_STATUS_2:
+ case WM8915_INTERRUPT_RAW_STATUS_2:
+ case WM8915_INTERRUPT_STATUS_1_MASK:
+ case WM8915_INTERRUPT_STATUS_2_MASK:
+ case WM8915_INTERRUPT_CONTROL:
+ case WM8915_LEFT_PDM_SPEAKER:
+ case WM8915_RIGHT_PDM_SPEAKER:
+ case WM8915_PDM_SPEAKER_MUTE_SEQUENCE:
+ case WM8915_PDM_SPEAKER_VOLUME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8915_volatile_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WM8915_SOFTWARE_RESET:
+ case WM8915_CHIP_REVISION:
+ case WM8915_LDO_1:
+ case WM8915_LDO_2:
+ case WM8915_INTERRUPT_STATUS_1:
+ case WM8915_INTERRUPT_STATUS_2:
+ case WM8915_INTERRUPT_RAW_STATUS_2:
+ case WM8915_DC_SERVO_READBACK_0:
+ case WM8915_DC_SERVO_2:
+ case WM8915_DC_SERVO_6:
+ case WM8915_DC_SERVO_7:
+ case WM8915_FLL_CONTROL_6:
+ case WM8915_MIC_DETECT_3:
+ case WM8915_HEADPHONE_DETECT_1:
+ case WM8915_HEADPHONE_DETECT_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8915_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915);
+}
+
+static int wm8915_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
+ WM8915_BG_ENA, WM8915_BG_ENA);
+ msleep(2);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena,
+ 1);
+ msleep(5);
+ }
+
+ codec->cache_only = false;
+ snd_soc_cache_sync(codec);
+ }
+
+ snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
+ WM8915_BG_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ codec->cache_only = true;
+ if (wm8915->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int aifctrl = 0;
+ int bclk = 0;
+ int lrclk_tx = 0;
+ int lrclk_rx = 0;
+ int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg;
+
+ switch (dai->id) {
+ case 0:
+ aifctrl_reg = WM8915_AIF1_CONTROL;
+ bclk_reg = WM8915_AIF1_BCLK;
+ lrclk_tx_reg = WM8915_AIF1_TX_LRCLK_2;
+ lrclk_rx_reg = WM8915_AIF1_RX_LRCLK_2;
+ break;
+ case 1:
+ aifctrl_reg = WM8915_AIF2_CONTROL;
+ bclk_reg = WM8915_AIF2_BCLK;
+ lrclk_tx_reg = WM8915_AIF2_TX_LRCLK_2;
+ lrclk_rx_reg = WM8915_AIF2_RX_LRCLK_2;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= WM8915_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= WM8915_AIF1_BCLK_INV;
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= WM8915_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= WM8915_AIF1_BCLK_MSTR;
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aifctrl |= 1;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aifctrl |= 2;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aifctrl |= 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, aifctrl_reg, WM8915_AIF1_FMT_MASK, aifctrl);
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8915_AIF1_BCLK_INV | WM8915_AIF1_BCLK_MSTR,
+ bclk);
+ snd_soc_update_bits(codec, lrclk_tx_reg,
+ WM8915_AIF1TX_LRCLK_INV |
+ WM8915_AIF1TX_LRCLK_MSTR,
+ lrclk_tx);
+ snd_soc_update_bits(codec, lrclk_rx_reg,
+ WM8915_AIF1RX_LRCLK_INV |
+ WM8915_AIF1RX_LRCLK_MSTR,
+ lrclk_rx);
+
+ return 0;
+}
+
+static const int bclk_divs[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static const int dsp_divs[] = {
+ 48000, 32000, 16000, 8000
+};
+
+static int wm8915_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 wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int bits, i, bclk_rate, best, cur_val;
+ int aifdata = 0;
+ int bclk = 0;
+ int lrclk = 0;
+ int dsp = 0;
+ int aifdata_reg, bclk_reg, lrclk_reg, dsp_shift;
+
+ if (!wm8915->sysclk) {
+ dev_err(codec->dev, "SYSCLK not configured\n");
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case 0:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8915_GPIO_1)) & WM8915_GP1_FN_MASK) {
+ aifdata_reg = WM8915_AIF1RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8915_AIF1_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8915_AIF1TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8915_AIF1_TX_LRCLK_1;
+ }
+ bclk_reg = WM8915_AIF1_BCLK;
+ dsp_shift = 0;
+ break;
+ case 1:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8915_GPIO_2)) & WM8915_GP2_FN_MASK) {
+ aifdata_reg = WM8915_AIF2RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8915_AIF2_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8915_AIF2TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8915_AIF2_TX_LRCLK_1;
+ }
+ bclk_reg = WM8915_AIF2_BCLK;
+ dsp_shift = WM8915_DSP2_DIV_SHIFT;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
+ return bclk_rate;
+ }
+
+ /* Needs looking at for TDM */
+ bits = snd_pcm_format_width(params_format(params));
+ if (bits < 0)
+ return bits;
+ aifdata |= (bits << WM8915_AIF1TX_WL_SHIFT) | bits;
+
+ for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) {
+ if (dsp_divs[i] == params_rate(params))
+ break;
+ }
+ if (i == ARRAY_SIZE(dsp_divs)) {
+ dev_err(codec->dev, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ dsp |= i << dsp_shift;
+
+ /* Pick a divisor for BCLK as close as we can get to ideal */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk_rate = wm8915->sysclk / bclk_divs[best];
+ dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+ bclk |= best;
+
+ lrclk = bclk_rate / params_rate(params);
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aifdata_reg,
+ WM8915_AIF1TX_WL_MASK |
+ WM8915_AIF1TX_SLOT_LEN_MASK,
+ aifdata);
+ snd_soc_update_bits(codec, bclk_reg, WM8915_AIF1_BCLK_DIV_MASK, bclk);
+ snd_soc_update_bits(codec, lrclk_reg, WM8915_AIF1RX_RATE_MASK,
+ lrclk);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_2,
+ WM8915_DSP1_DIV_SHIFT << dsp_shift, dsp);
+
+ wm8915->rx_rate[dai->id] = params_rate(params);
+
+ return 0;
+}
+
+static int wm8915_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int lfclk = 0;
+ int ratediv = 0;
+ int src;
+ int old;
+
+ /* Disable SYSCLK while we reconfigure */
+ old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_ENA, 0);
+
+ switch (clk_id) {
+ case WM8915_SYSCLK_MCLK1:
+ wm8915->sysclk = freq;
+ src = 0;
+ break;
+ case WM8915_SYSCLK_MCLK2:
+ wm8915->sysclk = freq;
+ src = 1;
+ break;
+ case WM8915_SYSCLK_FLL:
+ wm8915->sysclk = freq;
+ src = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ switch (wm8915->sysclk) {
+ case 6144000:
+ snd_soc_update_bits(codec, WM8915_AIF_RATE,
+ WM8915_SYSCLK_RATE, 0);
+ break;
+ case 24576000:
+ ratediv = WM8915_SYSCLK_DIV;
+ case 12288000:
+ snd_soc_update_bits(codec, WM8915_AIF_RATE,
+ WM8915_SYSCLK_RATE, WM8915_SYSCLK_RATE);
+ break;
+ case 32000:
+ case 32768:
+ lfclk = WM8915_LFCLK_ENA;
+ break;
+ default:
+ dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
+ wm8915->sysclk);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK,
+ src << WM8915_SYSCLK_SRC_SHIFT | ratediv);
+ snd_soc_update_bits(codec, WM8915_CLOCKING_1, WM8915_LFCLK_ENA, lfclk);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_ENA, old);
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 fll_loop_gain;
+ u16 fll_ref_freq;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ if (Fref >= 3000000)
+ fll_div->fll_loop_gain = 5;
+ else
+ fll_div->fll_loop_gain = 0;
+
+ if (Fref >= 48000)
+ fll_div->fll_ref_freq = 0;
+ else
+ fll_div->fll_ref_freq = 1;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div fll_div;
+ unsigned long timeout;
+ int ret, reg;
+
+ /* Any change? */
+ if (source == wm8915->fll_src && Fref == wm8915->fll_fref &&
+ Fout == wm8915->fll_fout)
+ return 0;
+
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+
+ wm8915->fll_fref = 0;
+ wm8915->fll_fout = 0;
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
+ WM8915_FLL_ENA, 0);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ switch (source) {
+ case WM8915_FLL_MCLK1:
+ reg = 0;
+ break;
+ case WM8915_FLL_MCLK2:
+ reg = 1;
+ case WM8915_FLL_DACLRCLK1:
+ reg = 2;
+ break;
+ case WM8915_FLL_BCLK1:
+ reg = 3;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ return -EINVAL;
+ }
+
+ reg |= fll_div.fll_refclk_div << WM8915_FLL_REFCLK_DIV_SHIFT;
+ reg |= fll_div.fll_ref_freq << WM8915_FLL_REF_FREQ_SHIFT;
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_5,
+ WM8915_FLL_REFCLK_DIV_MASK | WM8915_FLL_REF_FREQ |
+ WM8915_FLL_REFCLK_SRC_MASK, reg);
+
+ reg = 0;
+ if (fll_div.theta || fll_div.lambda)
+ reg |= WM8915_FLL_EFS_ENA | (3 << WM8915_FLL_LFSR_SEL_SHIFT);
+ else
+ reg |= 1 << WM8915_FLL_LFSR_SEL_SHIFT;
+ snd_soc_write(codec, WM8915_FLL_EFS_2, reg);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_2,
+ WM8915_FLL_OUTDIV_MASK |
+ WM8915_FLL_FRATIO_MASK,
+ (fll_div.fll_outdiv << WM8915_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio));
+
+ snd_soc_write(codec, WM8915_FLL_CONTROL_3, fll_div.theta);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_4,
+ WM8915_FLL_N_MASK | WM8915_FLL_LOOP_GAIN_MASK,
+ (fll_div.n << WM8915_FLL_N_SHIFT) |
+ fll_div.fll_loop_gain);
+
+ snd_soc_write(codec, WM8915_FLL_EFS_1, fll_div.lambda);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
+ WM8915_FLL_ENA, WM8915_FLL_ENA);
+
+ /* The FLL supports live reconfiguration - kick that in case we were
+ * already enabled.
+ */
+ snd_soc_write(codec, WM8915_FLL_CONTROL_6, WM8915_FLL_SWITCH_CLK);
+
+ /* Wait for the FLL to lock, using the interrupt if possible */
+ if (Fref > 1000000)
+ timeout = usecs_to_jiffies(300);
+ else
+ timeout = msecs_to_jiffies(2);
+
+ wait_for_completion_timeout(&wm8915->fll_lock, timeout);
+
+ dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+ wm8915->fll_fref = Fref;
+ wm8915->fll_fout = Fout;
+ wm8915->fll_src = source;
+
+ return 0;
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8915_priv *gpio_to_wm8915(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8915_priv, gpio_chip);
+}
+
+static void wm8915_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+
+ snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_LVL, !!value << WM8915_GP1_LVL_SHIFT);
+}
+
+static int wm8915_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+ int val;
+
+ val = (1 << WM8915_GP1_FN_SHIFT) | (!!value << WM8915_GP1_LVL_SHIFT);
+
+ return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_FN_MASK | WM8915_GP1_DIR |
+ WM8915_GP1_LVL, val);
+}
+
+static int wm8915_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+ int ret;
+
+ ret = snd_soc_read(codec, WM8915_GPIO_1 + offset);
+ if (ret < 0)
+ return ret;
+
+ return (ret & WM8915_GP1_LVL) != 0;
+}
+
+static int wm8915_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+
+ return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_FN_MASK | WM8915_GP1_DIR,
+ (1 << WM8915_GP1_FN_SHIFT) |
+ (1 << WM8915_GP1_DIR_SHIFT));
+}
+
+static struct gpio_chip wm8915_template_chip = {
+ .label = "wm8915",
+ .owner = THIS_MODULE,
+ .direction_output = wm8915_gpio_direction_out,
+ .set = wm8915_gpio_set,
+ .direction_input = wm8915_gpio_direction_in,
+ .get = wm8915_gpio_get,
+ .can_sleep = 1,
+};
+
+static void wm8915_init_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm8915->gpio_chip = wm8915_template_chip;
+ wm8915->gpio_chip.ngpio = 5;
+ wm8915->gpio_chip.dev = codec->dev;
+
+ if (wm8915->pdata.gpio_base)
+ wm8915->gpio_chip.base = wm8915->pdata.gpio_base;
+ else
+ wm8915->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8915->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8915_free_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = gpiochip_remove(&wm8915->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+}
+#else
+static void wm8915_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8915_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+/**
+ * wm8915_detect - Enable default WM8915 jack detection
+ *
+ * The WM8915 has advanced accessory detection support for headsets.
+ * This function provides a default implementation which integrates
+ * the majority of this functionality with minimal user configuration.
+ *
+ * This will detect headset, headphone and short circuit button and
+ * will also detect inverted microphone ground connections and update
+ * the polarity of the connections.
+ */
+int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8915_polarity_fn polarity_cb)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+
+ wm8915->jack = jack;
+ wm8915->detecting = true;
+ wm8915->polarity_cb = polarity_cb;
+
+ if (wm8915->polarity_cb)
+ wm8915->polarity_cb(codec, 0);
+
+ /* Clear discarge to avoid noise during detection */
+ snd_soc_update_bits(codec, WM8915_MICBIAS_1,
+ WM8915_MICB1_DISCH, 0);
+ snd_soc_update_bits(codec, WM8915_MICBIAS_2,
+ WM8915_MICB2_DISCH, 0);
+
+ /* LDO2 powers the microphones, SYSCLK clocks detection */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
+
+ /* We start off just enabling microphone detection - even a
+ * plain headphone will trigger detection.
+ */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_ENA, WM8915_MICD_ENA);
+
+ /* Slowest detection rate, gives debounce for initial detection */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ WM8915_MICD_RATE_MASK);
+
+ /* Enable interrupts and we're off */
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_STATUS_2_MASK,
+ WM8915_IM_MICD_EINT, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8915_detect);
+
+static void wm8915_micd(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int val, reg;
+
+ val = snd_soc_read(codec, WM8915_MIC_DETECT_3);
+
+ dev_dbg(codec->dev, "Microphone event: %x\n", val);
+
+ if (!(val & WM8915_MICD_VALID)) {
+ dev_warn(codec->dev, "Microphone detection state invalid\n");
+ return;
+ }
+
+ /* No accessory, reset everything and report removal */
+ if (!(val & WM8915_MICD_STS)) {
+ dev_dbg(codec->dev, "Jack removal detected\n");
+ wm8915->jack_mic = false;
+ wm8915->detecting = true;
+ snd_soc_jack_report(wm8915->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ WM8915_MICD_RATE_MASK);
+ return;
+ }
+
+ /* If the measurement is very high we've got a microphone but
+ * do a little debounce to account for mechanical issues.
+ */
+ if (val & 0x400) {
+ dev_dbg(codec->dev, "Microphone detected\n");
+ snd_soc_jack_report(wm8915->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ wm8915->jack_mic = true;
+ wm8915->detecting = false;
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones.
+ */
+ if (wm8915->detecting && (val & 0x3f0)) {
+ reg = snd_soc_read(codec, WM8915_ACCESSORY_DETECT_MODE_2);
+ reg ^= WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
+ WM8915_MICD_BIAS_SRC;
+ snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
+ WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
+ WM8915_MICD_BIAS_SRC, reg);
+
+ if (wm8915->polarity_cb)
+ wm8915->polarity_cb(codec,
+ (reg & WM8915_MICD_SRC) != 0);
+
+ dev_dbg(codec->dev, "Set microphone polarity to %d\n",
+ (reg & WM8915_MICD_SRC) != 0);
+
+ return;
+ }
+
+ /* Don't distinguish between buttons, just report any low
+ * impedence as BTN_0.
+ */
+ if (val & 0x3fc) {
+ if (wm8915->jack_mic) {
+ dev_dbg(codec->dev, "Mic button detected\n");
+ snd_soc_jack_report(wm8915->jack,
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ } else {
+ dev_dbg(codec->dev, "Headphone detected\n");
+ snd_soc_jack_report(wm8915->jack,
+ SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0);
+ wm8915->detecting = false;
+ }
+ }
+
+ /* Increase poll rate to give better responsiveness for buttons */
+ if (!wm8915->detecting)
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ 5 << WM8915_MICD_RATE_SHIFT);
+}
+
+static irqreturn_t wm8915_irq(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int irq_val;
+
+ irq_val = snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2);
+ if (irq_val < 0) {
+ dev_err(codec->dev, "Failed to read IRQ status: %d\n",
+ irq_val);
+ return IRQ_NONE;
+ }
+ irq_val &= ~snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2_MASK);
+
+ if (irq_val & (WM8915_DCS_DONE_01_EINT | WM8915_DCS_DONE_23_EINT)) {
+ dev_dbg(codec->dev, "DC servo IRQ\n");
+ complete(&wm8915->dcs_done);
+ }
+
+ if (irq_val & WM8915_FIFOS_ERR_EINT)
+ dev_err(codec->dev, "Digital core FIFO error\n");
+
+ if (irq_val & WM8915_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8915->fll_lock);
+ }
+
+ if (irq_val & WM8915_MICD_EINT)
+ wm8915_micd(codec);
+
+ if (irq_val) {
+ snd_soc_write(codec, WM8915_INTERRUPT_STATUS_2, irq_val);
+
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("DSP1 EQ Mode",
+ wm8915->retune_mobile_enum,
+ wm8915_get_retune_mobile_enum,
+ wm8915_put_retune_mobile_enum),
+ SOC_ENUM_EXT("DSP2 EQ Mode",
+ wm8915->retune_mobile_enum,
+ wm8915_get_retune_mobile_enum,
+ wm8915_put_retune_mobile_enum),
+ };
+ int ret, i, j;
+ const char **t;
+
+ /* We need an array of texts for the enum API but the number
+ * of texts is likely to be less than the number of
+ * configurations due to the sample rate dependency of the
+ * configurations. */
+ wm8915->num_retune_mobile_texts = 0;
+ wm8915->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8915->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8915->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8915->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8915->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8915->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8915->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8915->num_retune_mobile_texts++;
+ wm8915->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8915->num_retune_mobile_texts);
+
+ wm8915->retune_mobile_enum.max = wm8915->num_retune_mobile_texts;
+ wm8915->retune_mobile_enum.texts = wm8915->retune_mobile_texts;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev,
+ "Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static int wm8915_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i, irq_flags;
+
+ wm8915->codec = codec;
+
+ init_completion(&wm8915->dcs_done);
+ init_completion(&wm8915->fll_lock);
+
+ dapm->idle_bias_off = true;
+ dapm->bias_level = SND_SOC_BIAS_OFF;
+
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
+ wm8915->supplies[i].supply = wm8915_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ wm8915->disable_nb[0].notifier_call = wm8915_regulator_event_0;
+ wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1;
+ wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2;
+ wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3;
+ wm8915->disable_nb[4].notifier_call = wm8915_regulator_event_4;
+ wm8915->disable_nb[5].notifier_call = wm8915_regulator_event_5;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) {
+ ret = regulator_register_notifier(wm8915->supplies[i].consumer,
+ &wm8915->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 1);
+ msleep(5);
+ }
+
+ ret = snd_soc_read(codec, WM8915_SOFTWARE_RESET);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read ID register: %d\n", ret);
+ goto err_enable;
+ }
+ if (ret != 0x8915) {
+ dev_err(codec->dev, "Device is not a WM8915, ID %x\n", ret);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = snd_soc_read(codec, WM8915_CHIP_REVISION);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_enable;
+ }
+
+ dev_info(codec->dev, "revision %c\n",
+ (ret & WM8915_CHIP_REV_MASK) + 'A');
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+ } else {
+ ret = wm8915_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+ }
+
+ codec->cache_only = true;
+
+ /* Apply platform data settings */
+ snd_soc_update_bits(codec, WM8915_LINE_INPUT_CONTROL,
+ WM8915_INL_MODE_MASK | WM8915_INR_MODE_MASK,
+ wm8915->pdata.inl_mode << WM8915_INL_MODE_SHIFT |
+ wm8915->pdata.inr_mode);
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->pdata.gpio_default); i++) {
+ if (!wm8915->pdata.gpio_default[i])
+ continue;
+
+ snd_soc_write(codec, WM8915_GPIO_1 + i,
+ wm8915->pdata.gpio_default[i] & 0xffff);
+ }
+
+ if (wm8915->pdata.spkmute_seq)
+ snd_soc_update_bits(codec, WM8915_PDM_SPEAKER_MUTE_SEQUENCE,
+ WM8915_SPK_MUTE_ENDIAN |
+ WM8915_SPK_MUTE_SEQ1_MASK,
+ wm8915->pdata.spkmute_seq);
+
+ snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
+ WM8915_MICD_BIAS_SRC | WM8915_HPOUT1FB_SRC |
+ WM8915_MICD_SRC, wm8915->pdata.micdet_def);
+
+ /* Latch volume update bits */
+ snd_soc_update_bits(codec, WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_IN1_VU, WM8915_IN1_VU);
+ snd_soc_update_bits(codec, WM8915_RIGHT_LINE_INPUT_VOLUME,
+ WM8915_IN1_VU, WM8915_IN1_VU);
+
+ snd_soc_update_bits(codec, WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_DAC1_RIGHT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+ snd_soc_update_bits(codec, WM8915_DAC2_RIGHT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT1_RIGHT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT2_RIGHT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8915_DSP1_TX_LEFT_VOLUME,
+ WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP1_TX_RIGHT_VOLUME,
+ WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_TX_LEFT_VOLUME,
+ WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_TX_RIGHT_VOLUME,
+ WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
+
+ snd_soc_update_bits(codec, WM8915_DSP1_RX_LEFT_VOLUME,
+ WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP1_RX_RIGHT_VOLUME,
+ WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_RX_LEFT_VOLUME,
+ WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_RX_RIGHT_VOLUME,
+ WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
+
+ /* No support currently for the underclocked TDM modes and
+ * pick a default TDM layout with each channel pair working with
+ * slots 0 and 1. */
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF1RX_CHAN0_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF1RX_CHAN1_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_2_CONFIGURATION,
+ WM8915_AIF1RX_CHAN2_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN2_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_3_CONFIGURATION,
+ WM8915_AIF1RX_CHAN3_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_4_CONFIGURATION,
+ WM8915_AIF1RX_CHAN4_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_5_CONFIGURATION,
+ WM8915_AIF1RX_CHAN5_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF2RX_CHAN0_SLOTS_MASK |
+ WM8915_AIF2RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF2RX_CHAN1_SLOTS_MASK |
+ WM8915_AIF2RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF1TX_CHAN0_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF1TX_CHAN1_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_2_CONFIGURATION,
+ WM8915_AIF1TX_CHAN2_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_3_CONFIGURATION,
+ WM8915_AIF1TX_CHAN3_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_4_CONFIGURATION,
+ WM8915_AIF1TX_CHAN4_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_5_CONFIGURATION,
+ WM8915_AIF1TX_CHAN5_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF2TX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF2TX_CHAN0_SLOTS_MASK |
+ WM8915_AIF2TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF2TX_CHAN1_SLOTS_MASK |
+ WM8915_AIF2TX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+
+ if (wm8915->pdata.num_retune_mobile_cfgs)
+ wm8915_retune_mobile_pdata(codec);
+ else
+ snd_soc_add_controls(codec, wm8915_eq_controls,
+ ARRAY_SIZE(wm8915_eq_controls));
+
+ /* If the TX LRCLK pins are not in LRCLK mode configure the
+ * AIFs to source their clocks from the RX LRCLKs.
+ */
+ if ((snd_soc_read(codec, WM8915_GPIO_1)))
+ snd_soc_update_bits(codec, WM8915_AIF1_TX_LRCLK_2,
+ WM8915_AIF1TX_LRCLK_MODE,
+ WM8915_AIF1TX_LRCLK_MODE);
+
+ if ((snd_soc_read(codec, WM8915_GPIO_2)))
+ snd_soc_update_bits(codec, WM8915_AIF2_TX_LRCLK_2,
+ WM8915_AIF2TX_LRCLK_MODE,
+ WM8915_AIF2TX_LRCLK_MODE);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+
+ wm8915_init_gpio(codec);
+
+ if (i2c->irq) {
+ if (wm8915->pdata.irq_flags)
+ irq_flags = wm8915->pdata.irq_flags;
+ else
+ irq_flags = IRQF_TRIGGER_LOW;
+
+ irq_flags |= IRQF_ONESHOT;
+
+ ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
+ irq_flags, "wm8915", codec);
+ if (ret == 0) {
+ /* Unmask the interrupt */
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
+ WM8915_IM_IRQ, 0);
+
+ /* Enable error reporting and DC servo status */
+ snd_soc_update_bits(codec,
+ WM8915_INTERRUPT_STATUS_2_MASK,
+ WM8915_IM_DCS_DONE_23_EINT |
+ WM8915_IM_DCS_DONE_01_EINT |
+ WM8915_IM_FLL_LOCK_EINT |
+ WM8915_IM_FIFOS_ERR_EINT,
+ 0);
+ } else {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n",
+ ret);
+ }
+ }
+
+ return 0;
+
+err_enable:
+ if (wm8915->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+err:
+ return ret;
+}
+
+static int wm8915_remove(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ int i;
+
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
+ WM8915_IM_IRQ, WM8915_IM_IRQ);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, codec);
+
+ wm8915_free_gpio(codec);
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
+ regulator_unregister_notifier(wm8915->supplies[i].consumer,
+ &wm8915->disable_nb[i]);
+ regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8915 = {
+ .probe = wm8915_probe,
+ .remove = wm8915_remove,
+ .set_bias_level = wm8915_set_bias_level,
+ .seq_notifier = wm8915_seq_notifier,
+ .reg_cache_size = WM8915_MAX_REGISTER + 1,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = wm8915_reg,
+ .volatile_register = wm8915_volatile_register,
+ .readable_register = wm8915_readable_register,
+ .compress_type = SND_SOC_RBTREE_COMPRESSION,
+ .controls = wm8915_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8915_snd_controls),
+ .dapm_widgets = wm8915_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8915_dapm_widgets),
+ .dapm_routes = wm8915_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8915_dapm_routes),
+ .set_pll = wm8915_set_fll,
+};
+
+#define WM8915_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define WM8915_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8915_dai_ops = {
+ .set_fmt = wm8915_set_fmt,
+ .hw_params = wm8915_hw_params,
+ .set_sysclk = wm8915_set_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8915_dai[] = {
+ {
+ .name = "wm8915-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .ops = &wm8915_dai_ops,
+ },
+ {
+ .name = "wm8915-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .ops = &wm8915_dai_ops,
+ },
+};
+
+static __devinit int wm8915_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8915_priv *wm8915;
+ int ret;
+
+ wm8915 = kzalloc(sizeof(struct wm8915_priv), GFP_KERNEL);
+ if (wm8915 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8915);
+
+ if (dev_get_platdata(&i2c->dev))
+ memcpy(&wm8915->pdata, dev_get_platdata(&i2c->dev),
+ sizeof(wm8915->pdata));
+
+ if (wm8915->pdata.ldo_ena > 0) {
+ ret = gpio_request_one(wm8915->pdata.ldo_ena,
+ GPIOF_OUT_INIT_LOW, "WM8915 ENA");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
+ wm8915->pdata.ldo_ena, ret);
+ goto err;
+ }
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8915, wm8915_dai,
+ ARRAY_SIZE(wm8915_dai));
+ if (ret < 0)
+ goto err_gpio;
+
+ return ret;
+
+err_gpio:
+ if (wm8915->pdata.ldo_ena > 0)
+ gpio_free(wm8915->pdata.ldo_ena);
+err:
+ kfree(wm8915);
+
+ return ret;
+}
+
+static __devexit int wm8915_i2c_remove(struct i2c_client *client)
+{
+ struct wm8915_priv *wm8915 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ if (wm8915->pdata.ldo_ena > 0)
+ gpio_free(wm8915->pdata.ldo_ena);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id wm8915_i2c_id[] = {
+ { "wm8915", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8915_i2c_id);
+
+static struct i2c_driver wm8915_i2c_driver = {
+ .driver = {
+ .name = "wm8915",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8915_i2c_probe,
+ .remove = __devexit_p(wm8915_i2c_remove),
+ .id_table = wm8915_i2c_id,
+};
+
+static int __init wm8915_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm8915_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8915 I2C driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(wm8915_modinit);
+
+static void __exit wm8915_exit(void)
+{
+ i2c_del_driver(&wm8915_i2c_driver);
+}
+module_exit(wm8915_exit);
+
+MODULE_DESCRIPTION("ASoC WM8915 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8915.h b/sound/soc/codecs/wm8915.h
new file mode 100644
index 000000000000..200ffd7bf953
--- /dev/null
+++ b/sound/soc/codecs/wm8915.h
@@ -0,0 +1,3717 @@
+/*
+ * wm8915.h - WM8915 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _WM8915_H
+#define _WM8915_H
+
+#define WM8915_SYSCLK_MCLK1 1
+#define WM8915_SYSCLK_MCLK2 2
+#define WM8915_SYSCLK_FLL 3
+
+#define WM8915_FLL_MCLK1 1
+#define WM8915_FLL_MCLK2 2
+#define WM8915_FLL_DACLRCLK1 3
+#define WM8915_FLL_BCLK1 4
+
+typedef void (*wm8915_polarity_fn)(struct snd_soc_codec *codec, int polarity);
+
+int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8915_polarity_fn polarity_cb);
+
+/*
+ * Register values.
+ */
+#define WM8915_SOFTWARE_RESET 0x00
+#define WM8915_POWER_MANAGEMENT_1 0x01
+#define WM8915_POWER_MANAGEMENT_2 0x02
+#define WM8915_POWER_MANAGEMENT_3 0x03
+#define WM8915_POWER_MANAGEMENT_4 0x04
+#define WM8915_POWER_MANAGEMENT_5 0x05
+#define WM8915_POWER_MANAGEMENT_6 0x06
+#define WM8915_POWER_MANAGEMENT_7 0x07
+#define WM8915_POWER_MANAGEMENT_8 0x08
+#define WM8915_LEFT_LINE_INPUT_VOLUME 0x10
+#define WM8915_RIGHT_LINE_INPUT_VOLUME 0x11
+#define WM8915_LINE_INPUT_CONTROL 0x12
+#define WM8915_DAC1_HPOUT1_VOLUME 0x15
+#define WM8915_DAC2_HPOUT2_VOLUME 0x16
+#define WM8915_DAC1_LEFT_VOLUME 0x18
+#define WM8915_DAC1_RIGHT_VOLUME 0x19
+#define WM8915_DAC2_LEFT_VOLUME 0x1A
+#define WM8915_DAC2_RIGHT_VOLUME 0x1B
+#define WM8915_OUTPUT1_LEFT_VOLUME 0x1C
+#define WM8915_OUTPUT1_RIGHT_VOLUME 0x1D
+#define WM8915_OUTPUT2_LEFT_VOLUME 0x1E
+#define WM8915_OUTPUT2_RIGHT_VOLUME 0x1F
+#define WM8915_MICBIAS_1 0x20
+#define WM8915_MICBIAS_2 0x21
+#define WM8915_LDO_1 0x28
+#define WM8915_LDO_2 0x29
+#define WM8915_ACCESSORY_DETECT_MODE_1 0x30
+#define WM8915_ACCESSORY_DETECT_MODE_2 0x31
+#define WM8915_HEADPHONE_DETECT_1 0x34
+#define WM8915_HEADPHONE_DETECT_2 0x35
+#define WM8915_MIC_DETECT_1 0x38
+#define WM8915_MIC_DETECT_2 0x39
+#define WM8915_MIC_DETECT_3 0x3A
+#define WM8915_CHARGE_PUMP_1 0x40
+#define WM8915_CHARGE_PUMP_2 0x41
+#define WM8915_DC_SERVO_1 0x50
+#define WM8915_DC_SERVO_2 0x51
+#define WM8915_DC_SERVO_3 0x52
+#define WM8915_DC_SERVO_5 0x54
+#define WM8915_DC_SERVO_6 0x55
+#define WM8915_DC_SERVO_7 0x56
+#define WM8915_DC_SERVO_READBACK_0 0x57
+#define WM8915_ANALOGUE_HP_1 0x60
+#define WM8915_ANALOGUE_HP_2 0x61
+#define WM8915_CHIP_REVISION 0x100
+#define WM8915_CONTROL_INTERFACE_1 0x101
+#define WM8915_WRITE_SEQUENCER_CTRL_1 0x110
+#define WM8915_WRITE_SEQUENCER_CTRL_2 0x111
+#define WM8915_AIF_CLOCKING_1 0x200
+#define WM8915_AIF_CLOCKING_2 0x201
+#define WM8915_CLOCKING_1 0x208
+#define WM8915_CLOCKING_2 0x209
+#define WM8915_AIF_RATE 0x210
+#define WM8915_FLL_CONTROL_1 0x220
+#define WM8915_FLL_CONTROL_2 0x221
+#define WM8915_FLL_CONTROL_3 0x222
+#define WM8915_FLL_CONTROL_4 0x223
+#define WM8915_FLL_CONTROL_5 0x224
+#define WM8915_FLL_CONTROL_6 0x225
+#define WM8915_FLL_EFS_1 0x226
+#define WM8915_FLL_EFS_2 0x227
+#define WM8915_AIF1_CONTROL 0x300
+#define WM8915_AIF1_BCLK 0x301
+#define WM8915_AIF1_TX_LRCLK_1 0x302
+#define WM8915_AIF1_TX_LRCLK_2 0x303
+#define WM8915_AIF1_RX_LRCLK_1 0x304
+#define WM8915_AIF1_RX_LRCLK_2 0x305
+#define WM8915_AIF1TX_DATA_CONFIGURATION_1 0x306
+#define WM8915_AIF1TX_DATA_CONFIGURATION_2 0x307
+#define WM8915_AIF1RX_DATA_CONFIGURATION 0x308
+#define WM8915_AIF1TX_CHANNEL_0_CONFIGURATION 0x309
+#define WM8915_AIF1TX_CHANNEL_1_CONFIGURATION 0x30A
+#define WM8915_AIF1TX_CHANNEL_2_CONFIGURATION 0x30B
+#define WM8915_AIF1TX_CHANNEL_3_CONFIGURATION 0x30C
+#define WM8915_AIF1TX_CHANNEL_4_CONFIGURATION 0x30D
+#define WM8915_AIF1TX_CHANNEL_5_CONFIGURATION 0x30E
+#define WM8915_AIF1RX_CHANNEL_0_CONFIGURATION 0x30F
+#define WM8915_AIF1RX_CHANNEL_1_CONFIGURATION 0x310
+#define WM8915_AIF1RX_CHANNEL_2_CONFIGURATION 0x311
+#define WM8915_AIF1RX_CHANNEL_3_CONFIGURATION 0x312
+#define WM8915_AIF1RX_CHANNEL_4_CONFIGURATION 0x313
+#define WM8915_AIF1RX_CHANNEL_5_CONFIGURATION 0x314
+#define WM8915_AIF1RX_MONO_CONFIGURATION 0x315
+#define WM8915_AIF1TX_TEST 0x31A
+#define WM8915_AIF2_CONTROL 0x320
+#define WM8915_AIF2_BCLK 0x321
+#define WM8915_AIF2_TX_LRCLK_1 0x322
+#define WM8915_AIF2_TX_LRCLK_2 0x323
+#define WM8915_AIF2_RX_LRCLK_1 0x324
+#define WM8915_AIF2_RX_LRCLK_2 0x325
+#define WM8915_AIF2TX_DATA_CONFIGURATION_1 0x326
+#define WM8915_AIF2TX_DATA_CONFIGURATION_2 0x327
+#define WM8915_AIF2RX_DATA_CONFIGURATION 0x328
+#define WM8915_AIF2TX_CHANNEL_0_CONFIGURATION 0x329
+#define WM8915_AIF2TX_CHANNEL_1_CONFIGURATION 0x32A
+#define WM8915_AIF2RX_CHANNEL_0_CONFIGURATION 0x32B
+#define WM8915_AIF2RX_CHANNEL_1_CONFIGURATION 0x32C
+#define WM8915_AIF2RX_MONO_CONFIGURATION 0x32D
+#define WM8915_AIF2TX_TEST 0x32F
+#define WM8915_DSP1_TX_LEFT_VOLUME 0x400
+#define WM8915_DSP1_TX_RIGHT_VOLUME 0x401
+#define WM8915_DSP1_RX_LEFT_VOLUME 0x402
+#define WM8915_DSP1_RX_RIGHT_VOLUME 0x403
+#define WM8915_DSP1_TX_FILTERS 0x410
+#define WM8915_DSP1_RX_FILTERS_1 0x420
+#define WM8915_DSP1_RX_FILTERS_2 0x421
+#define WM8915_DSP1_DRC_1 0x440
+#define WM8915_DSP1_DRC_2 0x441
+#define WM8915_DSP1_DRC_3 0x442
+#define WM8915_DSP1_DRC_4 0x443
+#define WM8915_DSP1_DRC_5 0x444
+#define WM8915_DSP1_RX_EQ_GAINS_1 0x480
+#define WM8915_DSP1_RX_EQ_GAINS_2 0x481
+#define WM8915_DSP1_RX_EQ_BAND_1_A 0x482
+#define WM8915_DSP1_RX_EQ_BAND_1_B 0x483
+#define WM8915_DSP1_RX_EQ_BAND_1_PG 0x484
+#define WM8915_DSP1_RX_EQ_BAND_2_A 0x485
+#define WM8915_DSP1_RX_EQ_BAND_2_B 0x486
+#define WM8915_DSP1_RX_EQ_BAND_2_C 0x487
+#define WM8915_DSP1_RX_EQ_BAND_2_PG 0x488
+#define WM8915_DSP1_RX_EQ_BAND_3_A 0x489
+#define WM8915_DSP1_RX_EQ_BAND_3_B 0x48A
+#define WM8915_DSP1_RX_EQ_BAND_3_C 0x48B
+#define WM8915_DSP1_RX_EQ_BAND_3_PG 0x48C
+#define WM8915_DSP1_RX_EQ_BAND_4_A 0x48D
+#define WM8915_DSP1_RX_EQ_BAND_4_B 0x48E
+#define WM8915_DSP1_RX_EQ_BAND_4_C 0x48F
+#define WM8915_DSP1_RX_EQ_BAND_4_PG 0x490
+#define WM8915_DSP1_RX_EQ_BAND_5_A 0x491
+#define WM8915_DSP1_RX_EQ_BAND_5_B 0x492
+#define WM8915_DSP1_RX_EQ_BAND_5_PG 0x493
+#define WM8915_DSP2_TX_LEFT_VOLUME 0x500
+#define WM8915_DSP2_TX_RIGHT_VOLUME 0x501
+#define WM8915_DSP2_RX_LEFT_VOLUME 0x502
+#define WM8915_DSP2_RX_RIGHT_VOLUME 0x503
+#define WM8915_DSP2_TX_FILTERS 0x510
+#define WM8915_DSP2_RX_FILTERS_1 0x520
+#define WM8915_DSP2_RX_FILTERS_2 0x521
+#define WM8915_DSP2_DRC_1 0x540
+#define WM8915_DSP2_DRC_2 0x541
+#define WM8915_DSP2_DRC_3 0x542
+#define WM8915_DSP2_DRC_4 0x543
+#define WM8915_DSP2_DRC_5 0x544
+#define WM8915_DSP2_RX_EQ_GAINS_1 0x580
+#define WM8915_DSP2_RX_EQ_GAINS_2 0x581
+#define WM8915_DSP2_RX_EQ_BAND_1_A 0x582
+#define WM8915_DSP2_RX_EQ_BAND_1_B 0x583
+#define WM8915_DSP2_RX_EQ_BAND_1_PG 0x584
+#define WM8915_DSP2_RX_EQ_BAND_2_A 0x585
+#define WM8915_DSP2_RX_EQ_BAND_2_B 0x586
+#define WM8915_DSP2_RX_EQ_BAND_2_C 0x587
+#define WM8915_DSP2_RX_EQ_BAND_2_PG 0x588
+#define WM8915_DSP2_RX_EQ_BAND_3_A 0x589
+#define WM8915_DSP2_RX_EQ_BAND_3_B 0x58A
+#define WM8915_DSP2_RX_EQ_BAND_3_C 0x58B
+#define WM8915_DSP2_RX_EQ_BAND_3_PG 0x58C
+#define WM8915_DSP2_RX_EQ_BAND_4_A 0x58D
+#define WM8915_DSP2_RX_EQ_BAND_4_B 0x58E
+#define WM8915_DSP2_RX_EQ_BAND_4_C 0x58F
+#define WM8915_DSP2_RX_EQ_BAND_4_PG 0x590
+#define WM8915_DSP2_RX_EQ_BAND_5_A 0x591
+#define WM8915_DSP2_RX_EQ_BAND_5_B 0x592
+#define WM8915_DSP2_RX_EQ_BAND_5_PG 0x593
+#define WM8915_DAC1_MIXER_VOLUMES 0x600
+#define WM8915_DAC1_LEFT_MIXER_ROUTING 0x601
+#define WM8915_DAC1_RIGHT_MIXER_ROUTING 0x602
+#define WM8915_DAC2_MIXER_VOLUMES 0x603
+#define WM8915_DAC2_LEFT_MIXER_ROUTING 0x604
+#define WM8915_DAC2_RIGHT_MIXER_ROUTING 0x605
+#define WM8915_DSP1_TX_LEFT_MIXER_ROUTING 0x606
+#define WM8915_DSP1_TX_RIGHT_MIXER_ROUTING 0x607
+#define WM8915_DSP2_TX_LEFT_MIXER_ROUTING 0x608
+#define WM8915_DSP2_TX_RIGHT_MIXER_ROUTING 0x609
+#define WM8915_DSP_TX_MIXER_SELECT 0x60A
+#define WM8915_DAC_SOFTMUTE 0x610
+#define WM8915_OVERSAMPLING 0x620
+#define WM8915_SIDETONE 0x621
+#define WM8915_GPIO_1 0x700
+#define WM8915_GPIO_2 0x701
+#define WM8915_GPIO_3 0x702
+#define WM8915_GPIO_4 0x703
+#define WM8915_GPIO_5 0x704
+#define WM8915_PULL_CONTROL_1 0x720
+#define WM8915_PULL_CONTROL_2 0x721
+#define WM8915_INTERRUPT_STATUS_1 0x730
+#define WM8915_INTERRUPT_STATUS_2 0x731
+#define WM8915_INTERRUPT_RAW_STATUS_2 0x732
+#define WM8915_INTERRUPT_STATUS_1_MASK 0x738
+#define WM8915_INTERRUPT_STATUS_2_MASK 0x739
+#define WM8915_INTERRUPT_CONTROL 0x740
+#define WM8915_LEFT_PDM_SPEAKER 0x800
+#define WM8915_RIGHT_PDM_SPEAKER 0x801
+#define WM8915_PDM_SPEAKER_MUTE_SEQUENCE 0x802
+#define WM8915_PDM_SPEAKER_VOLUME 0x803
+#define WM8915_WRITE_SEQUENCER_0 0x3000
+#define WM8915_WRITE_SEQUENCER_1 0x3001
+#define WM8915_WRITE_SEQUENCER_2 0x3002
+#define WM8915_WRITE_SEQUENCER_3 0x3003
+#define WM8915_WRITE_SEQUENCER_4 0x3004
+#define WM8915_WRITE_SEQUENCER_5 0x3005
+#define WM8915_WRITE_SEQUENCER_6 0x3006
+#define WM8915_WRITE_SEQUENCER_7 0x3007
+#define WM8915_WRITE_SEQUENCER_8 0x3008
+#define WM8915_WRITE_SEQUENCER_9 0x3009
+#define WM8915_WRITE_SEQUENCER_10 0x300A
+#define WM8915_WRITE_SEQUENCER_11 0x300B
+#define WM8915_WRITE_SEQUENCER_12 0x300C
+#define WM8915_WRITE_SEQUENCER_13 0x300D
+#define WM8915_WRITE_SEQUENCER_14 0x300E
+#define WM8915_WRITE_SEQUENCER_15 0x300F
+#define WM8915_WRITE_SEQUENCER_16 0x3010
+#define WM8915_WRITE_SEQUENCER_17 0x3011
+#define WM8915_WRITE_SEQUENCER_18 0x3012
+#define WM8915_WRITE_SEQUENCER_19 0x3013
+#define WM8915_WRITE_SEQUENCER_20 0x3014
+#define WM8915_WRITE_SEQUENCER_21 0x3015
+#define WM8915_WRITE_SEQUENCER_22 0x3016
+#define WM8915_WRITE_SEQUENCER_23 0x3017
+#define WM8915_WRITE_SEQUENCER_24 0x3018
+#define WM8915_WRITE_SEQUENCER_25 0x3019
+#define WM8915_WRITE_SEQUENCER_26 0x301A
+#define WM8915_WRITE_SEQUENCER_27 0x301B
+#define WM8915_WRITE_SEQUENCER_28 0x301C
+#define WM8915_WRITE_SEQUENCER_29 0x301D
+#define WM8915_WRITE_SEQUENCER_30 0x301E
+#define WM8915_WRITE_SEQUENCER_31 0x301F
+#define WM8915_WRITE_SEQUENCER_32 0x3020
+#define WM8915_WRITE_SEQUENCER_33 0x3021
+#define WM8915_WRITE_SEQUENCER_34 0x3022
+#define WM8915_WRITE_SEQUENCER_35 0x3023
+#define WM8915_WRITE_SEQUENCER_36 0x3024
+#define WM8915_WRITE_SEQUENCER_37 0x3025
+#define WM8915_WRITE_SEQUENCER_38 0x3026
+#define WM8915_WRITE_SEQUENCER_39 0x3027
+#define WM8915_WRITE_SEQUENCER_40 0x3028
+#define WM8915_WRITE_SEQUENCER_41 0x3029
+#define WM8915_WRITE_SEQUENCER_42 0x302A
+#define WM8915_WRITE_SEQUENCER_43 0x302B
+#define WM8915_WRITE_SEQUENCER_44 0x302C
+#define WM8915_WRITE_SEQUENCER_45 0x302D
+#define WM8915_WRITE_SEQUENCER_46 0x302E
+#define WM8915_WRITE_SEQUENCER_47 0x302F
+#define WM8915_WRITE_SEQUENCER_48 0x3030
+#define WM8915_WRITE_SEQUENCER_49 0x3031
+#define WM8915_WRITE_SEQUENCER_50 0x3032
+#define WM8915_WRITE_SEQUENCER_51 0x3033
+#define WM8915_WRITE_SEQUENCER_52 0x3034
+#define WM8915_WRITE_SEQUENCER_53 0x3035
+#define WM8915_WRITE_SEQUENCER_54 0x3036
+#define WM8915_WRITE_SEQUENCER_55 0x3037
+#define WM8915_WRITE_SEQUENCER_56 0x3038
+#define WM8915_WRITE_SEQUENCER_57 0x3039
+#define WM8915_WRITE_SEQUENCER_58 0x303A
+#define WM8915_WRITE_SEQUENCER_59 0x303B
+#define WM8915_WRITE_SEQUENCER_60 0x303C
+#define WM8915_WRITE_SEQUENCER_61 0x303D
+#define WM8915_WRITE_SEQUENCER_62 0x303E
+#define WM8915_WRITE_SEQUENCER_63 0x303F
+#define WM8915_WRITE_SEQUENCER_64 0x3040
+#define WM8915_WRITE_SEQUENCER_65 0x3041
+#define WM8915_WRITE_SEQUENCER_66 0x3042
+#define WM8915_WRITE_SEQUENCER_67 0x3043
+#define WM8915_WRITE_SEQUENCER_68 0x3044
+#define WM8915_WRITE_SEQUENCER_69 0x3045
+#define WM8915_WRITE_SEQUENCER_70 0x3046
+#define WM8915_WRITE_SEQUENCER_71 0x3047
+#define WM8915_WRITE_SEQUENCER_72 0x3048
+#define WM8915_WRITE_SEQUENCER_73 0x3049
+#define WM8915_WRITE_SEQUENCER_74 0x304A
+#define WM8915_WRITE_SEQUENCER_75 0x304B
+#define WM8915_WRITE_SEQUENCER_76 0x304C
+#define WM8915_WRITE_SEQUENCER_77 0x304D
+#define WM8915_WRITE_SEQUENCER_78 0x304E
+#define WM8915_WRITE_SEQUENCER_79 0x304F
+#define WM8915_WRITE_SEQUENCER_80 0x3050
+#define WM8915_WRITE_SEQUENCER_81 0x3051
+#define WM8915_WRITE_SEQUENCER_82 0x3052
+#define WM8915_WRITE_SEQUENCER_83 0x3053
+#define WM8915_WRITE_SEQUENCER_84 0x3054
+#define WM8915_WRITE_SEQUENCER_85 0x3055
+#define WM8915_WRITE_SEQUENCER_86 0x3056
+#define WM8915_WRITE_SEQUENCER_87 0x3057
+#define WM8915_WRITE_SEQUENCER_88 0x3058
+#define WM8915_WRITE_SEQUENCER_89 0x3059
+#define WM8915_WRITE_SEQUENCER_90 0x305A
+#define WM8915_WRITE_SEQUENCER_91 0x305B
+#define WM8915_WRITE_SEQUENCER_92 0x305C
+#define WM8915_WRITE_SEQUENCER_93 0x305D
+#define WM8915_WRITE_SEQUENCER_94 0x305E
+#define WM8915_WRITE_SEQUENCER_95 0x305F
+#define WM8915_WRITE_SEQUENCER_96 0x3060
+#define WM8915_WRITE_SEQUENCER_97 0x3061
+#define WM8915_WRITE_SEQUENCER_98 0x3062
+#define WM8915_WRITE_SEQUENCER_99 0x3063
+#define WM8915_WRITE_SEQUENCER_100 0x3064
+#define WM8915_WRITE_SEQUENCER_101 0x3065
+#define WM8915_WRITE_SEQUENCER_102 0x3066
+#define WM8915_WRITE_SEQUENCER_103 0x3067
+#define WM8915_WRITE_SEQUENCER_104 0x3068
+#define WM8915_WRITE_SEQUENCER_105 0x3069
+#define WM8915_WRITE_SEQUENCER_106 0x306A
+#define WM8915_WRITE_SEQUENCER_107 0x306B
+#define WM8915_WRITE_SEQUENCER_108 0x306C
+#define WM8915_WRITE_SEQUENCER_109 0x306D
+#define WM8915_WRITE_SEQUENCER_110 0x306E
+#define WM8915_WRITE_SEQUENCER_111 0x306F
+#define WM8915_WRITE_SEQUENCER_112 0x3070
+#define WM8915_WRITE_SEQUENCER_113 0x3071
+#define WM8915_WRITE_SEQUENCER_114 0x3072
+#define WM8915_WRITE_SEQUENCER_115 0x3073
+#define WM8915_WRITE_SEQUENCER_116 0x3074
+#define WM8915_WRITE_SEQUENCER_117 0x3075
+#define WM8915_WRITE_SEQUENCER_118 0x3076
+#define WM8915_WRITE_SEQUENCER_119 0x3077
+#define WM8915_WRITE_SEQUENCER_120 0x3078
+#define WM8915_WRITE_SEQUENCER_121 0x3079
+#define WM8915_WRITE_SEQUENCER_122 0x307A
+#define WM8915_WRITE_SEQUENCER_123 0x307B
+#define WM8915_WRITE_SEQUENCER_124 0x307C
+#define WM8915_WRITE_SEQUENCER_125 0x307D
+#define WM8915_WRITE_SEQUENCER_126 0x307E
+#define WM8915_WRITE_SEQUENCER_127 0x307F
+#define WM8915_WRITE_SEQUENCER_128 0x3080
+#define WM8915_WRITE_SEQUENCER_129 0x3081
+#define WM8915_WRITE_SEQUENCER_130 0x3082
+#define WM8915_WRITE_SEQUENCER_131 0x3083
+#define WM8915_WRITE_SEQUENCER_132 0x3084
+#define WM8915_WRITE_SEQUENCER_133 0x3085
+#define WM8915_WRITE_SEQUENCER_134 0x3086
+#define WM8915_WRITE_SEQUENCER_135 0x3087
+#define WM8915_WRITE_SEQUENCER_136 0x3088
+#define WM8915_WRITE_SEQUENCER_137 0x3089
+#define WM8915_WRITE_SEQUENCER_138 0x308A
+#define WM8915_WRITE_SEQUENCER_139 0x308B
+#define WM8915_WRITE_SEQUENCER_140 0x308C
+#define WM8915_WRITE_SEQUENCER_141 0x308D
+#define WM8915_WRITE_SEQUENCER_142 0x308E
+#define WM8915_WRITE_SEQUENCER_143 0x308F
+#define WM8915_WRITE_SEQUENCER_144 0x3090
+#define WM8915_WRITE_SEQUENCER_145 0x3091
+#define WM8915_WRITE_SEQUENCER_146 0x3092
+#define WM8915_WRITE_SEQUENCER_147 0x3093
+#define WM8915_WRITE_SEQUENCER_148 0x3094
+#define WM8915_WRITE_SEQUENCER_149 0x3095
+#define WM8915_WRITE_SEQUENCER_150 0x3096
+#define WM8915_WRITE_SEQUENCER_151 0x3097
+#define WM8915_WRITE_SEQUENCER_152 0x3098
+#define WM8915_WRITE_SEQUENCER_153 0x3099
+#define WM8915_WRITE_SEQUENCER_154 0x309A
+#define WM8915_WRITE_SEQUENCER_155 0x309B
+#define WM8915_WRITE_SEQUENCER_156 0x309C
+#define WM8915_WRITE_SEQUENCER_157 0x309D
+#define WM8915_WRITE_SEQUENCER_158 0x309E
+#define WM8915_WRITE_SEQUENCER_159 0x309F
+#define WM8915_WRITE_SEQUENCER_160 0x30A0
+#define WM8915_WRITE_SEQUENCER_161 0x30A1
+#define WM8915_WRITE_SEQUENCER_162 0x30A2
+#define WM8915_WRITE_SEQUENCER_163 0x30A3
+#define WM8915_WRITE_SEQUENCER_164 0x30A4
+#define WM8915_WRITE_SEQUENCER_165 0x30A5
+#define WM8915_WRITE_SEQUENCER_166 0x30A6
+#define WM8915_WRITE_SEQUENCER_167 0x30A7
+#define WM8915_WRITE_SEQUENCER_168 0x30A8
+#define WM8915_WRITE_SEQUENCER_169 0x30A9
+#define WM8915_WRITE_SEQUENCER_170 0x30AA
+#define WM8915_WRITE_SEQUENCER_171 0x30AB
+#define WM8915_WRITE_SEQUENCER_172 0x30AC
+#define WM8915_WRITE_SEQUENCER_173 0x30AD
+#define WM8915_WRITE_SEQUENCER_174 0x30AE
+#define WM8915_WRITE_SEQUENCER_175 0x30AF
+#define WM8915_WRITE_SEQUENCER_176 0x30B0
+#define WM8915_WRITE_SEQUENCER_177 0x30B1
+#define WM8915_WRITE_SEQUENCER_178 0x30B2
+#define WM8915_WRITE_SEQUENCER_179 0x30B3
+#define WM8915_WRITE_SEQUENCER_180 0x30B4
+#define WM8915_WRITE_SEQUENCER_181 0x30B5
+#define WM8915_WRITE_SEQUENCER_182 0x30B6
+#define WM8915_WRITE_SEQUENCER_183 0x30B7
+#define WM8915_WRITE_SEQUENCER_184 0x30B8
+#define WM8915_WRITE_SEQUENCER_185 0x30B9
+#define WM8915_WRITE_SEQUENCER_186 0x30BA
+#define WM8915_WRITE_SEQUENCER_187 0x30BB
+#define WM8915_WRITE_SEQUENCER_188 0x30BC
+#define WM8915_WRITE_SEQUENCER_189 0x30BD
+#define WM8915_WRITE_SEQUENCER_190 0x30BE
+#define WM8915_WRITE_SEQUENCER_191 0x30BF
+#define WM8915_WRITE_SEQUENCER_192 0x30C0
+#define WM8915_WRITE_SEQUENCER_193 0x30C1
+#define WM8915_WRITE_SEQUENCER_194 0x30C2
+#define WM8915_WRITE_SEQUENCER_195 0x30C3
+#define WM8915_WRITE_SEQUENCER_196 0x30C4
+#define WM8915_WRITE_SEQUENCER_197 0x30C5
+#define WM8915_WRITE_SEQUENCER_198 0x30C6
+#define WM8915_WRITE_SEQUENCER_199 0x30C7
+#define WM8915_WRITE_SEQUENCER_200 0x30C8
+#define WM8915_WRITE_SEQUENCER_201 0x30C9
+#define WM8915_WRITE_SEQUENCER_202 0x30CA
+#define WM8915_WRITE_SEQUENCER_203 0x30CB
+#define WM8915_WRITE_SEQUENCER_204 0x30CC
+#define WM8915_WRITE_SEQUENCER_205 0x30CD
+#define WM8915_WRITE_SEQUENCER_206 0x30CE
+#define WM8915_WRITE_SEQUENCER_207 0x30CF
+#define WM8915_WRITE_SEQUENCER_208 0x30D0
+#define WM8915_WRITE_SEQUENCER_209 0x30D1
+#define WM8915_WRITE_SEQUENCER_210 0x30D2
+#define WM8915_WRITE_SEQUENCER_211 0x30D3
+#define WM8915_WRITE_SEQUENCER_212 0x30D4
+#define WM8915_WRITE_SEQUENCER_213 0x30D5
+#define WM8915_WRITE_SEQUENCER_214 0x30D6
+#define WM8915_WRITE_SEQUENCER_215 0x30D7
+#define WM8915_WRITE_SEQUENCER_216 0x30D8
+#define WM8915_WRITE_SEQUENCER_217 0x30D9
+#define WM8915_WRITE_SEQUENCER_218 0x30DA
+#define WM8915_WRITE_SEQUENCER_219 0x30DB
+#define WM8915_WRITE_SEQUENCER_220 0x30DC
+#define WM8915_WRITE_SEQUENCER_221 0x30DD
+#define WM8915_WRITE_SEQUENCER_222 0x30DE
+#define WM8915_WRITE_SEQUENCER_223 0x30DF
+#define WM8915_WRITE_SEQUENCER_224 0x30E0
+#define WM8915_WRITE_SEQUENCER_225 0x30E1
+#define WM8915_WRITE_SEQUENCER_226 0x30E2
+#define WM8915_WRITE_SEQUENCER_227 0x30E3
+#define WM8915_WRITE_SEQUENCER_228 0x30E4
+#define WM8915_WRITE_SEQUENCER_229 0x30E5
+#define WM8915_WRITE_SEQUENCER_230 0x30E6
+#define WM8915_WRITE_SEQUENCER_231 0x30E7
+#define WM8915_WRITE_SEQUENCER_232 0x30E8
+#define WM8915_WRITE_SEQUENCER_233 0x30E9
+#define WM8915_WRITE_SEQUENCER_234 0x30EA
+#define WM8915_WRITE_SEQUENCER_235 0x30EB
+#define WM8915_WRITE_SEQUENCER_236 0x30EC
+#define WM8915_WRITE_SEQUENCER_237 0x30ED
+#define WM8915_WRITE_SEQUENCER_238 0x30EE
+#define WM8915_WRITE_SEQUENCER_239 0x30EF
+#define WM8915_WRITE_SEQUENCER_240 0x30F0
+#define WM8915_WRITE_SEQUENCER_241 0x30F1
+#define WM8915_WRITE_SEQUENCER_242 0x30F2
+#define WM8915_WRITE_SEQUENCER_243 0x30F3
+#define WM8915_WRITE_SEQUENCER_244 0x30F4
+#define WM8915_WRITE_SEQUENCER_245 0x30F5
+#define WM8915_WRITE_SEQUENCER_246 0x30F6
+#define WM8915_WRITE_SEQUENCER_247 0x30F7
+#define WM8915_WRITE_SEQUENCER_248 0x30F8
+#define WM8915_WRITE_SEQUENCER_249 0x30F9
+#define WM8915_WRITE_SEQUENCER_250 0x30FA
+#define WM8915_WRITE_SEQUENCER_251 0x30FB
+#define WM8915_WRITE_SEQUENCER_252 0x30FC
+#define WM8915_WRITE_SEQUENCER_253 0x30FD
+#define WM8915_WRITE_SEQUENCER_254 0x30FE
+#define WM8915_WRITE_SEQUENCER_255 0x30FF
+#define WM8915_WRITE_SEQUENCER_256 0x3100
+#define WM8915_WRITE_SEQUENCER_257 0x3101
+#define WM8915_WRITE_SEQUENCER_258 0x3102
+#define WM8915_WRITE_SEQUENCER_259 0x3103
+#define WM8915_WRITE_SEQUENCER_260 0x3104
+#define WM8915_WRITE_SEQUENCER_261 0x3105
+#define WM8915_WRITE_SEQUENCER_262 0x3106
+#define WM8915_WRITE_SEQUENCER_263 0x3107
+#define WM8915_WRITE_SEQUENCER_264 0x3108
+#define WM8915_WRITE_SEQUENCER_265 0x3109
+#define WM8915_WRITE_SEQUENCER_266 0x310A
+#define WM8915_WRITE_SEQUENCER_267 0x310B
+#define WM8915_WRITE_SEQUENCER_268 0x310C
+#define WM8915_WRITE_SEQUENCER_269 0x310D
+#define WM8915_WRITE_SEQUENCER_270 0x310E
+#define WM8915_WRITE_SEQUENCER_271 0x310F
+#define WM8915_WRITE_SEQUENCER_272 0x3110
+#define WM8915_WRITE_SEQUENCER_273 0x3111
+#define WM8915_WRITE_SEQUENCER_274 0x3112
+#define WM8915_WRITE_SEQUENCER_275 0x3113
+#define WM8915_WRITE_SEQUENCER_276 0x3114
+#define WM8915_WRITE_SEQUENCER_277 0x3115
+#define WM8915_WRITE_SEQUENCER_278 0x3116
+#define WM8915_WRITE_SEQUENCER_279 0x3117
+#define WM8915_WRITE_SEQUENCER_280 0x3118
+#define WM8915_WRITE_SEQUENCER_281 0x3119
+#define WM8915_WRITE_SEQUENCER_282 0x311A
+#define WM8915_WRITE_SEQUENCER_283 0x311B
+#define WM8915_WRITE_SEQUENCER_284 0x311C
+#define WM8915_WRITE_SEQUENCER_285 0x311D
+#define WM8915_WRITE_SEQUENCER_286 0x311E
+#define WM8915_WRITE_SEQUENCER_287 0x311F
+#define WM8915_WRITE_SEQUENCER_288 0x3120
+#define WM8915_WRITE_SEQUENCER_289 0x3121
+#define WM8915_WRITE_SEQUENCER_290 0x3122
+#define WM8915_WRITE_SEQUENCER_291 0x3123
+#define WM8915_WRITE_SEQUENCER_292 0x3124
+#define WM8915_WRITE_SEQUENCER_293 0x3125
+#define WM8915_WRITE_SEQUENCER_294 0x3126
+#define WM8915_WRITE_SEQUENCER_295 0x3127
+#define WM8915_WRITE_SEQUENCER_296 0x3128
+#define WM8915_WRITE_SEQUENCER_297 0x3129
+#define WM8915_WRITE_SEQUENCER_298 0x312A
+#define WM8915_WRITE_SEQUENCER_299 0x312B
+#define WM8915_WRITE_SEQUENCER_300 0x312C
+#define WM8915_WRITE_SEQUENCER_301 0x312D
+#define WM8915_WRITE_SEQUENCER_302 0x312E
+#define WM8915_WRITE_SEQUENCER_303 0x312F
+#define WM8915_WRITE_SEQUENCER_304 0x3130
+#define WM8915_WRITE_SEQUENCER_305 0x3131
+#define WM8915_WRITE_SEQUENCER_306 0x3132
+#define WM8915_WRITE_SEQUENCER_307 0x3133
+#define WM8915_WRITE_SEQUENCER_308 0x3134
+#define WM8915_WRITE_SEQUENCER_309 0x3135
+#define WM8915_WRITE_SEQUENCER_310 0x3136
+#define WM8915_WRITE_SEQUENCER_311 0x3137
+#define WM8915_WRITE_SEQUENCER_312 0x3138
+#define WM8915_WRITE_SEQUENCER_313 0x3139
+#define WM8915_WRITE_SEQUENCER_314 0x313A
+#define WM8915_WRITE_SEQUENCER_315 0x313B
+#define WM8915_WRITE_SEQUENCER_316 0x313C
+#define WM8915_WRITE_SEQUENCER_317 0x313D
+#define WM8915_WRITE_SEQUENCER_318 0x313E
+#define WM8915_WRITE_SEQUENCER_319 0x313F
+#define WM8915_WRITE_SEQUENCER_320 0x3140
+#define WM8915_WRITE_SEQUENCER_321 0x3141
+#define WM8915_WRITE_SEQUENCER_322 0x3142
+#define WM8915_WRITE_SEQUENCER_323 0x3143
+#define WM8915_WRITE_SEQUENCER_324 0x3144
+#define WM8915_WRITE_SEQUENCER_325 0x3145
+#define WM8915_WRITE_SEQUENCER_326 0x3146
+#define WM8915_WRITE_SEQUENCER_327 0x3147
+#define WM8915_WRITE_SEQUENCER_328 0x3148
+#define WM8915_WRITE_SEQUENCER_329 0x3149
+#define WM8915_WRITE_SEQUENCER_330 0x314A
+#define WM8915_WRITE_SEQUENCER_331 0x314B
+#define WM8915_WRITE_SEQUENCER_332 0x314C
+#define WM8915_WRITE_SEQUENCER_333 0x314D
+#define WM8915_WRITE_SEQUENCER_334 0x314E
+#define WM8915_WRITE_SEQUENCER_335 0x314F
+#define WM8915_WRITE_SEQUENCER_336 0x3150
+#define WM8915_WRITE_SEQUENCER_337 0x3151
+#define WM8915_WRITE_SEQUENCER_338 0x3152
+#define WM8915_WRITE_SEQUENCER_339 0x3153
+#define WM8915_WRITE_SEQUENCER_340 0x3154
+#define WM8915_WRITE_SEQUENCER_341 0x3155
+#define WM8915_WRITE_SEQUENCER_342 0x3156
+#define WM8915_WRITE_SEQUENCER_343 0x3157
+#define WM8915_WRITE_SEQUENCER_344 0x3158
+#define WM8915_WRITE_SEQUENCER_345 0x3159
+#define WM8915_WRITE_SEQUENCER_346 0x315A
+#define WM8915_WRITE_SEQUENCER_347 0x315B
+#define WM8915_WRITE_SEQUENCER_348 0x315C
+#define WM8915_WRITE_SEQUENCER_349 0x315D
+#define WM8915_WRITE_SEQUENCER_350 0x315E
+#define WM8915_WRITE_SEQUENCER_351 0x315F
+#define WM8915_WRITE_SEQUENCER_352 0x3160
+#define WM8915_WRITE_SEQUENCER_353 0x3161
+#define WM8915_WRITE_SEQUENCER_354 0x3162
+#define WM8915_WRITE_SEQUENCER_355 0x3163
+#define WM8915_WRITE_SEQUENCER_356 0x3164
+#define WM8915_WRITE_SEQUENCER_357 0x3165
+#define WM8915_WRITE_SEQUENCER_358 0x3166
+#define WM8915_WRITE_SEQUENCER_359 0x3167
+#define WM8915_WRITE_SEQUENCER_360 0x3168
+#define WM8915_WRITE_SEQUENCER_361 0x3169
+#define WM8915_WRITE_SEQUENCER_362 0x316A
+#define WM8915_WRITE_SEQUENCER_363 0x316B
+#define WM8915_WRITE_SEQUENCER_364 0x316C
+#define WM8915_WRITE_SEQUENCER_365 0x316D
+#define WM8915_WRITE_SEQUENCER_366 0x316E
+#define WM8915_WRITE_SEQUENCER_367 0x316F
+#define WM8915_WRITE_SEQUENCER_368 0x3170
+#define WM8915_WRITE_SEQUENCER_369 0x3171
+#define WM8915_WRITE_SEQUENCER_370 0x3172
+#define WM8915_WRITE_SEQUENCER_371 0x3173
+#define WM8915_WRITE_SEQUENCER_372 0x3174
+#define WM8915_WRITE_SEQUENCER_373 0x3175
+#define WM8915_WRITE_SEQUENCER_374 0x3176
+#define WM8915_WRITE_SEQUENCER_375 0x3177
+#define WM8915_WRITE_SEQUENCER_376 0x3178
+#define WM8915_WRITE_SEQUENCER_377 0x3179
+#define WM8915_WRITE_SEQUENCER_378 0x317A
+#define WM8915_WRITE_SEQUENCER_379 0x317B
+#define WM8915_WRITE_SEQUENCER_380 0x317C
+#define WM8915_WRITE_SEQUENCER_381 0x317D
+#define WM8915_WRITE_SEQUENCER_382 0x317E
+#define WM8915_WRITE_SEQUENCER_383 0x317F
+#define WM8915_WRITE_SEQUENCER_384 0x3180
+#define WM8915_WRITE_SEQUENCER_385 0x3181
+#define WM8915_WRITE_SEQUENCER_386 0x3182
+#define WM8915_WRITE_SEQUENCER_387 0x3183
+#define WM8915_WRITE_SEQUENCER_388 0x3184
+#define WM8915_WRITE_SEQUENCER_389 0x3185
+#define WM8915_WRITE_SEQUENCER_390 0x3186
+#define WM8915_WRITE_SEQUENCER_391 0x3187
+#define WM8915_WRITE_SEQUENCER_392 0x3188
+#define WM8915_WRITE_SEQUENCER_393 0x3189
+#define WM8915_WRITE_SEQUENCER_394 0x318A
+#define WM8915_WRITE_SEQUENCER_395 0x318B
+#define WM8915_WRITE_SEQUENCER_396 0x318C
+#define WM8915_WRITE_SEQUENCER_397 0x318D
+#define WM8915_WRITE_SEQUENCER_398 0x318E
+#define WM8915_WRITE_SEQUENCER_399 0x318F
+#define WM8915_WRITE_SEQUENCER_400 0x3190
+#define WM8915_WRITE_SEQUENCER_401 0x3191
+#define WM8915_WRITE_SEQUENCER_402 0x3192
+#define WM8915_WRITE_SEQUENCER_403 0x3193
+#define WM8915_WRITE_SEQUENCER_404 0x3194
+#define WM8915_WRITE_SEQUENCER_405 0x3195
+#define WM8915_WRITE_SEQUENCER_406 0x3196
+#define WM8915_WRITE_SEQUENCER_407 0x3197
+#define WM8915_WRITE_SEQUENCER_408 0x3198
+#define WM8915_WRITE_SEQUENCER_409 0x3199
+#define WM8915_WRITE_SEQUENCER_410 0x319A
+#define WM8915_WRITE_SEQUENCER_411 0x319B
+#define WM8915_WRITE_SEQUENCER_412 0x319C
+#define WM8915_WRITE_SEQUENCER_413 0x319D
+#define WM8915_WRITE_SEQUENCER_414 0x319E
+#define WM8915_WRITE_SEQUENCER_415 0x319F
+#define WM8915_WRITE_SEQUENCER_416 0x31A0
+#define WM8915_WRITE_SEQUENCER_417 0x31A1
+#define WM8915_WRITE_SEQUENCER_418 0x31A2
+#define WM8915_WRITE_SEQUENCER_419 0x31A3
+#define WM8915_WRITE_SEQUENCER_420 0x31A4
+#define WM8915_WRITE_SEQUENCER_421 0x31A5
+#define WM8915_WRITE_SEQUENCER_422 0x31A6
+#define WM8915_WRITE_SEQUENCER_423 0x31A7
+#define WM8915_WRITE_SEQUENCER_424 0x31A8
+#define WM8915_WRITE_SEQUENCER_425 0x31A9
+#define WM8915_WRITE_SEQUENCER_426 0x31AA
+#define WM8915_WRITE_SEQUENCER_427 0x31AB
+#define WM8915_WRITE_SEQUENCER_428 0x31AC
+#define WM8915_WRITE_SEQUENCER_429 0x31AD
+#define WM8915_WRITE_SEQUENCER_430 0x31AE
+#define WM8915_WRITE_SEQUENCER_431 0x31AF
+#define WM8915_WRITE_SEQUENCER_432 0x31B0
+#define WM8915_WRITE_SEQUENCER_433 0x31B1
+#define WM8915_WRITE_SEQUENCER_434 0x31B2
+#define WM8915_WRITE_SEQUENCER_435 0x31B3
+#define WM8915_WRITE_SEQUENCER_436 0x31B4
+#define WM8915_WRITE_SEQUENCER_437 0x31B5
+#define WM8915_WRITE_SEQUENCER_438 0x31B6
+#define WM8915_WRITE_SEQUENCER_439 0x31B7
+#define WM8915_WRITE_SEQUENCER_440 0x31B8
+#define WM8915_WRITE_SEQUENCER_441 0x31B9
+#define WM8915_WRITE_SEQUENCER_442 0x31BA
+#define WM8915_WRITE_SEQUENCER_443 0x31BB
+#define WM8915_WRITE_SEQUENCER_444 0x31BC
+#define WM8915_WRITE_SEQUENCER_445 0x31BD
+#define WM8915_WRITE_SEQUENCER_446 0x31BE
+#define WM8915_WRITE_SEQUENCER_447 0x31BF
+#define WM8915_WRITE_SEQUENCER_448 0x31C0
+#define WM8915_WRITE_SEQUENCER_449 0x31C1
+#define WM8915_WRITE_SEQUENCER_450 0x31C2
+#define WM8915_WRITE_SEQUENCER_451 0x31C3
+#define WM8915_WRITE_SEQUENCER_452 0x31C4
+#define WM8915_WRITE_SEQUENCER_453 0x31C5
+#define WM8915_WRITE_SEQUENCER_454 0x31C6
+#define WM8915_WRITE_SEQUENCER_455 0x31C7
+#define WM8915_WRITE_SEQUENCER_456 0x31C8
+#define WM8915_WRITE_SEQUENCER_457 0x31C9
+#define WM8915_WRITE_SEQUENCER_458 0x31CA
+#define WM8915_WRITE_SEQUENCER_459 0x31CB
+#define WM8915_WRITE_SEQUENCER_460 0x31CC
+#define WM8915_WRITE_SEQUENCER_461 0x31CD
+#define WM8915_WRITE_SEQUENCER_462 0x31CE
+#define WM8915_WRITE_SEQUENCER_463 0x31CF
+#define WM8915_WRITE_SEQUENCER_464 0x31D0
+#define WM8915_WRITE_SEQUENCER_465 0x31D1
+#define WM8915_WRITE_SEQUENCER_466 0x31D2
+#define WM8915_WRITE_SEQUENCER_467 0x31D3
+#define WM8915_WRITE_SEQUENCER_468 0x31D4
+#define WM8915_WRITE_SEQUENCER_469 0x31D5
+#define WM8915_WRITE_SEQUENCER_470 0x31D6
+#define WM8915_WRITE_SEQUENCER_471 0x31D7
+#define WM8915_WRITE_SEQUENCER_472 0x31D8
+#define WM8915_WRITE_SEQUENCER_473 0x31D9
+#define WM8915_WRITE_SEQUENCER_474 0x31DA
+#define WM8915_WRITE_SEQUENCER_475 0x31DB
+#define WM8915_WRITE_SEQUENCER_476 0x31DC
+#define WM8915_WRITE_SEQUENCER_477 0x31DD
+#define WM8915_WRITE_SEQUENCER_478 0x31DE
+#define WM8915_WRITE_SEQUENCER_479 0x31DF
+#define WM8915_WRITE_SEQUENCER_480 0x31E0
+#define WM8915_WRITE_SEQUENCER_481 0x31E1
+#define WM8915_WRITE_SEQUENCER_482 0x31E2
+#define WM8915_WRITE_SEQUENCER_483 0x31E3
+#define WM8915_WRITE_SEQUENCER_484 0x31E4
+#define WM8915_WRITE_SEQUENCER_485 0x31E5
+#define WM8915_WRITE_SEQUENCER_486 0x31E6
+#define WM8915_WRITE_SEQUENCER_487 0x31E7
+#define WM8915_WRITE_SEQUENCER_488 0x31E8
+#define WM8915_WRITE_SEQUENCER_489 0x31E9
+#define WM8915_WRITE_SEQUENCER_490 0x31EA
+#define WM8915_WRITE_SEQUENCER_491 0x31EB
+#define WM8915_WRITE_SEQUENCER_492 0x31EC
+#define WM8915_WRITE_SEQUENCER_493 0x31ED
+#define WM8915_WRITE_SEQUENCER_494 0x31EE
+#define WM8915_WRITE_SEQUENCER_495 0x31EF
+#define WM8915_WRITE_SEQUENCER_496 0x31F0
+#define WM8915_WRITE_SEQUENCER_497 0x31F1
+#define WM8915_WRITE_SEQUENCER_498 0x31F2
+#define WM8915_WRITE_SEQUENCER_499 0x31F3
+#define WM8915_WRITE_SEQUENCER_500 0x31F4
+#define WM8915_WRITE_SEQUENCER_501 0x31F5
+#define WM8915_WRITE_SEQUENCER_502 0x31F6
+#define WM8915_WRITE_SEQUENCER_503 0x31F7
+#define WM8915_WRITE_SEQUENCER_504 0x31F8
+#define WM8915_WRITE_SEQUENCER_505 0x31F9
+#define WM8915_WRITE_SEQUENCER_506 0x31FA
+#define WM8915_WRITE_SEQUENCER_507 0x31FB
+#define WM8915_WRITE_SEQUENCER_508 0x31FC
+#define WM8915_WRITE_SEQUENCER_509 0x31FD
+#define WM8915_WRITE_SEQUENCER_510 0x31FE
+#define WM8915_WRITE_SEQUENCER_511 0x31FF
+
+#define WM8915_REGISTER_COUNT 706
+#define WM8915_MAX_REGISTER 0x31FF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8915_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8915_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8915_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8915_MICB2_ENA 0x0200 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_SHIFT 9 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8915_MICB1_ENA 0x0100 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_SHIFT 8 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8915_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */
+#define WM8915_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8915_BG_ENA 0x0001 /* BG_ENA */
+#define WM8915_BG_ENA_MASK 0x0001 /* BG_ENA */
+#define WM8915_BG_ENA_SHIFT 0 /* BG_ENA */
+#define WM8915_BG_ENA_WIDTH 1 /* BG_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8915_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8915_INL_ENA 0x0020 /* INL_ENA */
+#define WM8915_INL_ENA_MASK 0x0020 /* INL_ENA */
+#define WM8915_INL_ENA_SHIFT 5 /* INL_ENA */
+#define WM8915_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8915_INR_ENA 0x0010 /* INR_ENA */
+#define WM8915_INR_ENA_MASK 0x0010 /* INR_ENA */
+#define WM8915_INR_ENA_SHIFT 4 /* INR_ENA */
+#define WM8915_INR_ENA_WIDTH 1 /* INR_ENA */
+#define WM8915_LDO2_ENA 0x0002 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8915_DSP2RXL_ENA 0x0800 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_MASK 0x0800 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_SHIFT 11 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_WIDTH 1 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXR_ENA 0x0400 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_MASK 0x0400 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_SHIFT 10 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_WIDTH 1 /* DSP2RXR_ENA */
+#define WM8915_DSP1RXL_ENA 0x0200 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_MASK 0x0200 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_SHIFT 9 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_WIDTH 1 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXR_ENA 0x0100 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_MASK 0x0100 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_SHIFT 8 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_WIDTH 1 /* DSP1RXR_ENA */
+#define WM8915_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */
+#define WM8915_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */
+#define WM8915_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */
+#define WM8915_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */
+#define WM8915_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8915_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (4)
+ */
+#define WM8915_AIF2RX_CHAN1_ENA 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_MASK 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_SHIFT 9 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_WIDTH 1 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_MASK 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_SHIFT 8 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_WIDTH 1 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_MASK 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_SHIFT 5 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_WIDTH 1 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_MASK 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_SHIFT 4 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_WIDTH 1 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_MASK 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_SHIFT 3 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_WIDTH 1 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_MASK 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_SHIFT 2 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_WIDTH 1 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_MASK 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_SHIFT 1 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_WIDTH 1 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_MASK 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_SHIFT 0 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_WIDTH 1 /* AIF1RX_CHAN0_ENA */
+
+/*
+ * R5 (0x05) - Power Management (5)
+ */
+#define WM8915_DSP2TXL_ENA 0x0800 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_MASK 0x0800 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_SHIFT 11 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_WIDTH 1 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXR_ENA 0x0400 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_MASK 0x0400 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_SHIFT 10 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_WIDTH 1 /* DSP2TXR_ENA */
+#define WM8915_DSP1TXL_ENA 0x0200 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_MASK 0x0200 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_SHIFT 9 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_WIDTH 1 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXR_ENA 0x0100 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_MASK 0x0100 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_SHIFT 8 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_WIDTH 1 /* DSP1TXR_ENA */
+#define WM8915_DAC2L_ENA 0x0008 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */
+#define WM8915_DAC2R_ENA 0x0004 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */
+#define WM8915_DAC1L_ENA 0x0002 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */
+#define WM8915_DAC1R_ENA 0x0001 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */
+
+/*
+ * R6 (0x06) - Power Management (6)
+ */
+#define WM8915_AIF2TX_CHAN1_ENA 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_MASK 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_SHIFT 9 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_WIDTH 1 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_MASK 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_SHIFT 8 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_WIDTH 1 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_MASK 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_SHIFT 5 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_WIDTH 1 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_MASK 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_SHIFT 4 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_WIDTH 1 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_MASK 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_SHIFT 3 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_WIDTH 1 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_MASK 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_SHIFT 2 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_WIDTH 1 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_MASK 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_SHIFT 1 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_WIDTH 1 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_MASK 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_SHIFT 0 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_WIDTH 1 /* AIF1TX_CHAN0_ENA */
+
+/*
+ * R7 (0x07) - Power Management (7)
+ */
+#define WM8915_DMIC2_FN 0x0200 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_MASK 0x0200 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_SHIFT 9 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_WIDTH 1 /* DMIC2_FN */
+#define WM8915_DMIC1_FN 0x0100 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_MASK 0x0100 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_SHIFT 8 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_WIDTH 1 /* DMIC1_FN */
+#define WM8915_ADC_DMIC_DSP2R_ENA 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_MASK 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_SHIFT 7 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_WIDTH 1 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_MASK 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_SHIFT 6 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_WIDTH 1 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_SRC2_MASK 0x0030 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_SRC2_SHIFT 4 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_SRC2_WIDTH 2 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_DSP1R_ENA 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_MASK 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_SHIFT 3 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_WIDTH 1 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_MASK 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_SHIFT 2 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_WIDTH 1 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_SRC1_MASK 0x0003 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8915_ADC_DMIC_SRC1_SHIFT 0 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8915_ADC_DMIC_SRC1_WIDTH 2 /* ADC_DMIC_SRC1 - [1:0] */
+
+/*
+ * R8 (0x08) - Power Management (8)
+ */
+#define WM8915_AIF2TX_SRC_MASK 0x00C0 /* AIF2TX_SRC - [7:6] */
+#define WM8915_AIF2TX_SRC_SHIFT 6 /* AIF2TX_SRC - [7:6] */
+#define WM8915_AIF2TX_SRC_WIDTH 2 /* AIF2TX_SRC - [7:6] */
+#define WM8915_DSP2RX_SRC 0x0010 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_MASK 0x0010 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_SHIFT 4 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_WIDTH 1 /* DSP2RX_SRC */
+#define WM8915_DSP1RX_SRC 0x0001 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_MASK 0x0001 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_SHIFT 0 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_WIDTH 1 /* DSP1RX_SRC */
+
+/*
+ * R16 (0x10) - Left Line Input Volume
+ */
+#define WM8915_IN1_VU 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8915_IN1L_ZC 0x0020 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_SHIFT 5 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8915_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8915_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8915_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R17 (0x11) - Right Line Input Volume
+ */
+#define WM8915_IN1_VU 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8915_IN1R_ZC 0x0020 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_SHIFT 5 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8915_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8915_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8915_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R18 (0x12) - Line Input Control
+ */
+#define WM8915_INL_MODE_MASK 0x000C /* INL_MODE - [3:2] */
+#define WM8915_INL_MODE_SHIFT 2 /* INL_MODE - [3:2] */
+#define WM8915_INL_MODE_WIDTH 2 /* INL_MODE - [3:2] */
+#define WM8915_INR_MODE_MASK 0x0003 /* INR_MODE - [1:0] */
+#define WM8915_INR_MODE_SHIFT 0 /* INR_MODE - [1:0] */
+#define WM8915_INR_MODE_WIDTH 2 /* INR_MODE - [1:0] */
+
+/*
+ * R21 (0x15) - DAC1 HPOUT1 Volume
+ */
+#define WM8915_DAC1R_HPOUT1R_VOL_MASK 0x00F0 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1R_HPOUT1R_VOL_SHIFT 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1R_HPOUT1R_VOL_WIDTH 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1L_HPOUT1L_VOL_MASK 0x000F /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8915_DAC1L_HPOUT1L_VOL_SHIFT 0 /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8915_DAC1L_HPOUT1L_VOL_WIDTH 4 /* DAC1L_HPOUT1L_VOL - [3:0] */
+
+/*
+ * R22 (0x16) - DAC2 HPOUT2 Volume
+ */
+#define WM8915_DAC2R_HPOUT2R_VOL_MASK 0x00F0 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2R_HPOUT2R_VOL_SHIFT 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2R_HPOUT2R_VOL_WIDTH 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2L_HPOUT2L_VOL_MASK 0x000F /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8915_DAC2L_HPOUT2L_VOL_SHIFT 0 /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8915_DAC2L_HPOUT2L_VOL_WIDTH 4 /* DAC2L_HPOUT2L_VOL - [3:0] */
+
+/*
+ * R24 (0x18) - DAC1 Left Volume
+ */
+#define WM8915_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */
+#define WM8915_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */
+#define WM8915_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */
+
+/*
+ * R25 (0x19) - DAC1 Right Volume
+ */
+#define WM8915_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */
+#define WM8915_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */
+#define WM8915_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */
+
+/*
+ * R26 (0x1A) - DAC2 Left Volume
+ */
+#define WM8915_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */
+#define WM8915_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */
+#define WM8915_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */
+
+/*
+ * R27 (0x1B) - DAC2 Right Volume
+ */
+#define WM8915_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */
+#define WM8915_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */
+#define WM8915_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */
+
+/*
+ * R28 (0x1C) - Output1 Left Volume
+ */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_VOL_MASK 0x000F /* HPOUT1L_VOL - [3:0] */
+#define WM8915_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [3:0] */
+#define WM8915_HPOUT1L_VOL_WIDTH 4 /* HPOUT1L_VOL - [3:0] */
+
+/*
+ * R29 (0x1D) - Output1 Right Volume
+ */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_VOL_MASK 0x000F /* HPOUT1R_VOL - [3:0] */
+#define WM8915_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [3:0] */
+#define WM8915_HPOUT1R_VOL_WIDTH 4 /* HPOUT1R_VOL - [3:0] */
+
+/*
+ * R30 (0x1E) - Output2 Left Volume
+ */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_HPOUT2L_ZC 0x0080 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_MASK 0x0080 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_SHIFT 7 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_VOL_MASK 0x000F /* HPOUT2L_VOL - [3:0] */
+#define WM8915_HPOUT2L_VOL_SHIFT 0 /* HPOUT2L_VOL - [3:0] */
+#define WM8915_HPOUT2L_VOL_WIDTH 4 /* HPOUT2L_VOL - [3:0] */
+
+/*
+ * R31 (0x1F) - Output2 Right Volume
+ */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_HPOUT2R_ZC 0x0080 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_MASK 0x0080 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_SHIFT 7 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_VOL_MASK 0x000F /* HPOUT2R_VOL - [3:0] */
+#define WM8915_HPOUT2R_VOL_SHIFT 0 /* HPOUT2R_VOL - [3:0] */
+#define WM8915_HPOUT2R_VOL_WIDTH 4 /* HPOUT2R_VOL - [3:0] */
+
+/*
+ * R32 (0x20) - MICBIAS (1)
+ */
+#define WM8915_MICB1_RATE 0x0020 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
+#define WM8915_MICB1_MODE 0x0010 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_SHIFT 4 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
+#define WM8915_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_DISCH 0x0001 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+
+/*
+ * R33 (0x21) - MICBIAS (2)
+ */
+#define WM8915_MICB2_RATE 0x0020 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
+#define WM8915_MICB2_MODE 0x0010 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_SHIFT 4 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
+#define WM8915_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_DISCH 0x0001 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+
+/*
+ * R40 (0x28) - LDO 1
+ */
+#define WM8915_LDO1_MODE 0x0020 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_SHIFT 5 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_WIDTH 1 /* LDO1_MODE */
+#define WM8915_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_DISCH 0x0001 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */
+
+/*
+ * R41 (0x29) - LDO 2
+ */
+#define WM8915_LDO2_MODE 0x0020 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_SHIFT 5 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_WIDTH 1 /* LDO2_MODE */
+#define WM8915_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_DISCH 0x0001 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
+
+/*
+ * R48 (0x30) - Accessory Detect Mode 1
+ */
+#define WM8915_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */
+#define WM8915_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */
+#define WM8915_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */
+
+/*
+ * R49 (0x31) - Accessory Detect Mode 2
+ */
+#define WM8915_HPOUT1FB_SRC 0x0004 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_MASK 0x0004 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_SHIFT 2 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_WIDTH 1 /* HPOUT1FB_SRC */
+#define WM8915_MICD_SRC 0x0002 /* MICD_SRC */
+#define WM8915_MICD_SRC_MASK 0x0002 /* MICD_SRC */
+#define WM8915_MICD_SRC_SHIFT 1 /* MICD_SRC */
+#define WM8915_MICD_SRC_WIDTH 1 /* MICD_SRC */
+#define WM8915_MICD_BIAS_SRC 0x0001 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_MASK 0x0001 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_SHIFT 0 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_WIDTH 1 /* MICD_BIAS_SRC */
+
+/*
+ * R52 (0x34) - Headphone Detect 1
+ */
+#define WM8915_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */
+#define WM8915_HP_POLL 0x0001 /* HP_POLL */
+#define WM8915_HP_POLL_MASK 0x0001 /* HP_POLL */
+#define WM8915_HP_POLL_SHIFT 0 /* HP_POLL */
+#define WM8915_HP_POLL_WIDTH 1 /* HP_POLL */
+
+/*
+ * R53 (0x35) - Headphone Detect 2
+ */
+#define WM8915_HP_DONE 0x0080 /* HP_DONE */
+#define WM8915_HP_DONE_MASK 0x0080 /* HP_DONE */
+#define WM8915_HP_DONE_SHIFT 7 /* HP_DONE */
+#define WM8915_HP_DONE_WIDTH 1 /* HP_DONE */
+#define WM8915_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
+#define WM8915_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
+#define WM8915_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
+
+/*
+ * R56 (0x38) - Mic Detect 1
+ */
+#define WM8915_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_DBTIME 0x0002 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */
+#define WM8915_MICD_ENA 0x0001 /* MICD_ENA */
+#define WM8915_MICD_ENA_MASK 0x0001 /* MICD_ENA */
+#define WM8915_MICD_ENA_SHIFT 0 /* MICD_ENA */
+#define WM8915_MICD_ENA_WIDTH 1 /* MICD_ENA */
+
+/*
+ * R57 (0x39) - Mic Detect 2
+ */
+#define WM8915_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */
+#define WM8915_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */
+#define WM8915_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */
+
+/*
+ * R58 (0x3A) - Mic Detect 3
+ */
+#define WM8915_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */
+#define WM8915_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */
+#define WM8915_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */
+#define WM8915_MICD_VALID 0x0002 /* MICD_VALID */
+#define WM8915_MICD_VALID_MASK 0x0002 /* MICD_VALID */
+#define WM8915_MICD_VALID_SHIFT 1 /* MICD_VALID */
+#define WM8915_MICD_VALID_WIDTH 1 /* MICD_VALID */
+#define WM8915_MICD_STS 0x0001 /* MICD_STS */
+#define WM8915_MICD_STS_MASK 0x0001 /* MICD_STS */
+#define WM8915_MICD_STS_SHIFT 0 /* MICD_STS */
+#define WM8915_MICD_STS_WIDTH 1 /* MICD_STS */
+
+/*
+ * R64 (0x40) - Charge Pump (1)
+ */
+#define WM8915_CP_ENA 0x8000 /* CP_ENA */
+#define WM8915_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8915_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8915_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R65 (0x41) - Charge Pump (2)
+ */
+#define WM8915_CP_DISCH 0x8000 /* CP_DISCH */
+#define WM8915_CP_DISCH_MASK 0x8000 /* CP_DISCH */
+#define WM8915_CP_DISCH_SHIFT 15 /* CP_DISCH */
+#define WM8915_CP_DISCH_WIDTH 1 /* CP_DISCH */
+
+/*
+ * R80 (0x50) - DC Servo (1)
+ */
+#define WM8915_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R81 (0x51) - DC Servo (2)
+ */
+#define WM8915_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R82 (0x52) - DC Servo (3)
+ */
+#define WM8915_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8915_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8915_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R84 (0x54) - DC Servo (5)
+ */
+#define WM8915_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8915_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8915_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R85 (0x55) - DC Servo (6)
+ */
+#define WM8915_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R86 (0x56) - DC Servo (7)
+ */
+#define WM8915_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R87 (0x57) - DC Servo Readback 0
+ */
+#define WM8915_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8915_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8915_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R96 (0x60) - Analogue HP (1)
+ */
+#define WM8915_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R97 (0x61) - Analogue HP (2)
+ */
+#define WM8915_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */
+
+/*
+ * R256 (0x100) - Chip Revision
+ */
+#define WM8915_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8915_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8915_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R257 (0x101) - Control Interface (1)
+ */
+#define WM8915_AUTO_INC 0x0004 /* AUTO_INC */
+#define WM8915_AUTO_INC_MASK 0x0004 /* AUTO_INC */
+#define WM8915_AUTO_INC_SHIFT 2 /* AUTO_INC */
+#define WM8915_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R272 (0x110) - Write Sequencer Ctrl (1)
+ */
+#define WM8915_WSEQ_ENA 0x8000 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8915_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8915_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8915_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8915_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8915_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8915_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM8915_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM8915_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R273 (0x111) - Write Sequencer Ctrl (2)
+ */
+#define WM8915_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+#define WM8915_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8915_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8915_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */
+
+/*
+ * R512 (0x200) - AIF Clocking (1)
+ */
+#define WM8915_SYSCLK_SRC_MASK 0x0018 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_SRC_SHIFT 3 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_INV 0x0004 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_MASK 0x0004 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_SHIFT 2 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_WIDTH 1 /* SYSCLK_INV */
+#define WM8915_SYSCLK_DIV 0x0002 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_MASK 0x0002 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_SHIFT 1 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_WIDTH 1 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_ENA 0x0001 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_MASK 0x0001 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_SHIFT 0 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+
+/*
+ * R513 (0x201) - AIF Clocking (2)
+ */
+#define WM8915_DSP2_DIV_MASK 0x0018 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP2_DIV_SHIFT 3 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP2_DIV_WIDTH 2 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP1_DIV_MASK 0x0003 /* DSP1_DIV - [1:0] */
+#define WM8915_DSP1_DIV_SHIFT 0 /* DSP1_DIV - [1:0] */
+#define WM8915_DSP1_DIV_WIDTH 2 /* DSP1_DIV - [1:0] */
+
+/*
+ * R520 (0x208) - Clocking (1)
+ */
+#define WM8915_LFCLK_ENA 0x0020 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */
+#define WM8915_TOCLK_ENA 0x0010 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8915_AIFCLK_ENA 0x0004 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_MASK 0x0004 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_SHIFT 2 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_WIDTH 1 /* AIFCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */
+
+/*
+ * R521 (0x209) - Clocking (2)
+ */
+#define WM8915_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */
+#define WM8915_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */
+#define WM8915_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */
+#define WM8915_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */
+#define WM8915_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */
+#define WM8915_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */
+#define WM8915_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */
+#define WM8915_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */
+#define WM8915_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */
+
+/*
+ * R528 (0x210) - AIF Rate
+ */
+#define WM8915_SYSCLK_RATE 0x0001 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_MASK 0x0001 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_SHIFT 0 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_WIDTH 1 /* SYSCLK_RATE */
+
+/*
+ * R544 (0x220) - FLL Control (1)
+ */
+#define WM8915_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8915_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8915_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8915_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8915_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R545 (0x221) - FLL Control (2)
+ */
+#define WM8915_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8915_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8915_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R546 (0x222) - FLL Control (3)
+ */
+#define WM8915_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
+#define WM8915_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
+#define WM8915_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
+
+/*
+ * R547 (0x223) - FLL Control (4)
+ */
+#define WM8915_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8915_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8915_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8915_FLL_LOOP_GAIN_MASK 0x000F /* FLL_LOOP_GAIN - [3:0] */
+#define WM8915_FLL_LOOP_GAIN_SHIFT 0 /* FLL_LOOP_GAIN - [3:0] */
+#define WM8915_FLL_LOOP_GAIN_WIDTH 4 /* FLL_LOOP_GAIN - [3:0] */
+
+/*
+ * R548 (0x224) - FLL Control (5)
+ */
+#define WM8915_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+#define WM8915_FLL_REFCLK_DIV_MASK 0x0018 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REFCLK_DIV_SHIFT 3 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REF_FREQ 0x0004 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_MASK 0x0004 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_SHIFT 2 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */
+#define WM8915_FLL_REFCLK_SRC_MASK 0x0003 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8915_FLL_REFCLK_SRC_SHIFT 0 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8915_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [1:0] */
+
+/*
+ * R549 (0x225) - FLL Control (6)
+ */
+#define WM8915_FLL_REFCLK_SRC_STS_MASK 0x000C /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_REFCLK_SRC_STS_SHIFT 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_REFCLK_SRC_STS_WIDTH 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_SWITCH_CLK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_MASK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_SHIFT 0 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_WIDTH 1 /* FLL_SWITCH_CLK */
+
+/*
+ * R550 (0x226) - FLL EFS 1
+ */
+#define WM8915_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
+#define WM8915_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
+#define WM8915_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
+
+/*
+ * R551 (0x227) - FLL EFS 2
+ */
+#define WM8915_FLL_LFSR_SEL_MASK 0x0006 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_LFSR_SEL_SHIFT 1 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_LFSR_SEL_WIDTH 2 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */
+
+/*
+ * R768 (0x300) - AIF1 Control
+ */
+#define WM8915_AIF1_TRI 0x0004 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_MASK 0x0004 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_SHIFT 2 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+#define WM8915_AIF1_FMT_MASK 0x0003 /* AIF1_FMT - [1:0] */
+#define WM8915_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [1:0] */
+#define WM8915_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [1:0] */
+
+/*
+ * R769 (0x301) - AIF1 BCLK
+ */
+#define WM8915_AIF1_BCLK_INV 0x0400 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_MASK 0x0400 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_SHIFT 10 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_FRC 0x0200 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_MASK 0x0200 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_SHIFT 9 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_MSTR 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_MASK 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_SHIFT 8 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */
+#define WM8915_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */
+#define WM8915_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */
+
+/*
+ * R770 (0x302) - AIF1 TX LRCLK(1)
+ */
+#define WM8915_AIF1TX_RATE_MASK 0x07FF /* AIF1TX_RATE - [10:0] */
+#define WM8915_AIF1TX_RATE_SHIFT 0 /* AIF1TX_RATE - [10:0] */
+#define WM8915_AIF1TX_RATE_WIDTH 11 /* AIF1TX_RATE - [10:0] */
+
+/*
+ * R771 (0x303) - AIF1 TX LRCLK(2)
+ */
+#define WM8915_AIF1TX_LRCLK_MODE 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_MASK 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_SHIFT 3 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_WIDTH 1 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R772 (0x304) - AIF1 RX LRCLK(1)
+ */
+#define WM8915_AIF1RX_RATE_MASK 0x07FF /* AIF1RX_RATE - [10:0] */
+#define WM8915_AIF1RX_RATE_SHIFT 0 /* AIF1RX_RATE - [10:0] */
+#define WM8915_AIF1RX_RATE_WIDTH 11 /* AIF1RX_RATE - [10:0] */
+
+/*
+ * R773 (0x305) - AIF1 RX LRCLK(2)
+ */
+#define WM8915_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R774 (0x306) - AIF1TX Data Configuration (1)
+ */
+#define WM8915_AIF1TX_WL_MASK 0xFF00 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_WL_WIDTH 8 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R775 (0x307) - AIF1TX Data Configuration (2)
+ */
+#define WM8915_AIF1TX_DAT_TRI 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_MASK 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_SHIFT 0 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
+
+/*
+ * R776 (0x308) - AIF1RX Data Configuration
+ */
+#define WM8915_AIF1RX_WL_MASK 0xFF00 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_WL_WIDTH 8 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R777 (0x309) - AIF1TX Channel 0 Configuration
+ */
+#define WM8915_AIF1TX_CHAN0_DAT_INV 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_SHIFT 15 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_WIDTH 1 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_SPACING_MASK 0x7E00 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SPACING_SHIFT 9 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SPACING_WIDTH 6 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_SHIFT 6 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_WIDTH 3 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_MASK 0x003F /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_SHIFT 0 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_WIDTH 6 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R778 (0x30A) - AIF1TX Channel 1 Configuration
+ */
+#define WM8915_AIF1TX_CHAN1_DAT_INV 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_SHIFT 15 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_WIDTH 1 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_SPACING_MASK 0x7E00 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SPACING_SHIFT 9 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SPACING_WIDTH 6 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_SHIFT 6 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_WIDTH 3 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_MASK 0x003F /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_SHIFT 0 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_WIDTH 6 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R779 (0x30B) - AIF1TX Channel 2 Configuration
+ */
+#define WM8915_AIF1TX_CHAN2_DAT_INV 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_SHIFT 15 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_WIDTH 1 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_SPACING_MASK 0x7E00 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SPACING_SHIFT 9 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SPACING_WIDTH 6 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_SHIFT 6 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_WIDTH 3 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_MASK 0x003F /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_SHIFT 0 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_WIDTH 6 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R780 (0x30C) - AIF1TX Channel 3 Configuration
+ */
+#define WM8915_AIF1TX_CHAN3_DAT_INV 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_SHIFT 15 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_WIDTH 1 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_SPACING_MASK 0x7E00 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SPACING_SHIFT 9 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SPACING_WIDTH 6 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_SHIFT 6 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_WIDTH 3 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_MASK 0x003F /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_SHIFT 0 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_WIDTH 6 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R781 (0x30D) - AIF1TX Channel 4 Configuration
+ */
+#define WM8915_AIF1TX_CHAN4_DAT_INV 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_SHIFT 15 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_WIDTH 1 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_SPACING_MASK 0x7E00 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SPACING_SHIFT 9 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SPACING_WIDTH 6 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_SHIFT 6 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_WIDTH 3 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_MASK 0x003F /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_SHIFT 0 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_WIDTH 6 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R782 (0x30E) - AIF1TX Channel 5 Configuration
+ */
+#define WM8915_AIF1TX_CHAN5_DAT_INV 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_SHIFT 15 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_WIDTH 1 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_SPACING_MASK 0x7E00 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SPACING_SHIFT 9 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SPACING_WIDTH 6 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_SHIFT 6 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_WIDTH 3 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_MASK 0x003F /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_SHIFT 0 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_WIDTH 6 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R783 (0x30F) - AIF1RX Channel 0 Configuration
+ */
+#define WM8915_AIF1RX_CHAN0_DAT_INV 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_SHIFT 15 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_WIDTH 1 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_SPACING_MASK 0x7E00 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SPACING_SHIFT 9 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SPACING_WIDTH 6 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_SHIFT 6 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_WIDTH 3 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_MASK 0x003F /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_SHIFT 0 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_WIDTH 6 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R784 (0x310) - AIF1RX Channel 1 Configuration
+ */
+#define WM8915_AIF1RX_CHAN1_DAT_INV 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_SHIFT 15 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_WIDTH 1 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_SPACING_MASK 0x7E00 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SPACING_SHIFT 9 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SPACING_WIDTH 6 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_SHIFT 6 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_WIDTH 3 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_MASK 0x003F /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_SHIFT 0 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_WIDTH 6 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R785 (0x311) - AIF1RX Channel 2 Configuration
+ */
+#define WM8915_AIF1RX_CHAN2_DAT_INV 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_SHIFT 15 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_WIDTH 1 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_SPACING_MASK 0x7E00 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SPACING_SHIFT 9 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SPACING_WIDTH 6 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_SHIFT 6 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_WIDTH 3 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_MASK 0x003F /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_SHIFT 0 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_WIDTH 6 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R786 (0x312) - AIF1RX Channel 3 Configuration
+ */
+#define WM8915_AIF1RX_CHAN3_DAT_INV 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_SHIFT 15 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_WIDTH 1 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_SPACING_MASK 0x7E00 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SPACING_SHIFT 9 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SPACING_WIDTH 6 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_SHIFT 6 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_WIDTH 3 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_MASK 0x003F /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_SHIFT 0 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_WIDTH 6 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R787 (0x313) - AIF1RX Channel 4 Configuration
+ */
+#define WM8915_AIF1RX_CHAN4_DAT_INV 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_SHIFT 15 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_WIDTH 1 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_SPACING_MASK 0x7E00 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SPACING_SHIFT 9 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SPACING_WIDTH 6 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_SHIFT 6 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_WIDTH 3 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_MASK 0x003F /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_SHIFT 0 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_WIDTH 6 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R788 (0x314) - AIF1RX Channel 5 Configuration
+ */
+#define WM8915_AIF1RX_CHAN5_DAT_INV 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_SHIFT 15 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_WIDTH 1 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_SPACING_MASK 0x7E00 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SPACING_SHIFT 9 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SPACING_WIDTH 6 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_SHIFT 6 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_WIDTH 3 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_MASK 0x003F /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_SHIFT 0 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_WIDTH 6 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R789 (0x315) - AIF1RX Mono Configuration
+ */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_MASK 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_SHIFT 2 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_MASK 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_SHIFT 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN0_MONO_MODE */
+
+/*
+ * R794 (0x31A) - AIF1TX Test
+ */
+#define WM8915_AIF1TX45_DITHER_ENA 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_MASK 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_SHIFT 2 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_WIDTH 1 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_MASK 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_SHIFT 1 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_WIDTH 1 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_MASK 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_SHIFT 0 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_WIDTH 1 /* AIF1TX01_DITHER_ENA */
+
+/*
+ * R800 (0x320) - AIF2 Control
+ */
+#define WM8915_AIF2_TRI 0x0004 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_MASK 0x0004 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_SHIFT 2 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
+#define WM8915_AIF2_FMT_MASK 0x0003 /* AIF2_FMT - [1:0] */
+#define WM8915_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [1:0] */
+#define WM8915_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [1:0] */
+
+/*
+ * R801 (0x321) - AIF2 BCLK
+ */
+#define WM8915_AIF2_BCLK_INV 0x0400 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_MASK 0x0400 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_SHIFT 10 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_FRC 0x0200 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_MASK 0x0200 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_SHIFT 9 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_MSTR 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_MASK 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_SHIFT 8 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_DIV_MASK 0x000F /* AIF2_BCLK_DIV - [3:0] */
+#define WM8915_AIF2_BCLK_DIV_SHIFT 0 /* AIF2_BCLK_DIV - [3:0] */
+#define WM8915_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [3:0] */
+
+/*
+ * R802 (0x322) - AIF2 TX LRCLK(1)
+ */
+#define WM8915_AIF2TX_RATE_MASK 0x07FF /* AIF2TX_RATE - [10:0] */
+#define WM8915_AIF2TX_RATE_SHIFT 0 /* AIF2TX_RATE - [10:0] */
+#define WM8915_AIF2TX_RATE_WIDTH 11 /* AIF2TX_RATE - [10:0] */
+
+/*
+ * R803 (0x323) - AIF2 TX LRCLK(2)
+ */
+#define WM8915_AIF2TX_LRCLK_MODE 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_MASK 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_SHIFT 3 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_WIDTH 1 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */
+
+/*
+ * R804 (0x324) - AIF2 RX LRCLK(1)
+ */
+#define WM8915_AIF2RX_RATE_MASK 0x07FF /* AIF2RX_RATE - [10:0] */
+#define WM8915_AIF2RX_RATE_SHIFT 0 /* AIF2RX_RATE - [10:0] */
+#define WM8915_AIF2RX_RATE_WIDTH 11 /* AIF2RX_RATE - [10:0] */
+
+/*
+ * R805 (0x325) - AIF2 RX LRCLK(2)
+ */
+#define WM8915_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */
+
+/*
+ * R806 (0x326) - AIF2TX Data Configuration (1)
+ */
+#define WM8915_AIF2TX_WL_MASK 0xFF00 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_WL_WIDTH 8 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */
+
+/*
+ * R807 (0x327) - AIF2TX Data Configuration (2)
+ */
+#define WM8915_AIF2TX_DAT_TRI 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_MASK 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_SHIFT 0 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */
+
+/*
+ * R808 (0x328) - AIF2RX Data Configuration
+ */
+#define WM8915_AIF2RX_WL_MASK 0xFF00 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_WL_WIDTH 8 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */
+
+/*
+ * R809 (0x329) - AIF2TX Channel 0 Configuration
+ */
+#define WM8915_AIF2TX_CHAN0_DAT_INV 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_SHIFT 15 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_WIDTH 1 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_SPACING_MASK 0x7E00 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SPACING_SHIFT 9 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SPACING_WIDTH 6 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_SHIFT 6 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_WIDTH 3 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_MASK 0x003F /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_SHIFT 0 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_WIDTH 6 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R810 (0x32A) - AIF2TX Channel 1 Configuration
+ */
+#define WM8915_AIF2TX_CHAN1_DAT_INV 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_SHIFT 15 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_WIDTH 1 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_SPACING_MASK 0x7E00 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SPACING_SHIFT 9 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SPACING_WIDTH 6 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_SHIFT 6 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_WIDTH 3 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_MASK 0x003F /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_SHIFT 0 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_WIDTH 6 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R811 (0x32B) - AIF2RX Channel 0 Configuration
+ */
+#define WM8915_AIF2RX_CHAN0_DAT_INV 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_SHIFT 15 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_WIDTH 1 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_SPACING_MASK 0x7E00 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SPACING_SHIFT 9 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SPACING_WIDTH 6 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_SHIFT 6 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_WIDTH 3 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_MASK 0x003F /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_SHIFT 0 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_WIDTH 6 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R812 (0x32C) - AIF2RX Channel 1 Configuration
+ */
+#define WM8915_AIF2RX_CHAN1_DAT_INV 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_SHIFT 15 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_WIDTH 1 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_SPACING_MASK 0x7E00 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SPACING_SHIFT 9 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SPACING_WIDTH 6 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_SHIFT 6 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_WIDTH 3 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_MASK 0x003F /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_SHIFT 0 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_WIDTH 6 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R813 (0x32D) - AIF2RX Mono Configuration
+ */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF2RX_CHAN0_MONO_MODE */
+
+/*
+ * R815 (0x32F) - AIF2TX Test
+ */
+#define WM8915_AIF2TX_DITHER_ENA 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_MASK 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_SHIFT 0 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_WIDTH 1 /* AIF2TX_DITHER_ENA */
+
+/*
+ * R1024 (0x400) - DSP1 TX Left Volume
+ */
+#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8915_DSP1TXL_VOL_MASK 0x00FF /* DSP1TXL_VOL - [7:0] */
+#define WM8915_DSP1TXL_VOL_SHIFT 0 /* DSP1TXL_VOL - [7:0] */
+#define WM8915_DSP1TXL_VOL_WIDTH 8 /* DSP1TXL_VOL - [7:0] */
+
+/*
+ * R1025 (0x401) - DSP1 TX Right Volume
+ */
+#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8915_DSP1TXR_VOL_MASK 0x00FF /* DSP1TXR_VOL - [7:0] */
+#define WM8915_DSP1TXR_VOL_SHIFT 0 /* DSP1TXR_VOL - [7:0] */
+#define WM8915_DSP1TXR_VOL_WIDTH 8 /* DSP1TXR_VOL - [7:0] */
+
+/*
+ * R1026 (0x402) - DSP1 RX Left Volume
+ */
+#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8915_DSP1RXL_VOL_MASK 0x00FF /* DSP1RXL_VOL - [7:0] */
+#define WM8915_DSP1RXL_VOL_SHIFT 0 /* DSP1RXL_VOL - [7:0] */
+#define WM8915_DSP1RXL_VOL_WIDTH 8 /* DSP1RXL_VOL - [7:0] */
+
+/*
+ * R1027 (0x403) - DSP1 RX Right Volume
+ */
+#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8915_DSP1RXR_VOL_MASK 0x00FF /* DSP1RXR_VOL - [7:0] */
+#define WM8915_DSP1RXR_VOL_SHIFT 0 /* DSP1RXR_VOL - [7:0] */
+#define WM8915_DSP1RXR_VOL_WIDTH 8 /* DSP1RXR_VOL - [7:0] */
+
+/*
+ * R1040 (0x410) - DSP1 TX Filters
+ */
+#define WM8915_DSP1TX_NF 0x2000 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_MASK 0x2000 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_SHIFT 13 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_WIDTH 1 /* DSP1TX_NF */
+#define WM8915_DSP1TXL_HPF 0x1000 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_MASK 0x1000 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_SHIFT 12 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_WIDTH 1 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXR_HPF 0x0800 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_MASK 0x0800 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_SHIFT 11 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_WIDTH 1 /* DSP1TXR_HPF */
+#define WM8915_DSP1TX_HPF_MODE_MASK 0x0018 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_MODE_SHIFT 3 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_MODE_WIDTH 2 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_CUT_MASK 0x0007 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8915_DSP1TX_HPF_CUT_SHIFT 0 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8915_DSP1TX_HPF_CUT_WIDTH 3 /* DSP1TX_HPF_CUT - [2:0] */
+
+/*
+ * R1056 (0x420) - DSP1 RX Filters (1)
+ */
+#define WM8915_DSP1RX_MUTE 0x0200 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_MASK 0x0200 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_SHIFT 9 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_WIDTH 1 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MONO 0x0080 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_MASK 0x0080 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_SHIFT 7 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_WIDTH 1 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MUTERATE 0x0020 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_MASK 0x0020 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_SHIFT 5 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_WIDTH 1 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_UNMUTE_RAMP 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_MASK 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_SHIFT 4 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_WIDTH 1 /* DSP1RX_UNMUTE_RAMP */
+
+/*
+ * R1057 (0x421) - DSP1 RX Filters (2)
+ */
+#define WM8915_DSP1RX_3D_GAIN_MASK 0x3E00 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_GAIN_SHIFT 9 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_GAIN_WIDTH 5 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_ENA 0x0100 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_MASK 0x0100 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_SHIFT 8 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_WIDTH 1 /* DSP1RX_3D_ENA */
+
+/*
+ * R1088 (0x440) - DSP1 DRC (1)
+ */
+#define WM8915_DSP1DRC_SIG_DET_RMS_MASK 0xF800 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_RMS_SHIFT 11 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_RMS_WIDTH 5 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_PK_MASK 0x0600 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_SIG_DET_PK_SHIFT 9 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_SIG_DET_PK_WIDTH 2 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_NG_ENA 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_MASK 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_SHIFT 8 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_WIDTH 1 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_SIG_DET_MODE 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_MASK 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_SHIFT 7 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_WIDTH 1 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_MASK 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_SHIFT 6 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_WIDTH 1 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_QR 0x0010 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_MASK 0x0010 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_SHIFT 4 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_WIDTH 1 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_ANTICLIP 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_MASK 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_SHIFT 3 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_WIDTH 1 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1RX_DRC_ENA 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_MASK 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_SHIFT 2 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_WIDTH 1 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_MASK 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_SHIFT 1 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_WIDTH 1 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_MASK 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_SHIFT 0 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_WIDTH 1 /* DSP1TXR_DRC_ENA */
+
+/*
+ * R1089 (0x441) - DSP1 DRC (2)
+ */
+#define WM8915_DSP1DRC_ATK_MASK 0x1E00 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_ATK_SHIFT 9 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_ATK_WIDTH 4 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_DCY_MASK 0x01E0 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_DCY_SHIFT 5 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_DCY_WIDTH 4 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_MINGAIN_MASK 0x001C /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MINGAIN_SHIFT 2 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MINGAIN_WIDTH 3 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MAXGAIN_MASK 0x0003 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP1DRC_MAXGAIN_SHIFT 0 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP1DRC_MAXGAIN_WIDTH 2 /* DSP1DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1090 (0x442) - DSP1 DRC (3)
+ */
+#define WM8915_DSP1DRC_NG_MINGAIN_MASK 0xF000 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_MINGAIN_SHIFT 12 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_MINGAIN_WIDTH 4 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_EXP_MASK 0x0C00 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_NG_EXP_SHIFT 10 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_NG_EXP_WIDTH 2 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_QR_THR_MASK 0x0300 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_THR_SHIFT 8 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_THR_WIDTH 2 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_DCY_MASK 0x00C0 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_QR_DCY_SHIFT 6 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_QR_DCY_WIDTH 2 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_HI_COMP_MASK 0x0038 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_HI_COMP_SHIFT 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_HI_COMP_WIDTH 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_LO_COMP_MASK 0x0007 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8915_DSP1DRC_LO_COMP_SHIFT 0 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8915_DSP1DRC_LO_COMP_WIDTH 3 /* DSP1DRC_LO_COMP - [2:0] */
+
+/*
+ * R1091 (0x443) - DSP1 DRC (4)
+ */
+#define WM8915_DSP1DRC_KNEE_IP_MASK 0x07E0 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_IP_SHIFT 5 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_IP_WIDTH 6 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_OP_MASK 0x001F /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE_OP_SHIFT 0 /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE_OP_WIDTH 5 /* DSP1DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1092 (0x444) - DSP1 DRC (5)
+ */
+#define WM8915_DSP1DRC_KNEE2_IP_MASK 0x03E0 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_IP_SHIFT 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_IP_WIDTH 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_OP_MASK 0x001F /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE2_OP_SHIFT 0 /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE2_OP_WIDTH 5 /* DSP1DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1152 (0x480) - DSP1 RX EQ Gains (1)
+ */
+#define WM8915_DSP1RX_EQ_B1_GAIN_MASK 0xF800 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B1_GAIN_SHIFT 11 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B1_GAIN_WIDTH 5 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_SHIFT 6 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_WIDTH 5 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_MASK 0x003E /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_SHIFT 1 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_WIDTH 5 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_ENA 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_MASK 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_SHIFT 0 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_WIDTH 1 /* DSP1RX_EQ_ENA */
+
+/*
+ * R1153 (0x481) - DSP1 RX EQ Gains (2)
+ */
+#define WM8915_DSP1RX_EQ_B4_GAIN_MASK 0xF800 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B4_GAIN_SHIFT 11 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B4_GAIN_WIDTH 5 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_SHIFT 6 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_WIDTH 5 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1154 (0x482) - DSP1 RX EQ Band 1 A
+ */
+#define WM8915_DSP1RX_EQ_B1_A_MASK 0xFFFF /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_A_SHIFT 0 /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_A_WIDTH 16 /* DSP1RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1155 (0x483) - DSP1 RX EQ Band 1 B
+ */
+#define WM8915_DSP1RX_EQ_B1_B_MASK 0xFFFF /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_B_SHIFT 0 /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_B_WIDTH 16 /* DSP1RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1156 (0x484) - DSP1 RX EQ Band 1 PG
+ */
+#define WM8915_DSP1RX_EQ_B1_PG_MASK 0xFFFF /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_PG_SHIFT 0 /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_PG_WIDTH 16 /* DSP1RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1157 (0x485) - DSP1 RX EQ Band 2 A
+ */
+#define WM8915_DSP1RX_EQ_B2_A_MASK 0xFFFF /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_A_SHIFT 0 /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_A_WIDTH 16 /* DSP1RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1158 (0x486) - DSP1 RX EQ Band 2 B
+ */
+#define WM8915_DSP1RX_EQ_B2_B_MASK 0xFFFF /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_B_SHIFT 0 /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_B_WIDTH 16 /* DSP1RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1159 (0x487) - DSP1 RX EQ Band 2 C
+ */
+#define WM8915_DSP1RX_EQ_B2_C_MASK 0xFFFF /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_C_SHIFT 0 /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_C_WIDTH 16 /* DSP1RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1160 (0x488) - DSP1 RX EQ Band 2 PG
+ */
+#define WM8915_DSP1RX_EQ_B2_PG_MASK 0xFFFF /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_PG_SHIFT 0 /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_PG_WIDTH 16 /* DSP1RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1161 (0x489) - DSP1 RX EQ Band 3 A
+ */
+#define WM8915_DSP1RX_EQ_B3_A_MASK 0xFFFF /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_A_SHIFT 0 /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_A_WIDTH 16 /* DSP1RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1162 (0x48A) - DSP1 RX EQ Band 3 B
+ */
+#define WM8915_DSP1RX_EQ_B3_B_MASK 0xFFFF /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_B_SHIFT 0 /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_B_WIDTH 16 /* DSP1RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1163 (0x48B) - DSP1 RX EQ Band 3 C
+ */
+#define WM8915_DSP1RX_EQ_B3_C_MASK 0xFFFF /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_C_SHIFT 0 /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_C_WIDTH 16 /* DSP1RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1164 (0x48C) - DSP1 RX EQ Band 3 PG
+ */
+#define WM8915_DSP1RX_EQ_B3_PG_MASK 0xFFFF /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_PG_SHIFT 0 /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_PG_WIDTH 16 /* DSP1RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1165 (0x48D) - DSP1 RX EQ Band 4 A
+ */
+#define WM8915_DSP1RX_EQ_B4_A_MASK 0xFFFF /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_A_SHIFT 0 /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_A_WIDTH 16 /* DSP1RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1166 (0x48E) - DSP1 RX EQ Band 4 B
+ */
+#define WM8915_DSP1RX_EQ_B4_B_MASK 0xFFFF /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_B_SHIFT 0 /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_B_WIDTH 16 /* DSP1RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1167 (0x48F) - DSP1 RX EQ Band 4 C
+ */
+#define WM8915_DSP1RX_EQ_B4_C_MASK 0xFFFF /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_C_SHIFT 0 /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_C_WIDTH 16 /* DSP1RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1168 (0x490) - DSP1 RX EQ Band 4 PG
+ */
+#define WM8915_DSP1RX_EQ_B4_PG_MASK 0xFFFF /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_PG_SHIFT 0 /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_PG_WIDTH 16 /* DSP1RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1169 (0x491) - DSP1 RX EQ Band 5 A
+ */
+#define WM8915_DSP1RX_EQ_B5_A_MASK 0xFFFF /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_A_SHIFT 0 /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_A_WIDTH 16 /* DSP1RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1170 (0x492) - DSP1 RX EQ Band 5 B
+ */
+#define WM8915_DSP1RX_EQ_B5_B_MASK 0xFFFF /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_B_SHIFT 0 /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_B_WIDTH 16 /* DSP1RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1171 (0x493) - DSP1 RX EQ Band 5 PG
+ */
+#define WM8915_DSP1RX_EQ_B5_PG_MASK 0xFFFF /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_PG_SHIFT 0 /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_PG_WIDTH 16 /* DSP1RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1280 (0x500) - DSP2 TX Left Volume
+ */
+#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8915_DSP2TXL_VOL_MASK 0x00FF /* DSP2TXL_VOL - [7:0] */
+#define WM8915_DSP2TXL_VOL_SHIFT 0 /* DSP2TXL_VOL - [7:0] */
+#define WM8915_DSP2TXL_VOL_WIDTH 8 /* DSP2TXL_VOL - [7:0] */
+
+/*
+ * R1281 (0x501) - DSP2 TX Right Volume
+ */
+#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8915_DSP2TXR_VOL_MASK 0x00FF /* DSP2TXR_VOL - [7:0] */
+#define WM8915_DSP2TXR_VOL_SHIFT 0 /* DSP2TXR_VOL - [7:0] */
+#define WM8915_DSP2TXR_VOL_WIDTH 8 /* DSP2TXR_VOL - [7:0] */
+
+/*
+ * R1282 (0x502) - DSP2 RX Left Volume
+ */
+#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8915_DSP2RXL_VOL_MASK 0x00FF /* DSP2RXL_VOL - [7:0] */
+#define WM8915_DSP2RXL_VOL_SHIFT 0 /* DSP2RXL_VOL - [7:0] */
+#define WM8915_DSP2RXL_VOL_WIDTH 8 /* DSP2RXL_VOL - [7:0] */
+
+/*
+ * R1283 (0x503) - DSP2 RX Right Volume
+ */
+#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8915_DSP2RXR_VOL_MASK 0x00FF /* DSP2RXR_VOL - [7:0] */
+#define WM8915_DSP2RXR_VOL_SHIFT 0 /* DSP2RXR_VOL - [7:0] */
+#define WM8915_DSP2RXR_VOL_WIDTH 8 /* DSP2RXR_VOL - [7:0] */
+
+/*
+ * R1296 (0x510) - DSP2 TX Filters
+ */
+#define WM8915_DSP2TX_NF 0x2000 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_MASK 0x2000 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_SHIFT 13 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_WIDTH 1 /* DSP2TX_NF */
+#define WM8915_DSP2TXL_HPF 0x1000 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_MASK 0x1000 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_SHIFT 12 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_WIDTH 1 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXR_HPF 0x0800 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_MASK 0x0800 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_SHIFT 11 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_WIDTH 1 /* DSP2TXR_HPF */
+#define WM8915_DSP2TX_HPF_MODE_MASK 0x0018 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_MODE_SHIFT 3 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_MODE_WIDTH 2 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_CUT_MASK 0x0007 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8915_DSP2TX_HPF_CUT_SHIFT 0 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8915_DSP2TX_HPF_CUT_WIDTH 3 /* DSP2TX_HPF_CUT - [2:0] */
+
+/*
+ * R1312 (0x520) - DSP2 RX Filters (1)
+ */
+#define WM8915_DSP2RX_MUTE 0x0200 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_MASK 0x0200 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_SHIFT 9 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_WIDTH 1 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MONO 0x0080 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_MASK 0x0080 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_SHIFT 7 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_WIDTH 1 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MUTERATE 0x0020 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_MASK 0x0020 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_SHIFT 5 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_WIDTH 1 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_UNMUTE_RAMP 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_MASK 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_SHIFT 4 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_WIDTH 1 /* DSP2RX_UNMUTE_RAMP */
+
+/*
+ * R1313 (0x521) - DSP2 RX Filters (2)
+ */
+#define WM8915_DSP2RX_3D_GAIN_MASK 0x3E00 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_GAIN_SHIFT 9 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_GAIN_WIDTH 5 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_ENA 0x0100 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_MASK 0x0100 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_SHIFT 8 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_WIDTH 1 /* DSP2RX_3D_ENA */
+
+/*
+ * R1344 (0x540) - DSP2 DRC (1)
+ */
+#define WM8915_DSP2DRC_SIG_DET_RMS_MASK 0xF800 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_RMS_SHIFT 11 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_RMS_WIDTH 5 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_PK_MASK 0x0600 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_SIG_DET_PK_SHIFT 9 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_SIG_DET_PK_WIDTH 2 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_NG_ENA 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_MASK 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_SHIFT 8 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_WIDTH 1 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_SIG_DET_MODE 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_MASK 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_SHIFT 7 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_WIDTH 1 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_MASK 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_SHIFT 6 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_WIDTH 1 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_QR 0x0010 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_MASK 0x0010 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_SHIFT 4 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_WIDTH 1 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_ANTICLIP 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_MASK 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_SHIFT 3 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_WIDTH 1 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2RX_DRC_ENA 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_MASK 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_SHIFT 2 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_WIDTH 1 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_MASK 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_SHIFT 1 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_WIDTH 1 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_MASK 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_SHIFT 0 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_WIDTH 1 /* DSP2TXR_DRC_ENA */
+
+/*
+ * R1345 (0x541) - DSP2 DRC (2)
+ */
+#define WM8915_DSP2DRC_ATK_MASK 0x1E00 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_ATK_SHIFT 9 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_ATK_WIDTH 4 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_DCY_MASK 0x01E0 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_DCY_SHIFT 5 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_DCY_WIDTH 4 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_MINGAIN_MASK 0x001C /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MINGAIN_SHIFT 2 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MINGAIN_WIDTH 3 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MAXGAIN_MASK 0x0003 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP2DRC_MAXGAIN_SHIFT 0 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP2DRC_MAXGAIN_WIDTH 2 /* DSP2DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1346 (0x542) - DSP2 DRC (3)
+ */
+#define WM8915_DSP2DRC_NG_MINGAIN_MASK 0xF000 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_MINGAIN_SHIFT 12 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_MINGAIN_WIDTH 4 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_EXP_MASK 0x0C00 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_NG_EXP_SHIFT 10 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_NG_EXP_WIDTH 2 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_QR_THR_MASK 0x0300 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_THR_SHIFT 8 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_THR_WIDTH 2 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_DCY_MASK 0x00C0 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_QR_DCY_SHIFT 6 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_QR_DCY_WIDTH 2 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_HI_COMP_MASK 0x0038 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_HI_COMP_SHIFT 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_HI_COMP_WIDTH 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_LO_COMP_MASK 0x0007 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8915_DSP2DRC_LO_COMP_SHIFT 0 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8915_DSP2DRC_LO_COMP_WIDTH 3 /* DSP2DRC_LO_COMP - [2:0] */
+
+/*
+ * R1347 (0x543) - DSP2 DRC (4)
+ */
+#define WM8915_DSP2DRC_KNEE_IP_MASK 0x07E0 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_IP_SHIFT 5 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_IP_WIDTH 6 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_OP_MASK 0x001F /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE_OP_SHIFT 0 /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE_OP_WIDTH 5 /* DSP2DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1348 (0x544) - DSP2 DRC (5)
+ */
+#define WM8915_DSP2DRC_KNEE2_IP_MASK 0x03E0 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_IP_SHIFT 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_IP_WIDTH 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_OP_MASK 0x001F /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE2_OP_SHIFT 0 /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE2_OP_WIDTH 5 /* DSP2DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1408 (0x580) - DSP2 RX EQ Gains (1)
+ */
+#define WM8915_DSP2RX_EQ_B1_GAIN_MASK 0xF800 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B1_GAIN_SHIFT 11 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B1_GAIN_WIDTH 5 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_SHIFT 6 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_WIDTH 5 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_MASK 0x003E /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_SHIFT 1 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_WIDTH 5 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_ENA 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_MASK 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_SHIFT 0 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_WIDTH 1 /* DSP2RX_EQ_ENA */
+
+/*
+ * R1409 (0x581) - DSP2 RX EQ Gains (2)
+ */
+#define WM8915_DSP2RX_EQ_B4_GAIN_MASK 0xF800 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B4_GAIN_SHIFT 11 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B4_GAIN_WIDTH 5 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_SHIFT 6 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_WIDTH 5 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1410 (0x582) - DSP2 RX EQ Band 1 A
+ */
+#define WM8915_DSP2RX_EQ_B1_A_MASK 0xFFFF /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_A_SHIFT 0 /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_A_WIDTH 16 /* DSP2RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1411 (0x583) - DSP2 RX EQ Band 1 B
+ */
+#define WM8915_DSP2RX_EQ_B1_B_MASK 0xFFFF /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_B_SHIFT 0 /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_B_WIDTH 16 /* DSP2RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1412 (0x584) - DSP2 RX EQ Band 1 PG
+ */
+#define WM8915_DSP2RX_EQ_B1_PG_MASK 0xFFFF /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_PG_SHIFT 0 /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_PG_WIDTH 16 /* DSP2RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1413 (0x585) - DSP2 RX EQ Band 2 A
+ */
+#define WM8915_DSP2RX_EQ_B2_A_MASK 0xFFFF /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_A_SHIFT 0 /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_A_WIDTH 16 /* DSP2RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1414 (0x586) - DSP2 RX EQ Band 2 B
+ */
+#define WM8915_DSP2RX_EQ_B2_B_MASK 0xFFFF /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_B_SHIFT 0 /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_B_WIDTH 16 /* DSP2RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1415 (0x587) - DSP2 RX EQ Band 2 C
+ */
+#define WM8915_DSP2RX_EQ_B2_C_MASK 0xFFFF /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_C_SHIFT 0 /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_C_WIDTH 16 /* DSP2RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1416 (0x588) - DSP2 RX EQ Band 2 PG
+ */
+#define WM8915_DSP2RX_EQ_B2_PG_MASK 0xFFFF /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_PG_SHIFT 0 /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_PG_WIDTH 16 /* DSP2RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1417 (0x589) - DSP2 RX EQ Band 3 A
+ */
+#define WM8915_DSP2RX_EQ_B3_A_MASK 0xFFFF /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_A_SHIFT 0 /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_A_WIDTH 16 /* DSP2RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1418 (0x58A) - DSP2 RX EQ Band 3 B
+ */
+#define WM8915_DSP2RX_EQ_B3_B_MASK 0xFFFF /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_B_SHIFT 0 /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_B_WIDTH 16 /* DSP2RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1419 (0x58B) - DSP2 RX EQ Band 3 C
+ */
+#define WM8915_DSP2RX_EQ_B3_C_MASK 0xFFFF /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_C_SHIFT 0 /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_C_WIDTH 16 /* DSP2RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1420 (0x58C) - DSP2 RX EQ Band 3 PG
+ */
+#define WM8915_DSP2RX_EQ_B3_PG_MASK 0xFFFF /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_PG_SHIFT 0 /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_PG_WIDTH 16 /* DSP2RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1421 (0x58D) - DSP2 RX EQ Band 4 A
+ */
+#define WM8915_DSP2RX_EQ_B4_A_MASK 0xFFFF /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_A_SHIFT 0 /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_A_WIDTH 16 /* DSP2RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1422 (0x58E) - DSP2 RX EQ Band 4 B
+ */
+#define WM8915_DSP2RX_EQ_B4_B_MASK 0xFFFF /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_B_SHIFT 0 /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_B_WIDTH 16 /* DSP2RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1423 (0x58F) - DSP2 RX EQ Band 4 C
+ */
+#define WM8915_DSP2RX_EQ_B4_C_MASK 0xFFFF /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_C_SHIFT 0 /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_C_WIDTH 16 /* DSP2RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1424 (0x590) - DSP2 RX EQ Band 4 PG
+ */
+#define WM8915_DSP2RX_EQ_B4_PG_MASK 0xFFFF /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_PG_SHIFT 0 /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_PG_WIDTH 16 /* DSP2RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1425 (0x591) - DSP2 RX EQ Band 5 A
+ */
+#define WM8915_DSP2RX_EQ_B5_A_MASK 0xFFFF /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_A_SHIFT 0 /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_A_WIDTH 16 /* DSP2RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1426 (0x592) - DSP2 RX EQ Band 5 B
+ */
+#define WM8915_DSP2RX_EQ_B5_B_MASK 0xFFFF /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_B_SHIFT 0 /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_B_WIDTH 16 /* DSP2RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1427 (0x593) - DSP2 RX EQ Band 5 PG
+ */
+#define WM8915_DSP2RX_EQ_B5_PG_MASK 0xFFFF /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_PG_SHIFT 0 /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_PG_WIDTH 16 /* DSP2RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1536 (0x600) - DAC1 Mixer Volumes
+ */
+#define WM8915_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */
+#define WM8915_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */
+#define WM8915_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */
+
+/*
+ * R1537 (0x601) - DAC1 Left Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_MASK 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_SHIFT 1 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_WIDTH 1 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_MASK 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_SHIFT 0 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_WIDTH 1 /* DSP1RXL_TO_DAC1L */
+
+/*
+ * R1538 (0x602) - DAC1 Right Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_MASK 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_SHIFT 1 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_WIDTH 1 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_MASK 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_SHIFT 0 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_WIDTH 1 /* DSP1RXR_TO_DAC1R */
+
+/*
+ * R1539 (0x603) - DAC2 Mixer Volumes
+ */
+#define WM8915_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */
+#define WM8915_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */
+#define WM8915_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */
+
+/*
+ * R1540 (0x604) - DAC2 Left Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_MASK 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_SHIFT 1 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_WIDTH 1 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_MASK 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_SHIFT 0 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_WIDTH 1 /* DSP1RXL_TO_DAC2L */
+
+/*
+ * R1541 (0x605) - DAC2 Right Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_MASK 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_SHIFT 1 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_WIDTH 1 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_MASK 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_SHIFT 0 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_WIDTH 1 /* DSP1RXR_TO_DAC2R */
+
+/*
+ * R1542 (0x606) - DSP1 TX Left Mixer Routing
+ */
+#define WM8915_ADC1L_TO_DSP1TXL 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_MASK 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_SHIFT 1 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_WIDTH 1 /* ADC1L_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_MASK 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_SHIFT 0 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_WIDTH 1 /* DACL_TO_DSP1TXL */
+
+/*
+ * R1543 (0x607) - DSP1 TX Right Mixer Routing
+ */
+#define WM8915_ADC1R_TO_DSP1TXR 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_MASK 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_SHIFT 1 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_WIDTH 1 /* ADC1R_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_MASK 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_SHIFT 0 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_WIDTH 1 /* DACR_TO_DSP1TXR */
+
+/*
+ * R1544 (0x608) - DSP2 TX Left Mixer Routing
+ */
+#define WM8915_ADC2L_TO_DSP2TXL 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_MASK 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_SHIFT 1 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_WIDTH 1 /* ADC2L_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_MASK 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_SHIFT 0 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_WIDTH 1 /* DACL_TO_DSP2TXL */
+
+/*
+ * R1545 (0x609) - DSP2 TX Right Mixer Routing
+ */
+#define WM8915_ADC2R_TO_DSP2TXR 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_MASK 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_SHIFT 1 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_WIDTH 1 /* ADC2R_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_MASK 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_SHIFT 0 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_WIDTH 1 /* DACR_TO_DSP2TXR */
+
+/*
+ * R1546 (0x60A) - DSP TX Mixer Select
+ */
+#define WM8915_DAC_TO_DSPTX_SRC 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_MASK 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_SHIFT 0 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_WIDTH 1 /* DAC_TO_DSPTX_SRC */
+
+/*
+ * R1552 (0x610) - DAC Softmute
+ */
+#define WM8915_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+
+/*
+ * R1568 (0x620) - Oversampling
+ */
+#define WM8915_SPK_OSR128 0x0008 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_MASK 0x0008 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_SHIFT 3 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_WIDTH 1 /* SPK_OSR128 */
+#define WM8915_DMIC_OSR64 0x0004 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_MASK 0x0004 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_SHIFT 2 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_WIDTH 1 /* DMIC_OSR64 */
+#define WM8915_ADC_OSR128 0x0002 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8915_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R1569 (0x621) - Sidetone
+ */
+#define WM8915_ST_LPF 0x1000 /* ST_LPF */
+#define WM8915_ST_LPF_MASK 0x1000 /* ST_LPF */
+#define WM8915_ST_LPF_SHIFT 12 /* ST_LPF */
+#define WM8915_ST_LPF_WIDTH 1 /* ST_LPF */
+#define WM8915_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF 0x0040 /* ST_HPF */
+#define WM8915_ST_HPF_MASK 0x0040 /* ST_HPF */
+#define WM8915_ST_HPF_SHIFT 6 /* ST_HPF */
+#define WM8915_ST_HPF_WIDTH 1 /* ST_HPF */
+#define WM8915_STR_SEL 0x0002 /* STR_SEL */
+#define WM8915_STR_SEL_MASK 0x0002 /* STR_SEL */
+#define WM8915_STR_SEL_SHIFT 1 /* STR_SEL */
+#define WM8915_STR_SEL_WIDTH 1 /* STR_SEL */
+#define WM8915_STL_SEL 0x0001 /* STL_SEL */
+#define WM8915_STL_SEL_MASK 0x0001 /* STL_SEL */
+#define WM8915_STL_SEL_SHIFT 0 /* STL_SEL */
+#define WM8915_STL_SEL_WIDTH 1 /* STL_SEL */
+
+/*
+ * R1792 (0x700) - GPIO 1
+ */
+#define WM8915_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM8915_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM8915_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM8915_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM8915_GP1_PU 0x4000 /* GP1_PU */
+#define WM8915_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM8915_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM8915_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM8915_GP1_PD 0x2000 /* GP1_PD */
+#define WM8915_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM8915_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM8915_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM8915_GP1_POL 0x0400 /* GP1_POL */
+#define WM8915_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM8915_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM8915_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM8915_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM8915_GP1_DB 0x0100 /* GP1_DB */
+#define WM8915_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM8915_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM8915_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM8915_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM8915_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM8915_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM8915_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM8915_GP1_FN_MASK 0x000F /* GP1_FN - [3:0] */
+#define WM8915_GP1_FN_SHIFT 0 /* GP1_FN - [3:0] */
+#define WM8915_GP1_FN_WIDTH 4 /* GP1_FN - [3:0] */
+
+/*
+ * R1793 (0x701) - GPIO 2
+ */
+#define WM8915_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM8915_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM8915_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM8915_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM8915_GP2_PU 0x4000 /* GP2_PU */
+#define WM8915_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM8915_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM8915_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM8915_GP2_PD 0x2000 /* GP2_PD */
+#define WM8915_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM8915_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM8915_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM8915_GP2_POL 0x0400 /* GP2_POL */
+#define WM8915_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM8915_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM8915_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM8915_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM8915_GP2_DB 0x0100 /* GP2_DB */
+#define WM8915_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM8915_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM8915_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM8915_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM8915_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM8915_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM8915_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8915_GP2_FN_MASK 0x000F /* GP2_FN - [3:0] */
+#define WM8915_GP2_FN_SHIFT 0 /* GP2_FN - [3:0] */
+#define WM8915_GP2_FN_WIDTH 4 /* GP2_FN - [3:0] */
+
+/*
+ * R1794 (0x702) - GPIO 3
+ */
+#define WM8915_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM8915_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM8915_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM8915_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM8915_GP3_PU 0x4000 /* GP3_PU */
+#define WM8915_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM8915_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM8915_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM8915_GP3_PD 0x2000 /* GP3_PD */
+#define WM8915_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM8915_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM8915_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM8915_GP3_POL 0x0400 /* GP3_POL */
+#define WM8915_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM8915_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM8915_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM8915_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM8915_GP3_DB 0x0100 /* GP3_DB */
+#define WM8915_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM8915_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM8915_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM8915_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM8915_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM8915_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM8915_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8915_GP3_FN_MASK 0x000F /* GP3_FN - [3:0] */
+#define WM8915_GP3_FN_SHIFT 0 /* GP3_FN - [3:0] */
+#define WM8915_GP3_FN_WIDTH 4 /* GP3_FN - [3:0] */
+
+/*
+ * R1795 (0x703) - GPIO 4
+ */
+#define WM8915_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM8915_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM8915_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM8915_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM8915_GP4_PU 0x4000 /* GP4_PU */
+#define WM8915_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM8915_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM8915_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM8915_GP4_PD 0x2000 /* GP4_PD */
+#define WM8915_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM8915_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM8915_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM8915_GP4_POL 0x0400 /* GP4_POL */
+#define WM8915_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM8915_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM8915_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM8915_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM8915_GP4_DB 0x0100 /* GP4_DB */
+#define WM8915_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM8915_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM8915_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM8915_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM8915_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM8915_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM8915_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM8915_GP4_FN_MASK 0x000F /* GP4_FN - [3:0] */
+#define WM8915_GP4_FN_SHIFT 0 /* GP4_FN - [3:0] */
+#define WM8915_GP4_FN_WIDTH 4 /* GP4_FN - [3:0] */
+
+/*
+ * R1796 (0x704) - GPIO 5
+ */
+#define WM8915_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM8915_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM8915_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM8915_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8915_GP5_PU 0x4000 /* GP5_PU */
+#define WM8915_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM8915_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM8915_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8915_GP5_PD 0x2000 /* GP5_PD */
+#define WM8915_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM8915_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM8915_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8915_GP5_POL 0x0400 /* GP5_POL */
+#define WM8915_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM8915_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM8915_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM8915_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8915_GP5_DB 0x0100 /* GP5_DB */
+#define WM8915_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM8915_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM8915_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM8915_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM8915_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM8915_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM8915_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8915_GP5_FN_MASK 0x000F /* GP5_FN - [3:0] */
+#define WM8915_GP5_FN_SHIFT 0 /* GP5_FN - [3:0] */
+#define WM8915_GP5_FN_WIDTH 4 /* GP5_FN - [3:0] */
+
+/*
+ * R1824 (0x720) - Pull Control (1)
+ */
+#define WM8915_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM8915_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+#define WM8915_MCLK2_PU 0x0200 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_SHIFT 9 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_WIDTH 1 /* MCLK2_PU */
+#define WM8915_MCLK2_PD 0x0100 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_SHIFT 8 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM8915_MCLK1_PU 0x0080 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_SHIFT 7 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_WIDTH 1 /* MCLK1_PU */
+#define WM8915_MCLK1_PD 0x0040 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_SHIFT 6 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM8915_DACDAT1_PU 0x0020 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PD 0x0010 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
+#define WM8915_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
+#define WM8915_BCLK1_PU 0x0002 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_SHIFT 1 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
+#define WM8915_BCLK1_PD 0x0001 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_SHIFT 0 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
+
+/*
+ * R1825 (0x721) - Pull Control (2)
+ */
+#define WM8915_LDO1ENA_PD 0x0100 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_MASK 0x0100 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_SHIFT 8 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM8915_ADDR_PD 0x0040 /* ADDR_PD */
+#define WM8915_ADDR_PD_MASK 0x0040 /* ADDR_PD */
+#define WM8915_ADDR_PD_SHIFT 6 /* ADDR_PD */
+#define WM8915_ADDR_PD_WIDTH 1 /* ADDR_PD */
+#define WM8915_DACDAT2_PU 0x0020 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_MASK 0x0020 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_SHIFT 5 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_WIDTH 1 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PD 0x0010 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_MASK 0x0010 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_SHIFT 4 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_WIDTH 1 /* DACDAT2_PD */
+#define WM8915_DACLRCLK2_PU 0x0008 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_MASK 0x0008 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_SHIFT 3 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_WIDTH 1 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PD 0x0004 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_MASK 0x0004 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_SHIFT 2 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_WIDTH 1 /* DACLRCLK2_PD */
+#define WM8915_BCLK2_PU 0x0002 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_MASK 0x0002 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_SHIFT 1 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_WIDTH 1 /* BCLK2_PU */
+#define WM8915_BCLK2_PD 0x0001 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_MASK 0x0001 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_SHIFT 0 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_WIDTH 1 /* BCLK2_PD */
+
+/*
+ * R1840 (0x730) - Interrupt Status 1
+ */
+#define WM8915_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8915_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8915_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8915_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8915_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8915_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8915_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8915_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8915_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8915_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8915_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8915_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8915_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8915_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8915_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8915_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8915_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8915_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8915_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8915_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R1841 (0x731) - Interrupt Status 2
+ */
+#define WM8915_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */
+#define WM8915_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
+#define WM8915_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8915_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */
+#define WM8915_MICD_EINT 0x0001 /* MICD_EINT */
+#define WM8915_MICD_EINT_MASK 0x0001 /* MICD_EINT */
+#define WM8915_MICD_EINT_SHIFT 0 /* MICD_EINT */
+#define WM8915_MICD_EINT_WIDTH 1 /* MICD_EINT */
+
+/*
+ * R1842 (0x732) - Interrupt Raw Status 2
+ */
+#define WM8915_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */
+#define WM8915_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */
+#define WM8915_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_MASK 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_SHIFT 7 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_WIDTH 1 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_MASK 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_SHIFT 6 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_WIDTH 1 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_FLL_LOCK_STS 0x0004 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_MASK 0x0004 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_SHIFT 2 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */
+
+/*
+ * R1848 (0x738) - Interrupt Status 1 Mask
+ */
+#define WM8915_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8915_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8915_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8915_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8915_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R1849 (0x739) - Interrupt Status 2 Mask
+ */
+#define WM8915_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */
+#define WM8915_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
+
+/*
+ * R1856 (0x740) - Interrupt Control
+ */
+#define WM8915_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM8915_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM8915_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM8915_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R2048 (0x800) - Left PDM Speaker
+ */
+#define WM8915_SPKL_ENA 0x0010 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_MASK 0x0010 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_SHIFT 4 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+#define WM8915_SPKL_MUTE 0x0008 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_MASK 0x0008 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_SHIFT 3 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_ZC 0x0004 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_MASK 0x0004 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_SHIFT 2 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_WIDTH 1 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_SRC_MASK 0x0003 /* SPKL_SRC - [1:0] */
+#define WM8915_SPKL_SRC_SHIFT 0 /* SPKL_SRC - [1:0] */
+#define WM8915_SPKL_SRC_WIDTH 2 /* SPKL_SRC - [1:0] */
+
+/*
+ * R2049 (0x801) - Right PDM Speaker
+ */
+#define WM8915_SPKR_ENA 0x0010 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_MASK 0x0010 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_SHIFT 4 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+#define WM8915_SPKR_MUTE 0x0008 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_MASK 0x0008 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_SHIFT 3 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_ZC 0x0004 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_MASK 0x0004 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_SHIFT 2 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_WIDTH 1 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_SRC_MASK 0x0003 /* SPKR_SRC - [1:0] */
+#define WM8915_SPKR_SRC_SHIFT 0 /* SPKR_SRC - [1:0] */
+#define WM8915_SPKR_SRC_WIDTH 2 /* SPKR_SRC - [1:0] */
+
+/*
+ * R2050 (0x802) - PDM Speaker Mute Sequence
+ */
+#define WM8915_SPK_MUTE_ENDIAN 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_MASK 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_SHIFT 8 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_WIDTH 1 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_SEQ1_MASK 0x00FF /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8915_SPK_MUTE_SEQ1_SHIFT 0 /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8915_SPK_MUTE_SEQ1_WIDTH 8 /* SPK_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R2051 (0x803) - PDM Speaker Volume
+ */
+#define WM8915_SPKR_VOL_MASK 0x00F0 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKR_VOL_SHIFT 4 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKR_VOL_WIDTH 4 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKL_VOL_MASK 0x000F /* SPKL_VOL - [3:0] */
+#define WM8915_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [3:0] */
+#define WM8915_SPKL_VOL_WIDTH 4 /* SPKL_VOL - [3:0] */
+
+#endif
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
new file mode 100644
index 000000000000..0293763debe5
--- /dev/null
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -0,0 +1,1051 @@
+/*
+ * wm8958-dsp2.c -- WM8958 DSP2 support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+
+#include "wm8994.h"
+
+#define WM_FW_BLOCK_INFO 0xff
+#define WM_FW_BLOCK_PM 0x00
+#define WM_FW_BLOCK_X 0x01
+#define WM_FW_BLOCK_Y 0x02
+#define WM_FW_BLOCK_Z 0x03
+#define WM_FW_BLOCK_I 0x06
+#define WM_FW_BLOCK_A 0x08
+#define WM_FW_BLOCK_C 0x0c
+
+static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
+ const struct firmware *fw, bool check)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ u64 data64;
+ u32 data32;
+ const u8 *data;
+ char *str;
+ size_t block_len, len;
+ int ret = 0;
+
+ /* Suppress unneeded downloads */
+ if (wm8994->cur_fw == fw)
+ return 0;
+
+ if (fw->size < 32) {
+ dev_err(codec->dev, "%s: firmware too short\n", name);
+ goto err;
+ }
+
+ if (memcmp(fw->data, "WMFW", 4) != 0) {
+ dev_err(codec->dev, "%s: firmware has bad file magic %08x\n",
+ name, data32);
+ goto err;
+ }
+
+ memcpy(&data32, fw->data + 4, sizeof(data32));
+ len = be32_to_cpu(data32);
+
+ memcpy(&data32, fw->data + 8, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+ if ((data32 >> 24) & 0xff) {
+ dev_err(codec->dev, "%s: unsupported firmware version %d\n",
+ name, (data32 >> 24) & 0xff);
+ goto err;
+ }
+ if ((data32 & 0xffff) != 8958) {
+ dev_err(codec->dev, "%s: unsupported target device %d\n",
+ name, data32 & 0xffff);
+ goto err;
+ }
+ if (((data32 >> 16) & 0xff) != 0xc) {
+ dev_err(codec->dev, "%s: unsupported target core %d\n",
+ name, (data32 >> 16) & 0xff);
+ goto err;
+ }
+
+ if (check) {
+ memcpy(&data64, fw->data + 24, sizeof(u64));
+ dev_info(codec->dev, "%s timestamp %llx\n",
+ name, be64_to_cpu(data64));
+ } else {
+ snd_soc_write(codec, 0x102, 0x2);
+ snd_soc_write(codec, 0x900, 0x2);
+ }
+
+ data = fw->data + len;
+ len = fw->size - len;
+ while (len) {
+ if (len < 12) {
+ dev_err(codec->dev, "%s short data block of %zd\n",
+ name, len);
+ goto err;
+ }
+
+ memcpy(&data32, data + 4, sizeof(data32));
+ block_len = be32_to_cpu(data32);
+ if (block_len + 8 > len) {
+ dev_err(codec->dev, "%zd byte block longer than file\n",
+ block_len);
+ goto err;
+ }
+ if (block_len == 0) {
+ dev_err(codec->dev, "Zero length block\n");
+ goto err;
+ }
+
+ memcpy(&data32, data, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+
+ switch ((data32 >> 24) & 0xff) {
+ case WM_FW_BLOCK_INFO:
+ /* Informational text */
+ if (!check)
+ break;
+
+ str = kzalloc(block_len + 1, GFP_KERNEL);
+ if (str) {
+ memcpy(str, data + 8, block_len);
+ dev_info(codec->dev, "%s: %s\n", name, str);
+ kfree(str);
+ } else {
+ dev_err(codec->dev, "Out of memory\n");
+ }
+ break;
+ case WM_FW_BLOCK_PM:
+ case WM_FW_BLOCK_X:
+ case WM_FW_BLOCK_Y:
+ case WM_FW_BLOCK_Z:
+ case WM_FW_BLOCK_I:
+ case WM_FW_BLOCK_A:
+ case WM_FW_BLOCK_C:
+ dev_dbg(codec->dev, "%s: %zd bytes of %x@%x\n", name,
+ block_len, (data32 >> 24) & 0xff,
+ data32 & 0xffffff);
+
+ if (check)
+ break;
+
+ data32 &= 0xffffff;
+
+ wm8994_bulk_write(codec->control_data,
+ data32 & 0xffffff,
+ block_len / 2,
+ (void *)(data + 8));
+
+ break;
+ default:
+ dev_warn(codec->dev, "%s: unknown block type %d\n",
+ name, (data32 >> 24) & 0xff);
+ break;
+ }
+
+ /* Round up to the next 32 bit word */
+ block_len += block_len % 4;
+
+ data += block_len + 8;
+ len -= block_len + 8;
+ }
+
+ if (!check) {
+ dev_dbg(codec->dev, "%s: download done\n", name);
+ wm8994->cur_fw = fw;
+ } else {
+ dev_info(codec->dev, "%s: got firmware\n", name);
+ }
+
+ goto ok;
+
+err:
+ ret = -EINVAL;
+ok:
+ if (!check) {
+ snd_soc_write(codec, 0x900, 0x0);
+ snd_soc_write(codec, 0x102, 0x0);
+ }
+
+ return ret;
+}
+
+static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i;
+
+ /* If the DSP is already running then noop */
+ if (snd_soc_read(codec, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
+ return;
+
+ /* If we have MBC firmware download it */
+ if (wm8994->mbc)
+ wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied MBC settings use them */
+ if (pdata && pdata->num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &pdata->mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
+ snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
+ cfg->coeff_regs[i]);
+
+ for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
+ snd_soc_write(codec,
+ i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
+ cfg->cutoff_regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* And we're off! */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA |
+ WM8958_MBC_SEL_MASK,
+ path << WM8958_MBC_SEL_SHIFT |
+ WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i, ena;
+
+ if (wm8994->mbc_vss)
+ wm8958_dsp2_fw(codec, "MBC+VSS", wm8994->mbc_vss, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (pdata && pdata->num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &pdata->mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
+ snd_soc_write(codec, i + 0x2800,
+ cfg->combined_regs[i]);
+ }
+
+ if (pdata && pdata->num_vss_cfgs) {
+ struct wm8958_vss_cfg *cfg
+ = &pdata->vss_cfgs[wm8994->vss_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2600, cfg->regs[i]);
+ }
+
+ if (pdata && pdata->num_vss_hpf_cfgs) {
+ struct wm8958_vss_hpf_cfg *cfg
+ = &pdata->vss_hpf_cfgs[wm8994->vss_hpf_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2400, cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Enable the algorithms we've selected */
+ ena = 0;
+ if (wm8994->mbc_ena[path])
+ ena |= 0x8;
+ if (wm8994->hpf2_ena[path])
+ ena |= 0x4;
+ if (wm8994->hpf1_ena[path])
+ ena |= 0x2;
+ if (wm8994->vss_ena[path])
+ ena |= 0x1;
+
+ snd_soc_write(codec, 0x2201, ena);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i;
+
+ wm8958_dsp2_fw(codec, "ENH_EQ", wm8994->enh_eq, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (pdata && pdata->num_enh_eq_cfgs) {
+ struct wm8958_enh_eq_cfg *cfg
+ = &pdata->enh_eq_cfgs[wm8994->enh_eq_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2200,
+ cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
+ int ena, reg, aif;
+
+ switch (path) {
+ case 0:
+ pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
+ aif = 0;
+ break;
+ case 1:
+ pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
+ aif = 0;
+ break;
+ case 2:
+ pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
+ aif = 1;
+ break;
+ default:
+ BUG();
+ return;
+ }
+
+ /* Do we have both an active AIF and an active algorithm? */
+ ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||
+ wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] ||
+ wm8994->enh_eq_ena[path];
+ if (!pwr_reg)
+ ena = 0;
+
+ reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
+
+ dev_dbg(codec->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
+ path, wm8994->dsp_active, start, pwr_reg, reg);
+
+ if (start && ena) {
+ /* If the DSP is already running then noop */
+ if (reg & WM8958_DSP2_ENA)
+ return;
+
+ /* If either AIFnCLK is not yet enabled postpone */
+ if (!(snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
+ & WM8994_AIF1CLK_ENA_MASK) &&
+ !(snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
+ & WM8994_AIF2CLK_ENA_MASK))
+ return;
+
+ /* Switch the clock over to the appropriate AIF */
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
+ aif << WM8958_DSP2CLK_SRC_SHIFT |
+ WM8958_DSP2CLK_ENA);
+
+ if (wm8994->enh_eq_ena[path])
+ wm8958_dsp_start_enh_eq(codec, path);
+ else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
+ wm8994->hpf2_ena[path])
+ wm8958_dsp_start_vss(codec, path);
+ else if (wm8994->mbc_ena[path])
+ wm8958_dsp_start_mbc(codec, path);
+
+ wm8994->dsp_active = path;
+
+ dev_dbg(codec->dev, "DSP running in path %d\n", path);
+ }
+
+ if (!start && wm8994->dsp_active == path) {
+ /* If the DSP is already stopped then noop */
+ if (!(reg & WM8958_DSP2_ENA))
+ return;
+
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA, 0);
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_STOP);
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, 0);
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_ENA, 0);
+
+ wm8994->dsp_active = -1;
+
+ dev_dbg(codec->dev, "DSP stopped\n");
+ }
+}
+
+int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMU:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* Check if DSP2 is in use on another AIF */
+static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
+ if (i == aif)
+ continue;
+ if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||
+ wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_mbc_cfgs)
+ return -EINVAL;
+
+ wm8994->mbc_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
+
+ return 0;
+}
+
+static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
+
+ return 0;
+}
+
+static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (wm8958_dsp2_busy(wm8994, mbc)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", mbc);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[mbc])
+ return -EBUSY;
+
+ wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]);
+
+ return 0;
+}
+
+#define WM8958_MBC_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_mbc_info, \
+ .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
+ .private_value = xval }
+
+static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_vss_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
+
+ return 0;
+}
+
+static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_vss_hpf_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_hpf_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
+
+ return 0;
+}
+
+static int wm8958_vss_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_vss_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
+
+ return 0;
+}
+
+static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, vss)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", vss);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[vss])
+ return -EBUSY;
+
+ wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]);
+
+ return 0;
+}
+
+
+#define WM8958_VSS_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_vss_info, \
+ .get = wm8958_vss_get, .put = wm8958_vss_put, \
+ .private_value = xval }
+
+static int wm8958_hpf_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3)
+ ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
+ else
+ ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
+
+ return 0;
+}
+
+static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3) {
+ if (wm8994->hpf1_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ } else {
+ if (wm8994->hpf2_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ }
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", hpf);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[hpf % 3])
+ return -EBUSY;
+
+ if (hpf < 3)
+ wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];
+ else
+ wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, hpf % 3, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_HPF_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_hpf_info, \
+ .get = wm8958_hpf_get, .put = wm8958_hpf_put, \
+ .private_value = xval }
+
+static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_enh_eq_cfgs)
+ return -EINVAL;
+
+ wm8994->enh_eq_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
+
+ return 0;
+}
+
+static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
+
+ return 0;
+}
+
+static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->enh_eq)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, eq)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", eq);
+ return -EBUSY;
+ }
+
+ if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] ||
+ wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq])
+ return -EBUSY;
+
+ wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, eq, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_ENH_EQ_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_enh_eq_info, \
+ .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \
+ .private_value = xval }
+
+static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
+WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
+WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
+WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
+};
+
+static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
+WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
+WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
+WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
+WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
+WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
+};
+
+static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {
+WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),
+WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),
+WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),
+};
+
+static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
+ mutex_lock(&codec->mutex);
+ wm8994->enh_eq = fw;
+ mutex_unlock(&codec->mutex);
+ }
+}
+
+static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
+ mutex_lock(&codec->mutex);
+ wm8994->mbc_vss = fw;
+ mutex_unlock(&codec->mutex);
+ }
+
+ /* We can't have more than one request outstanding at once so
+ * we daisy chain.
+ */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_enh_eq.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_enh_eq_loaded);
+}
+
+static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8958_dsp2_fw(codec, "MBC", fw, true) != 0)
+ return;
+
+ mutex_lock(&codec->mutex);
+ wm8994->mbc = fw;
+ mutex_unlock(&codec->mutex);
+
+ /* We can't have more than one request outstanding at once so
+ * we daisy chain.
+ */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc_vss.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_vss_loaded);
+}
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int ret, i;
+
+ wm8994->dsp_active = -1;
+
+ snd_soc_add_controls(codec, wm8958_mbc_snd_controls,
+ ARRAY_SIZE(wm8958_mbc_snd_controls));
+ snd_soc_add_controls(codec, wm8958_vss_snd_controls,
+ ARRAY_SIZE(wm8958_vss_snd_controls));
+ snd_soc_add_controls(codec, wm8958_enh_eq_snd_controls,
+ ARRAY_SIZE(wm8958_enh_eq_snd_controls));
+
+
+ /* We don't *require* firmware and don't want to delay boot */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_loaded);
+
+ if (!pdata)
+ return;
+
+ if (pdata->num_mbc_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
+ wm8958_get_mbc_enum, wm8958_put_mbc_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->mbc_texts = kmalloc(sizeof(char *)
+ * pdata->num_mbc_cfgs, GFP_KERNEL);
+ if (!wm8994->mbc_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d MBC config texts\n",
+ pdata->num_mbc_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_mbc_cfgs; i++)
+ wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
+
+ wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
+ wm8994->mbc_enum.texts = wm8994->mbc_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add MBC mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
+ wm8958_get_vss_enum, wm8958_put_vss_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d VSS config texts\n",
+ pdata->num_vss_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_vss_cfgs; i++)
+ wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
+
+ wm8994->vss_enum.max = pdata->num_vss_cfgs;
+ wm8994->vss_enum.texts = wm8994->vss_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add VSS mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_hpf_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
+ wm8958_get_vss_hpf_enum,
+ wm8958_put_vss_hpf_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_hpf_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_hpf_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d VSS HPF config texts\n",
+ pdata->num_vss_hpf_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
+ wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
+
+ wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs;
+ wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add VSS HPFmode controls: %d\n",
+ ret);
+ }
+
+ if (pdata->num_enh_eq_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,
+ wm8958_get_enh_eq_enum,
+ wm8958_put_enh_eq_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->enh_eq_texts = kmalloc(sizeof(char *)
+ * pdata->num_enh_eq_cfgs, GFP_KERNEL);
+ if (!wm8994->enh_eq_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d enhanced EQ config texts\n",
+ pdata->num_enh_eq_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_enh_eq_cfgs; i++)
+ wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;
+
+ wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs;
+ wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add enhanced EQ controls: %d\n",
+ ret);
+ }
+}
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 500011eb8b2b..f90ae427242b 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -58,6 +58,7 @@ struct wm8962_priv {
int bclk; /* Desired BCLK */
int lrclk;
+ struct completion fll_lock;
int fll_src;
int fll_fref;
int fll_fout;
@@ -2038,6 +2039,13 @@ static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
return 0;
}
+static const char *cap_hpf_mode_text[] = {
+ "Hi-fi", "Application"
+};
+
+static const struct soc_enum cap_hpf_mode =
+ SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
+
static const struct snd_kcontrol_new wm8962_snd_controls[] = {
SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
@@ -2063,6 +2071,9 @@ SOC_DOUBLE_R("Capture Switch", WM8962_LEFT_INPUT_VOLUME,
WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1),
SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1),
+SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
+SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
+SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
@@ -2467,6 +2478,7 @@ SND_SOC_DAPM_INPUT("IN3R"),
SND_SOC_DAPM_INPUT("IN4L"),
SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_INPUT("Beep"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0),
@@ -2486,6 +2498,8 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0,
SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
mixinr, ARRAY_SIZE(mixinr)),
+SND_SOC_DAPM_AIF_IN("DMIC", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
+
SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
@@ -2563,13 +2577,17 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
{ "MICBIAS", NULL, "SYSCLK" },
+ { "DMIC", NULL, "DMICDAT" },
+
{ "ADCL", NULL, "SYSCLK" },
{ "ADCL", NULL, "TOCLK" },
{ "ADCL", NULL, "MIXINL" },
+ { "ADCL", NULL, "DMIC" },
{ "ADCR", NULL, "SYSCLK" },
{ "ADCR", NULL, "TOCLK" },
{ "ADCR", NULL, "MIXINR" },
+ { "ADCR", NULL, "DMIC" },
{ "STL", "Left", "ADCL" },
{ "STL", "Right", "ADCR" },
@@ -2990,7 +3008,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
case WM8962_SYSCLK_FLL:
wm8962->sysclk = WM8962_SYSCLK_FLL;
src = 1 << WM8962_SYSCLK_SRC_SHIFT;
- WARN_ON(freq != wm8962->fll_fout);
break;
default:
return -EINVAL;
@@ -3172,12 +3189,12 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct snd_soc_codec *codec = dai->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct _fll_div fll_div;
+ unsigned long timeout;
int ret;
int fll1 = snd_soc_read(codec, WM8962_FLL_CONTROL_1) & WM8962_FLL_ENA;
@@ -3244,6 +3261,11 @@ static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+ /* This should be a massive overestimate */
+ timeout = msecs_to_jiffies(1);
+
+ wait_for_completion_timeout(&wm8962->fll_lock, timeout);
+
wm8962->fll_fref = Fref;
wm8962->fll_fout = Fout;
wm8962->fll_src = source;
@@ -3274,7 +3296,6 @@ static struct snd_soc_dai_ops wm8962_dai_ops = {
.hw_params = wm8962_hw_params,
.set_sysclk = wm8962_set_dai_sysclk,
.set_fmt = wm8962_set_dai_fmt,
- .set_pll = wm8962_set_fll,
.digital_mute = wm8962_mute,
};
@@ -3340,6 +3361,11 @@ static irqreturn_t wm8962_irq(int irq, void *data)
active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
active &= ~mask;
+ if (active & WM8962_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8962->fll_lock);
+ }
+
if (active & WM8962_FIFOS_ERR_EINT)
dev_err(codec->dev, "FIFO error\n");
@@ -3709,9 +3735,11 @@ static int wm8962_probe(struct snd_soc_codec *codec)
dev);
u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
+ bool dmicclk, dmicdat;
wm8962->codec = codec;
INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
+ init_completion(&wm8962->fll_lock);
codec->cache_sync = 1;
codec->dapm.idle_bias_off = 1;
@@ -3845,6 +3873,29 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_add_widgets(codec);
+ /* Save boards having to disable DMIC when not in use */
+ dmicclk = false;
+ dmicdat = false;
+ for (i = 0; i < WM8962_MAX_GPIO; i++) {
+ switch (snd_soc_read(codec, WM8962_GPIO_BASE + i)
+ & WM8962_GP2_FN_MASK) {
+ case WM8962_GPIO_FN_DMICCLK:
+ dmicclk = true;
+ break;
+ case WM8962_GPIO_FN_DMICDAT:
+ dmicdat = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!dmicclk || !dmicdat) {
+ dev_dbg(codec->dev, "DMIC not in use, disabling\n");
+ snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
+ }
+ if (dmicclk != dmicdat)
+ dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
+
wm8962_init_beep(codec);
wm8962_init_gpio(codec);
@@ -3868,9 +3919,10 @@ static int wm8962_probe(struct snd_soc_codec *codec)
i2c->irq, ret);
/* Non-fatal */
} else {
- /* Enable error reporting IRQs by default */
+ /* Enable some IRQs by default */
snd_soc_update_bits(codec,
WM8962_INTERRUPT_STATUS_2_MASK,
+ WM8962_FLL_LOCK_EINT |
WM8962_TEMP_SHUT_EINT |
WM8962_FIFOS_ERR_EINT, 0);
}
@@ -3918,6 +3970,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
.reg_cache_default = wm8962_reg,
.volatile_register = wm8962_volatile_register,
.readable_register = wm8962_readable_register,
+ .set_pll = wm8962_set_fll,
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 056aef904347..9e5ff789b805 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -718,7 +718,8 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
static int class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int ret;
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 84e1bd1d2822..970a95c5360b 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -38,12 +38,6 @@
#include "wm8994.h"
#include "wm_hubs.h"
-struct fll_config {
- int src;
- int in;
- int out;
-};
-
#define WM8994_NUM_DRC 3
#define WM8994_NUM_EQ 3
@@ -59,63 +53,11 @@ static int wm8994_retune_mobile_base[] = {
WM8994_AIF2_EQ_GAINS_1,
};
-struct wm8994_micdet {
- struct snd_soc_jack *jack;
- int det;
- int shrt;
-};
-
-/* codec private data */
-struct wm8994_priv {
- struct wm_hubs_data hubs;
- enum snd_soc_control_type control_type;
- void *control_data;
- struct snd_soc_codec *codec;
- int sysclk[2];
- int sysclk_rate[2];
- int mclk[2];
- int aifclk[2];
- struct fll_config fll[2], fll_suspend[2];
-
- int dac_rates[2];
- int lrclk_shared[2];
-
- int mbc_ena[3];
-
- /* Platform dependent DRC configuration */
- const char **drc_texts;
- int drc_cfg[WM8994_NUM_DRC];
- struct soc_enum drc_enum;
-
- /* Platform dependent ReTune mobile configuration */
- int num_retune_mobile_texts;
- const char **retune_mobile_texts;
- int retune_mobile_cfg[WM8994_NUM_EQ];
- struct soc_enum retune_mobile_enum;
-
- /* Platform dependent MBC configuration */
- int mbc_cfg;
- const char **mbc_texts;
- struct soc_enum mbc_enum;
-
- struct wm8994_micdet micdet[2];
-
- wm8958_micdet_cb jack_cb;
- void *jack_cb_data;
- int micdet_irq;
-
- int revision;
- struct wm8994_pdata *pdata;
-
- unsigned int aif1clk_enable:1;
- unsigned int aif2clk_enable:1;
-
- unsigned int aif1clk_disable:1;
- unsigned int aif2clk_disable:1;
-};
-
static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->control_data;
+
switch (reg) {
case WM8994_GPIO_1:
case WM8994_GPIO_2:
@@ -132,6 +74,15 @@ static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
case WM8994_INTERRUPT_STATUS_2:
case WM8994_INTERRUPT_RAW_STATUS_2:
return 1;
+
+ case WM8958_DSP2_PROGRAM:
+ case WM8958_DSP2_CONFIG:
+ case WM8958_DSP2_EXECCONTROL:
+ if (control->type == WM8958)
+ return 1;
+ else
+ return 0;
+
default:
break;
}
@@ -574,215 +525,6 @@ static const struct soc_enum dac_osr =
static const struct soc_enum adc_osr =
SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text);
-static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
-{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994_pdata *pdata = wm8994->pdata;
- int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
- int ena, reg, aif, i;
-
- switch (mbc) {
- case 0:
- pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
- aif = 0;
- break;
- case 1:
- pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
- aif = 0;
- break;
- case 2:
- pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
- aif = 1;
- break;
- default:
- BUG();
- return;
- }
-
- /* We can only enable the MBC if the AIF is enabled and we
- * want it to be enabled. */
- ena = pwr_reg && wm8994->mbc_ena[mbc];
-
- reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
-
- dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n",
- mbc, start, pwr_reg, reg);
-
- if (start && ena) {
- /* If the DSP is already running then noop */
- if (reg & WM8958_DSP2_ENA)
- return;
-
- /* Switch the clock over to the appropriate AIF */
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
- WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
- aif << WM8958_DSP2CLK_SRC_SHIFT |
- WM8958_DSP2CLK_ENA);
-
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
- WM8958_DSP2_ENA, WM8958_DSP2_ENA);
-
- /* If we've got user supplied MBC settings use them */
- if (pdata && pdata->num_mbc_cfgs) {
- struct wm8958_mbc_cfg *cfg
- = &pdata->mbc_cfgs[wm8994->mbc_cfg];
-
- for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
- snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
- cfg->coeff_regs[i]);
-
- for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
- snd_soc_write(codec,
- i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
- cfg->cutoff_regs[i]);
- }
-
- /* Run the DSP */
- snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
- WM8958_DSP2_RUNR);
-
- /* And we're off! */
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
- WM8958_MBC_ENA | WM8958_MBC_SEL_MASK,
- mbc << WM8958_MBC_SEL_SHIFT |
- WM8958_MBC_ENA);
- } else {
- /* If the DSP is already stopped then noop */
- if (!(reg & WM8958_DSP2_ENA))
- return;
-
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
- WM8958_MBC_ENA, 0);
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
- WM8958_DSP2_ENA, 0);
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
- WM8958_DSP2CLK_ENA, 0);
- }
-}
-
-static int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- int mbc;
-
- switch (w->shift) {
- case 13:
- case 12:
- mbc = 2;
- break;
- case 11:
- case 10:
- mbc = 1;
- break;
- case 9:
- case 8:
- mbc = 0;
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- wm8958_mbc_apply(codec, mbc, 1);
- break;
- case SND_SOC_DAPM_POST_PMD:
- wm8958_mbc_apply(codec, mbc, 0);
- break;
- }
-
- return 0;
-}
-
-static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994_pdata *pdata = wm8994->pdata;
- int value = ucontrol->value.integer.value[0];
- int reg;
-
- /* Don't allow on the fly reconfiguration */
- reg = snd_soc_read(codec, WM8994_CLOCKING_1);
- if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
- return -EBUSY;
-
- if (value >= pdata->num_mbc_cfgs)
- return -EINVAL;
-
- wm8994->mbc_cfg = value;
-
- return 0;
-}
-
-static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
-
- return 0;
-}
-
-static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
-
-static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int mbc = kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
-
- return 0;
-}
-
-static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int mbc = kcontrol->private_value;
- int i;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- if (ucontrol->value.integer.value[0] > 1)
- return -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
- if (mbc != i && wm8994->mbc_ena[i]) {
- dev_dbg(codec->dev, "MBC %d active already\n", mbc);
- return -EBUSY;
- }
- }
-
- wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
-
- wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]);
-
- return 0;
-}
-
-#define WM8958_MBC_SWITCH(xname, xval) {\
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .info = wm8958_mbc_info, \
- .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
- .private_value = xval }
-
static const struct snd_kcontrol_new wm8994_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
@@ -924,9 +666,6 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
static const struct snd_kcontrol_new wm8958_snd_controls[] = {
SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
-WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
-WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
-WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
};
static int clk_sys_event(struct snd_soc_dapm_widget *w,
@@ -1032,6 +771,9 @@ static int late_enable_ev(struct snd_soc_dapm_widget *w,
break;
}
+ /* We may also have postponed startup of DSP, handle that. */
+ wm8958_aif_ev(w, kcontrol, event);
+
return 0;
}
@@ -1135,7 +877,8 @@ static const char *hp_mux_text[] = {
static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec;
int ret;
@@ -1262,7 +1005,8 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec;
int ret;
@@ -2180,6 +1924,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK, 0);
+ wm8994->cur_fw = NULL;
+
pm_runtime_put(codec->dev);
}
break;
@@ -2672,11 +2418,22 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = codec->control_data;
int i, ret;
+ switch (control->type) {
+ case WM8994:
+ snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
+ break;
+ case WM8958:
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, 0);
+ break;
+ }
+
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
- sizeof(struct fll_config));
+ sizeof(struct wm8994_fll_config));
ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0);
if (ret < 0)
dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
@@ -2691,6 +2448,7 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int wm8994_resume(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = codec->control_data;
int i, ret;
unsigned int val, mask;
@@ -2729,6 +2487,19 @@ static int wm8994_resume(struct snd_soc_codec *codec)
i + 1, ret);
}
+ switch (control->type) {
+ case WM8994:
+ if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
+ snd_soc_update_bits(codec, WM8994_MICBIAS,
+ WM8994_MICD_ENA, WM8994_MICD_ENA);
+ break;
+ case WM8958:
+ if (wm8994->jack_cb)
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, WM8958_MICD_ENA);
+ break;
+ }
+
return 0;
}
#else
@@ -2862,34 +2633,6 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
pdata->num_retune_mobile_cfgs);
- if (pdata->num_mbc_cfgs) {
- struct snd_kcontrol_new control[] = {
- SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
- wm8958_get_mbc_enum, wm8958_put_mbc_enum),
- };
-
- /* We need an array of texts for the enum API */
- wm8994->mbc_texts = kmalloc(sizeof(char *)
- * pdata->num_mbc_cfgs, GFP_KERNEL);
- if (!wm8994->mbc_texts) {
- dev_err(wm8994->codec->dev,
- "Failed to allocate %d MBC config texts\n",
- pdata->num_mbc_cfgs);
- return;
- }
-
- for (i = 0; i < pdata->num_mbc_cfgs; i++)
- wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
-
- wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
- wm8994->mbc_enum.texts = wm8994->mbc_texts;
-
- ret = snd_soc_add_controls(wm8994->codec, control, 1);
- if (ret != 0)
- dev_err(wm8994->codec->dev,
- "Failed to add MBC mode controls: %d\n", ret);
- }
-
if (pdata->num_retune_mobile_cfgs)
wm8994_handle_retune_mobile_pdata(wm8994);
else
@@ -3343,14 +3086,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
case WM8958:
snd_soc_add_controls(codec, wm8958_snd_controls,
ARRAY_SIZE(wm8958_snd_controls));
- snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
- ARRAY_SIZE(wm8994_lateclk_widgets));
- snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
- ARRAY_SIZE(wm8994_adc_widgets));
- snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
- ARRAY_SIZE(wm8994_dac_widgets));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
+ if (wm8994->revision < 1) {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
+ ARRAY_SIZE(wm8994_lateclk_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
+ ARRAY_SIZE(wm8994_adc_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets,
+ ARRAY_SIZE(wm8994_dac_revd_widgets));
+ } else {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
+ ARRAY_SIZE(wm8994_lateclk_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
+ ARRAY_SIZE(wm8994_adc_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
+ ARRAY_SIZE(wm8994_dac_widgets));
+ }
break;
}
@@ -3374,10 +3126,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
break;
case WM8958:
- snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
- ARRAY_SIZE(wm8994_lateclk_intercon));
- snd_soc_dapm_add_routes(dapm, wm8958_intercon,
- ARRAY_SIZE(wm8958_intercon));
+ if (wm8994->revision < 1) {
+ snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
+ ARRAY_SIZE(wm8994_revd_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
+ ARRAY_SIZE(wm8994_lateclk_revd_intercon));
+ } else {
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
+ ARRAY_SIZE(wm8994_lateclk_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8958_intercon,
+ ARRAY_SIZE(wm8958_intercon));
+ }
+
+ wm8958_dsp2_init(codec);
break;
}
@@ -3420,6 +3181,12 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
free_irq(wm8994->micdet_irq, wm8994);
break;
}
+ if (wm8994->mbc)
+ release_firmware(wm8994->mbc);
+ if (wm8994->mbc_vss)
+ release_firmware(wm8994->mbc_vss);
+ if (wm8994->enh_eq)
+ release_firmware(wm8994->enh_eq);
kfree(wm8994->retune_mobile_texts);
kfree(wm8994->drc_texts);
kfree(wm8994);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 999b8851226b..0a1db04b73bd 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -10,6 +10,9 @@
#define _WM8994_H
#include <sound/soc.h>
+#include <linux/firmware.h>
+
+#include "wm_hubs.h"
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
@@ -45,4 +48,98 @@ struct wm8994_access_mask {
extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE];
extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
+int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec);
+
+struct wm8994_micdet {
+ struct snd_soc_jack *jack;
+ int det;
+ int shrt;
+};
+
+/* codec private data */
+struct wm8994_fll_config {
+ int src;
+ int in;
+ int out;
+};
+
+#define WM8994_NUM_DRC 3
+#define WM8994_NUM_EQ 3
+
+struct wm8994_priv {
+ struct wm_hubs_data hubs;
+ enum snd_soc_control_type control_type;
+ void *control_data;
+ struct snd_soc_codec *codec;
+ int sysclk[2];
+ int sysclk_rate[2];
+ int mclk[2];
+ int aifclk[2];
+ struct wm8994_fll_config fll[2], fll_suspend[2];
+
+ int dac_rates[2];
+ int lrclk_shared[2];
+
+ int mbc_ena[3];
+ int hpf1_ena[3];
+ int hpf2_ena[3];
+ int vss_ena[3];
+ int enh_eq_ena[3];
+
+ /* Platform dependant DRC configuration */
+ const char **drc_texts;
+ int drc_cfg[WM8994_NUM_DRC];
+ struct soc_enum drc_enum;
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[WM8994_NUM_EQ];
+ struct soc_enum retune_mobile_enum;
+
+ /* Platform dependant MBC configuration */
+ int mbc_cfg;
+ const char **mbc_texts;
+ struct soc_enum mbc_enum;
+
+ /* Platform dependant VSS configuration */
+ int vss_cfg;
+ const char **vss_texts;
+ struct soc_enum vss_enum;
+
+ /* Platform dependant VSS HPF configuration */
+ int vss_hpf_cfg;
+ const char **vss_hpf_texts;
+ struct soc_enum vss_hpf_enum;
+
+ /* Platform dependant enhanced EQ configuration */
+ int enh_eq_cfg;
+ const char **enh_eq_texts;
+ struct soc_enum enh_eq_enum;
+
+ struct wm8994_micdet micdet[2];
+
+ wm8958_micdet_cb jack_cb;
+ void *jack_cb_data;
+ int micdet_irq;
+
+ int revision;
+ struct wm8994_pdata *pdata;
+
+ unsigned int aif1clk_enable:1;
+ unsigned int aif2clk_enable:1;
+
+ unsigned int aif1clk_disable:1;
+ unsigned int aif2clk_disable:1;
+
+ int dsp_active;
+ const struct firmware *cur_fw;
+ const struct firmware *mbc;
+ const struct firmware *mbc_vss;
+ const struct firmware *enh_eq;
+};
+
#endif
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 67eaaecbb42e..5ad873fda814 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -305,11 +305,11 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source,
static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec;
int ret;
- w = snd_kcontrol_chip(kcontrol);
codec = w->codec;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
wm8995_update_class_w(codec);
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 47b357adabdd..646b58dda849 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -142,7 +142,7 @@ static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = {
* constantly enabled, we use the mutes on those inputs to simulate such
* controls.
*/
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9705_audio_map[] = {
/* HP mixer */
{"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"},
{"HP Mixer", "CD Playback Switch", "CD PGA"},
@@ -200,17 +200,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Right ADC", NULL, "ADC PGA"},
};
-static int wm9705_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9705_dapm_widgets,
- ARRAY_SIZE(wm9705_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
/* We use a register cache to enhance read performance. */
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
{
@@ -364,7 +353,6 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm9705_snd_ac97_controls,
ARRAY_SIZE(wm9705_snd_ac97_controls));
- wm9705_add_widgets(codec);
return 0;
@@ -390,6 +378,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9705_reg,
+ .dapm_widgets = wm9705_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
+ .dapm_routes = wm9705_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map),
};
static __devinit int wm9705_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index bf5d4ef1a2a6..90117f8156e8 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -332,7 +332,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9712_audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer", NULL, "Left DAC"},
{"AC97 Mixer", NULL, "Right DAC"},
@@ -429,17 +429,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"ROUT2", NULL, "Speaker PGA"},
};
-static int wm9712_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9712_dapm_widgets,
- ARRAY_SIZE(wm9712_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -651,7 +640,6 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
ARRAY_SIZE(wm9712_snd_ac97_controls));
- wm9712_add_widgets(codec);
return 0;
@@ -678,6 +666,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9712_reg,
+ .dapm_widgets = wm9712_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
+ .dapm_routes = wm9712_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
};
static __devinit int wm9712_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 38ed98558718..7167cb6787db 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -487,7 +487,7 @@ SND_SOC_DAPM_INPUT("MIC2B"),
SND_SOC_DAPM_VMID("VMID"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9713_audio_map[] = {
/* left HP mixer */
{"Left HP Mixer", "Beep Playback Switch", "PCBEEP"},
{"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -644,18 +644,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Capture Mono Mux", "Right", "Right Capture Source"},
};
-static int wm9713_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9713_dapm_widgets,
- ARRAY_SIZE(wm9713_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -1231,7 +1219,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls));
- wm9713_add_widgets(codec);
return 0;
@@ -1262,6 +1249,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9713_reg,
+ .dapm_widgets = wm9713_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
+ .dapm_routes = wm9713_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
};
static __devinit int wm9713_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 4005e9af5d61..e55b298c14a0 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -787,17 +787,17 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
{ "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
- { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+ { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
};
static const struct snd_soc_dapm_route lineout1_se_routes[] = {
- { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+ { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
@@ -806,17 +806,17 @@ static const struct snd_soc_dapm_route lineout1_se_routes[] = {
static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
{ "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
{ "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
- { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+ { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
};
static const struct snd_soc_dapm_route lineout2_se_routes[] = {
- { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+ { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
@@ -836,17 +836,21 @@ int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
WM8993_IN2_VU, WM8993_IN2_VU);
+ snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
+ WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
- WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
- WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index a5af834c8ef5..8566238db2a5 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -434,17 +434,21 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26));
+ mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
+ ACLKX | AHCLKX | AFSX);
break;
case SND_SOC_DAIFMT_CBM_CFS:
/* codec is clock master and frame slave */
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26));
+ mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG,
+ ACLKX | ACLKR);
+ mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
+ AFSX | AFSR);
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* codec is clock and frame master */
@@ -454,7 +458,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26));
+ mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG,
+ ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
break;
default:
@@ -644,7 +649,7 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
- if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+ if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32))
mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
FSXMOD(dev->tdm_slots), FSXMOD(0x1FF));
else
@@ -660,7 +665,7 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
AHCLKRE);
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
- if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+ if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32))
mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG,
FSRMOD(dev->tdm_slots), FSRMOD(0x1FF));
else
@@ -904,6 +909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
+ dma_data->sram_size = pdata->sram_size_playback;
dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
mem->start);
@@ -920,6 +926,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
+ dma_data->sram_size = pdata->sram_size_capture;
dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
mem->start);
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
index ac2ded969253..5b13feca7537 100644
--- a/sound/soc/imx/imx-ssi.c
+++ b/sound/soc/imx/imx-ssi.c
@@ -667,12 +667,6 @@ static int imx_ssi_probe(struct platform_device *pdev)
if (res)
ssi->dma_params_rx.dma = res->start;
- if ((cpu_is_mx27() || cpu_is_mx21()) &&
- !(ssi->flags & IMX_SSI_USE_AC97) &&
- (ssi->flags & IMX_SSI_DMA)) {
- ssi->flags |= IMX_SSI_DMA;
- }
-
platform_set_drvdata(pdev, ssi);
ret = snd_soc_register_dai(&pdev->dev, dai);
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 419bf4f5534a..cd22a54b2f14 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -133,7 +133,7 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
uint32_t conf;
- if (!dai->active)
+ if (dai->active)
return;
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
index 49723e3e7e38..c5fc339f68f1 100644
--- a/sound/soc/jz4740/qi_lb60.c
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -27,11 +27,7 @@
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *ctrl, int event)
{
- int on = 0;
- if (event & SND_SOC_DAPM_POST_PMU)
- on = 1;
- else if (event & SND_SOC_DAPM_PRE_PMD)
- on = 0;
+ int on = !SND_SOC_DAPM_EVENT_OFF(event);
gpio_set_value(QI_LB60_SND_GPIO, on);
gpio_set_value(QI_LB60_AMP_GPIO, on);
@@ -70,12 +66,6 @@ static int qi_lb60_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- snd_soc_dapm_new_controls(dapm, qi_lb60_widgets,
- ARRAY_SIZE(qi_lb60_widgets));
- snd_soc_dapm_add_routes(dapm, qi_lb60_routes,
- ARRAY_SIZE(qi_lb60_routes));
- snd_soc_dapm_sync(dapm);
-
return 0;
}
@@ -93,10 +83,20 @@ static struct snd_soc_card qi_lb60 = {
.name = "QI LB60",
.dai_link = &qi_lb60_dai,
.num_links = 1,
+
+ .dapm_widgets = qi_lb60_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
+ .dapm_routes = qi_lb60_routes,
+ .num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
};
static struct platform_device *qi_lb60_snd_device;
+static const struct gpio qi_lb60_gpios[] = {
+ { QI_LB60_SND_GPIO, GPIOF_OUT_INIT_LOW, "SND" },
+ { QI_LB60_AMP_GPIO, GPIOF_OUT_INIT_LOW, "AMP" },
+};
+
static int __init qi_lb60_init(void)
{
int ret;
@@ -106,23 +106,12 @@ static int __init qi_lb60_init(void)
if (!qi_lb60_snd_device)
return -ENOMEM;
- ret = gpio_request(QI_LB60_SND_GPIO, "SND");
+ ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
if (ret) {
- pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
- QI_LB60_SND_GPIO, ret);
+ pr_err("qi_lb60 snd: Failed to request gpios: %d\n", ret);
goto err_device_put;
}
- ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
- if (ret) {
- pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
- QI_LB60_AMP_GPIO, ret);
- goto err_gpio_free_snd;
- }
-
- gpio_direction_output(QI_LB60_SND_GPIO, 0);
- gpio_direction_output(QI_LB60_AMP_GPIO, 0);
-
platform_set_drvdata(qi_lb60_snd_device, &qi_lb60);
ret = platform_device_add(qi_lb60_snd_device);
@@ -135,10 +124,8 @@ static int __init qi_lb60_init(void)
err_unset_pdata:
platform_set_drvdata(qi_lb60_snd_device, NULL);
-/*err_gpio_free_amp:*/
- gpio_free(QI_LB60_AMP_GPIO);
-err_gpio_free_snd:
- gpio_free(QI_LB60_SND_GPIO);
+/*err_gpio_free_array:*/
+ gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
err_device_put:
platform_device_put(qi_lb60_snd_device);
@@ -148,9 +135,8 @@ module_init(qi_lb60_init);
static void __exit qi_lb60_exit(void)
{
- gpio_free(QI_LB60_AMP_GPIO);
- gpio_free(QI_LB60_SND_GPIO);
platform_device_unregister(qi_lb60_snd_device);
+ gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
}
module_exit(qi_lb60_exit);
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c
index d567c322a2fb..5a946b4115a2 100644
--- a/sound/soc/mid-x86/sst_platform.c
+++ b/sound/soc/mid-x86/sst_platform.c
@@ -249,10 +249,13 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
return -ENOMEM;
}
stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
+ stream->sstdrv_ops->module_name = SST_CARD_NAMES;
/* registering with SST driver to get access to SST APIs to use */
ret_val = register_sst_card(stream->sstdrv_ops);
if (ret_val) {
pr_err("sst: sst card registration failed\n");
+ kfree(stream->sstdrv_ops);
+ kfree(stream);
return ret_val;
}
runtime->private_data = stream;
@@ -270,6 +273,7 @@ static int sst_platform_close(struct snd_pcm_substream *substream)
str_id = stream->stream_info.str_id;
if (str_id)
ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
+ unregister_sst_card(stream->sstdrv_ops);
kfree(stream->sstdrv_ops);
kfree(stream);
return ret_val;
@@ -376,6 +380,11 @@ static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open,
.close = sst_platform_close,
@@ -384,6 +393,7 @@ static struct snd_pcm_ops sst_platform_ops = {
.trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer,
.hw_params = sst_platform_pcm_hw_params,
+ .hw_free = sst_platform_pcm_hw_free,
};
static void sst_pcm_free(struct snd_pcm *pcm)
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 2175f09e57b6..07b772357244 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -146,7 +146,7 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
* 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
* 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
*/
- if (cpu_is_omap343x() || cpu_is_omap44xx()) {
+ if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
/*
* Rule for the buffer size. We should not allow
* smaller buffer than the FIFO size to avoid underruns
@@ -258,7 +258,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- if (cpu_is_omap343x()) {
+ if (cpu_is_omap34xx()) {
dma_data->set_threshold = omap_mcbsp_set_threshold;
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (omap_mcbsp_get_dma_op_mode(bus_id) ==
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 37dc7211ed3f..9a7dedd6f5a9 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 8caeb8d305c3..e6a6b991d05f 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -37,7 +37,8 @@ static const struct snd_pcm_hardware omap_pcm_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32,
@@ -195,7 +196,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
if ((cpu_is_omap1510()))
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
- else
+ else if (!substream->runtime->no_period_wakeup)
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
if (!(cpu_class_is_omap1())) {
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h
index fea0515331fb..a0ed1dbb52d6 100644
--- a/sound/soc/omap/omap-pcm.h
+++ b/sound/soc/omap/omap-pcm.h
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index d0986220eff9..0aae998b6540 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 - 2009 Nokia Corporation
*
- * Contact: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
* Eduardo Valentin <eduardo.valentin@nokia.com>
* Jarkko Nikula <jhnikula@gmail.com>
*
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 580f48571303..33ebc46b45b5 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -155,6 +155,15 @@ config SND_SOC_RAUMFELD
help
Say Y if you want to add support for SoC audio on Raumfeld devices
+config SND_PXA2XX_SOC_HX4700
+ tristate "SoC Audio support for HP iPAQ hx4700"
+ depends on SND_PXA2XX_SOC && MACH_H4700
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_AK4641
+ help
+ Say Y if you want to add support for SoC audio on the
+ HP iPAQ hx4700.
+
config SND_PXA2XX_SOC_MAGICIAN
tristate "SoC Audio support for HTC Magician"
depends on SND_PXA2XX_SOC && MACH_MAGICIAN
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 07660165ec8d..af357623be9d 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -22,6 +22,7 @@ snd-soc-palm27x-objs := palm27x.o
snd-soc-saarb-objs := saarb.o
snd-soc-tavorevb3-objs := tavorevb3.o
snd-soc-zylonite-objs := zylonite.o
+snd-soc-hx4700-objs := hx4700.o
snd-soc-magician-objs := magician.o
snd-soc-mioa701-objs := mioa701_wm9713.o
snd-soc-z2-objs := z2.o
@@ -37,6 +38,7 @@ obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
+obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o
obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 9027da466cae..28757fb9df31 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -310,7 +310,7 @@ static struct snd_soc_dai_link corgi_dai = {
.cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.init = corgi_wm8731_init,
.ops = &corgi_ops,
};
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
new file mode 100644
index 000000000000..65c124831a00
--- /dev/null
+++ b/sound/soc/pxa/hx4700.c
@@ -0,0 +1,255 @@
+/*
+ * SoC audio for HP iPAQ hx4700
+ *
+ * Copyright (c) 2009 Philipp Zabel
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/hx4700.h>
+#include <asm/mach-types.h>
+#include "pxa2xx-i2s.h"
+
+#include "../codecs/ak4641.h"
+
+static struct snd_soc_jack hs_jack;
+
+/* Headphones jack detection DAPM pin */
+static struct snd_soc_jack_pin hs_jack_pin[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Speaker",
+ /* disable speaker when hp jack is inserted */
+ .mask = SND_JACK_HEADPHONE,
+ .invert = 1,
+ },
+};
+
+/* Headphones jack detection GPIO */
+static struct snd_soc_jack_gpio hs_jack_gpio = {
+ .gpio = GPIO75_HX4700_EARPHONE_nDET,
+ .invert = true,
+ .name = "hp-gpio",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+};
+
+/*
+ * iPAQ hx4700 uses I2S for capture and playback.
+ */
+static int hx4700_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as output */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ /* inform codec driver about clock freq *
+ * (PXA I2S always uses divider 256) */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops hx4700_ops = {
+ .hw_params = hx4700_hw_params,
+};
+
+static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+/* hx4700 machine dapm widgets */
+static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
+ SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
+ SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
+};
+
+/* hx4700 machine audio_map */
+static const struct snd_soc_dapm_route hx4700_audio_map[] = {
+
+ /* Headphone connected to LOUT, ROUT */
+ {"Headphone Jack", NULL, "LOUT"},
+ {"Headphone Jack", NULL, "ROUT"},
+
+ /* Speaker connected to MOUT2 */
+ {"Speaker", NULL, "MOUT2"},
+
+ /* Microphone connected to MICIN */
+ {"MICIN", NULL, "Built-in Microphone"},
+ {"AIN", NULL, "MICOUT"},
+};
+
+/*
+ * Logic for a ak4641 as connected on a HP iPAQ hx4700
+ */
+static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int err;
+
+ /* NC codec pins */
+ /* FIXME: is anything connected here? */
+ snd_soc_dapm_nc_pin(dapm, "MOUT1");
+ snd_soc_dapm_nc_pin(dapm, "MICEXT");
+ snd_soc_dapm_nc_pin(dapm, "AUX");
+
+ /* Jack detection API stuff */
+ err = snd_soc_jack_new(codec, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack);
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin),
+ hs_jack_pin);
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
+
+ return err;
+}
+
+/* hx4700 digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link hx4700_dai = {
+ .name = "ak4641",
+ .stream_name = "AK4641",
+ .cpu_dai_name = "pxa2xx-i2s",
+ .codec_dai_name = "ak4641-hifi",
+ .platform_name = "pxa-pcm-audio",
+ .codec_name = "ak4641.0-0012",
+ .init = hx4700_ak4641_init,
+ .ops = &hx4700_ops,
+};
+
+/* hx4700 audio machine driver */
+static struct snd_soc_card snd_soc_card_hx4700 = {
+ .name = "iPAQ hx4700",
+ .dai_link = &hx4700_dai,
+ .num_links = 1,
+ .dapm_widgets = hx4700_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets),
+ .dapm_routes = hx4700_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(hx4700_audio_map),
+};
+
+static struct gpio hx4700_audio_gpios[] = {
+ { GPIO107_HX4700_SPK_nSD, GPIOF_OUT_INIT_HIGH, "SPK_POWER" },
+ { GPIO92_HX4700_HP_DRIVER, GPIOF_OUT_INIT_LOW, "EP_POWER" },
+};
+
+static int __devinit hx4700_audio_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!machine_is_h4700())
+ return -ENODEV;
+
+ ret = gpio_request_array(hx4700_audio_gpios,
+ ARRAY_SIZE(hx4700_audio_gpios));
+ if (ret)
+ return ret;
+
+ snd_soc_card_hx4700.dev = &pdev->dev;
+ ret = snd_soc_register_card(&snd_soc_card_hx4700);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int __devexit hx4700_audio_remove(struct platform_device *pdev)
+{
+ snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
+ snd_soc_unregister_card(&snd_soc_card_hx4700);
+
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
+
+ gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios));
+ return 0;
+}
+
+static struct platform_driver hx4700_audio_driver = {
+ .driver = {
+ .name = "hx4700-audio",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = hx4700_audio_probe,
+ .remove = __devexit_p(hx4700_audio_remove),
+};
+
+static int __init hx4700_modinit(void)
+{
+ return platform_driver_register(&hx4700_audio_driver);
+}
+module_init(hx4700_modinit);
+
+static void __exit hx4700_modexit(void)
+{
+ platform_driver_unregister(&hx4700_audio_driver);
+}
+
+module_exit(hx4700_modexit);
+
+MODULE_AUTHOR("Philipp Zabel");
+MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hx4700-audio");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index a7d4999f9b24..da3ae4316cf2 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -276,7 +276,7 @@ static struct snd_soc_dai_link poodle_dai = {
.cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.init = poodle_wm8731_init,
.ops = &poodle_ops,
};
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 8e1571350630..b253d864868a 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -42,6 +42,7 @@
static int spitz_jack_func;
static int spitz_spk_func;
+static int spitz_mic_gpio;
static void spitz_ext_control(struct snd_soc_codec *codec)
{
@@ -217,14 +218,7 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol,
static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (machine_is_borzoi() || machine_is_spitz())
- gpio_set_value(SPITZ_GPIO_MIC_BIAS,
- SND_SOC_DAPM_EVENT_ON(event));
-
- if (machine_is_akita())
- gpio_set_value(AKITA_GPIO_MIC_BIAS,
- SND_SOC_DAPM_EVENT_ON(event));
-
+ gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -339,22 +333,45 @@ static int __init spitz_init(void)
if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
return -ENODEV;
+ if (machine_is_borzoi() || machine_is_spitz())
+ spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
+ else
+ spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
+
+ ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
+ if (ret)
+ goto err1;
+
+ ret = gpio_direction_output(spitz_mic_gpio, 0);
+ if (ret)
+ goto err2;
+
spitz_snd_device = platform_device_alloc("soc-audio", -1);
- if (!spitz_snd_device)
- return -ENOMEM;
+ if (!spitz_snd_device) {
+ ret = -ENOMEM;
+ goto err2;
+ }
platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
- ret = platform_device_add(spitz_snd_device);
+ ret = platform_device_add(spitz_snd_device);
if (ret)
- platform_device_put(spitz_snd_device);
+ goto err3;
+
+ return 0;
+err3:
+ platform_device_put(spitz_snd_device);
+err2:
+ gpio_free(spitz_mic_gpio);
+err1:
return ret;
}
static void __exit spitz_exit(void)
{
platform_device_unregister(spitz_snd_device);
+ gpio_free(spitz_mic_gpio);
}
module_init(spitz_init);
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index a3fdfb631469..459566bfcd35 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -162,3 +162,18 @@ config SND_SOC_SAMSUNG_SMDK_SPDIF
select SND_SAMSUNG_SPDIF
help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
+
+config SND_SOC_SMDK_WM8580_PCM
+ tristate "SoC PCM Audio support for WM8580 on SMDK"
+ depends on SND_SOC_SAMSUNG && (MACH_SMDK6450 || MACH_SMDKV210 || MACH_SMDKC110)
+ select SND_SOC_WM8580
+ select SND_SAMSUNG_PCM
+ help
+ Say Y if you want to add support for SoC audio on the SMDK.
+
+config SND_SOC_SPEYSIDE
+ tristate "Audio support for Wolfson Speyside"
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+ select SND_SAMSUNG_I2S
+ select SND_SOC_WM8915
+ select SND_SOC_WM9081
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 294dec05c26d..683843a2744f 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -34,6 +34,8 @@ snd-soc-smdk-wm9713-objs := smdk_wm9713.o
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
snd-soc-goni-wm8994-objs := goni_wm8994.o
snd-soc-smdk-spdif-objs := smdk_spdif.o
+snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
+snd-soc-speyside-objs := speyside.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -51,3 +53,5 @@ obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
+obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
+obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c
index f6b3a3ce5919..eb6d72ed55a7 100644
--- a/sound/soc/samsung/goni_wm8994.c
+++ b/sound/soc/samsung/goni_wm8994.c
@@ -236,18 +236,17 @@ static struct snd_soc_dai_link goni_dai[] = {
.name = "WM8994",
.stream_name = "WM8994 HiFi",
.cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8994-hifi",
+ .codec_dai_name = "wm8994-aif1",
.platform_name = "samsung-audio",
- .codec_name = "wm8994-codec.0-0x1a",
+ .codec_name = "wm8994-codec.0-001a",
.init = goni_wm8994_init,
.ops = &goni_hifi_ops,
}, {
.name = "WM8994 Voice",
.stream_name = "Voice",
.cpu_dai_name = "goni-voice-dai",
- .codec_dai_name = "wm8994-voice",
- .platform_name = "samsung-audio",
- .codec_name = "wm8994-codec.0-0x1a",
+ .codec_dai_name = "wm8994-aif2",
+ .codec_name = "wm8994-codec.0-001a",
.ops = &goni_voice_ops,
},
};
diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c
index 452230975632..16152ed08648 100644
--- a/sound/soc/samsung/neo1973_wm8753.c
+++ b/sound/soc/samsung/neo1973_wm8753.c
@@ -432,7 +432,6 @@ static struct snd_soc_dai_link neo1973_dai[] = {
{ /* Voice via BT */
.name = "Bluetooth",
.stream_name = "Voice",
- .platform_name = "samsung-audio",
.cpu_dai_name = "dfbmcs320-pcm",
.codec_dai_name = "wm8753-voice",
.codec_name = "wm8753-codec.0-001a",
diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c
new file mode 100644
index 000000000000..0d12092df164
--- /dev/null
+++ b/sound/soc/samsung/smdk_wm8580pcm.c
@@ -0,0 +1,206 @@
+/*
+ * sound/soc/samsung/smdk_wm8580pcm.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co. Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm8580.h"
+#include "dma.h"
+#include "pcm.h"
+
+/*
+ * Board Settings:
+ * o '1' means 'ON'
+ * o '0' means 'OFF'
+ * o 'X' means 'Don't care'
+ *
+ * SMDK6410, SMDK6440, SMDK6450 Base B/D: CFG1-0000, CFG2-1111
+ * SMDKC110, SMDKV210: CFGB11-100100, CFGB12-0000
+ */
+
+#define SMDK_WM8580_EXT_OSC 12000000
+#define SMDK_WM8580_EXT_MCLK 4096000
+#define SMDK_WM8580_EXT_VOICE 2048000
+
+static unsigned long mclk_freq;
+static unsigned long xtal_freq;
+
+/*
+ * If MCLK clock directly gets from XTAL, we don't have to use PLL
+ * to make MCLK, but if XTAL clock source connects with other codec
+ * pin (like XTI), we should have to set codec's PLL to make MCLK.
+ * Because Samsung SoC does not support pcmcdclk output like I2S.
+ */
+
+static int smdk_wm8580_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int rfs, ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ break;
+ default:
+ printk(KERN_ERR "%s:%d Sampling Rate %u not supported!\n",
+ __func__, __LINE__, params_rate(params));
+ return -EINVAL;
+ }
+
+ rfs = mclk_freq / params_rate(params) / 2;
+
+ /* Set the codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B
+ | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* Set the cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B
+ | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ if (mclk_freq == xtal_freq) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_MCLK);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_PLLA);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
+ xtal_freq, mclk_freq);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set PCM source clock on CPU */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Set SCLK_DIV for making bclk */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops smdk_wm8580_pcm_ops = {
+ .hw_params = smdk_wm8580_pcm_hw_params,
+};
+
+static struct snd_soc_dai_link smdk_dai[] = {
+ {
+ .name = "WM8580 PAIF PCM RX",
+ .stream_name = "Playback",
+ .cpu_dai_name = "samsung-pcm.0",
+ .codec_dai_name = "wm8580-hifi-playback",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8580-codec.0-001b",
+ .ops = &smdk_wm8580_pcm_ops,
+ }, {
+ .name = "WM8580 PAIF PCM TX",
+ .stream_name = "Capture",
+ .cpu_dai_name = "samsung-pcm.0",
+ .codec_dai_name = "wm8580-hifi-capture",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8580-codec.0-001b",
+ .ops = &smdk_wm8580_pcm_ops,
+ },
+};
+
+static struct snd_soc_card smdk_pcm = {
+ .name = "SMDK-PCM",
+ .dai_link = smdk_dai,
+ .num_links = 2,
+};
+
+/*
+ * After SMDKC110 Base Board's Rev is '0.1', 12MHz External OSC(X1)
+ * is absent (or not connected), so we connect EXT_VOICE_CLK(OSC4),
+ * 2.0484Mhz, directly with MCLK both Codec and SoC.
+ */
+static int __devinit snd_smdk_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ xtal_freq = SMDK_WM8580_EXT_OSC;
+ mclk_freq = SMDK_WM8580_EXT_MCLK;
+
+ if (machine_is_smdkc110() || machine_is_smdkv210())
+ xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE;
+
+ smdk_pcm.dev = &pdev->dev;
+ ret = snd_soc_register_card(&smdk_pcm);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit snd_smdk_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&smdk_pcm);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver snd_smdk_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "samsung-smdk-pcm",
+ },
+ .probe = snd_smdk_probe,
+ .remove = __devexit_p(snd_smdk_remove),
+};
+
+static int __init smdk_audio_init(void)
+{
+ return platform_driver_register(&snd_smdk_driver);
+}
+
+module_init(smdk_audio_init);
+
+static void __exit smdk_audio_exit(void)
+{
+ platform_driver_unregister(&snd_smdk_driver);
+}
+
+module_exit(smdk_audio_exit);
+
+MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("ALSA SoC SMDK WM8580 for PCM");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
new file mode 100644
index 000000000000..360a333cb7c0
--- /dev/null
+++ b/sound/soc/samsung/speyside.c
@@ -0,0 +1,332 @@
+/*
+ * Speyside audio support
+ *
+ * Copyright 2011 Wolfson Microelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+
+#include "../codecs/wm8915.h"
+#include "../codecs/wm9081.h"
+
+#define WM8915_HPSEL_GPIO 214
+
+static int speyside_set_bias_level(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
+ 32768, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
+ 0, 0, 0);
+ if (ret < 0) {
+ pr_err("Failed to stop FLL\n");
+ return ret;
+ }
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int speyside_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1,
+ 32768, 256 * 48000);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL,
+ 256 * 48000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops speyside_ops = {
+ .hw_params = speyside_hw_params,
+};
+
+static struct snd_soc_jack speyside_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin speyside_headset_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+/* Default the headphone selection to active high */
+static int speyside_jack_polarity;
+
+static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
+ return 1;
+ if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
+ return 1;
+
+ return 0;
+}
+
+static void speyside_set_polarity(struct snd_soc_codec *codec,
+ int polarity)
+{
+ speyside_jack_polarity = !polarity;
+ gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+
+ /* Re-run DAPM to make sure we're using the correct mic bias */
+ snd_soc_dapm_sync(&codec->dapm);
+}
+
+static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL");
+ if (ret != 0)
+ pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
+ gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+
+ ret = snd_soc_jack_new(codec, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &speyside_headset);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_pins(&speyside_headset,
+ ARRAY_SIZE(speyside_headset_pins),
+ speyside_headset_pins);
+ if (ret)
+ return ret;
+
+ wm8915_detect(codec, &speyside_headset, speyside_set_polarity);
+
+ return 0;
+}
+
+static int speyside_late_probe(struct snd_soc_card *card)
+{
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
+
+ return 0;
+}
+
+static struct snd_soc_dai_link speyside_dai[] = {
+ {
+ .name = "CPU",
+ .stream_name = "CPU",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_dai_name = "wm8915-aif1",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8915.1-001a",
+ .init = speyside_wm8915_init,
+ .ops = &speyside_ops,
+ },
+ {
+ .name = "Baseband",
+ .stream_name = "Baseband",
+ .cpu_dai_name = "wm8915-aif2",
+ .codec_dai_name = "wm1250-ev1",
+ .codec_name = "wm1250-ev1.1-0027",
+ .ops = &speyside_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
+{
+ snd_soc_dapm_nc_pin(dapm, "LINEOUT");
+
+ /* At any time the WM9081 is active it will have this clock */
+ return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK,
+ 48000 * 256, 0);
+}
+
+static struct snd_soc_aux_dev speyside_aux_dev[] = {
+ {
+ .name = "wm9081",
+ .codec_name = "wm9081.1-006c",
+ .init = speyside_wm9081_init,
+ },
+};
+
+static struct snd_soc_codec_conf speyside_codec_conf[] = {
+ {
+ .dev_name = "wm9081.1-006c",
+ .name_prefix = "Sub",
+ },
+};
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Main Speaker"),
+ SOC_DAPM_PIN_SWITCH("Main DMIC"),
+ SOC_DAPM_PIN_SWITCH("Main AMIC"),
+ SOC_DAPM_PIN_SWITCH("WM1250 Input"),
+ SOC_DAPM_PIN_SWITCH("WM1250 Output"),
+};
+
+static struct snd_soc_dapm_widget widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+
+ SND_SOC_DAPM_SPK("Main Speaker", NULL),
+
+ SND_SOC_DAPM_MIC("Main AMIC", NULL),
+ SND_SOC_DAPM_MIC("Main DMIC", NULL),
+};
+
+static struct snd_soc_dapm_route audio_paths[] = {
+ { "IN1RN", NULL, "MICB1" },
+ { "IN1RP", NULL, "MICB1" },
+ { "IN1RN", NULL, "MICB2" },
+ { "IN1RP", NULL, "MICB2" },
+ { "MICB1", NULL, "Headset Mic", speyside_get_micbias },
+ { "MICB2", NULL, "Headset Mic", speyside_get_micbias },
+
+ { "IN1LP", NULL, "MICB2" },
+ { "IN1RN", NULL, "MICB1" },
+ { "MICB2", NULL, "Main AMIC" },
+
+ { "DMIC1DAT", NULL, "MICB1" },
+ { "DMIC2DAT", NULL, "MICB1" },
+ { "MICB1", NULL, "Main DMIC" },
+
+ { "Headphone", NULL, "HPOUT1L" },
+ { "Headphone", NULL, "HPOUT1R" },
+
+ { "Sub IN1", NULL, "HPOUT2L" },
+ { "Sub IN2", NULL, "HPOUT2R" },
+
+ { "Main Speaker", NULL, "Sub SPKN" },
+ { "Main Speaker", NULL, "Sub SPKP" },
+ { "Main Speaker", NULL, "SPKDAT" },
+};
+
+static struct snd_soc_card speyside = {
+ .name = "Speyside",
+ .dai_link = speyside_dai,
+ .num_links = ARRAY_SIZE(speyside_dai),
+ .aux_dev = speyside_aux_dev,
+ .num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
+ .codec_conf = speyside_codec_conf,
+ .num_configs = ARRAY_SIZE(speyside_codec_conf),
+
+ .set_bias_level = speyside_set_bias_level,
+
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .dapm_widgets = widgets,
+ .num_dapm_widgets = ARRAY_SIZE(widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+
+ .late_probe = speyside_late_probe,
+};
+
+static __devinit int speyside_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &speyside;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit speyside_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver speyside_driver = {
+ .driver = {
+ .name = "speyside",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = speyside_probe,
+ .remove = __devexit_p(speyside_remove),
+};
+
+static int __init speyside_audio_init(void)
+{
+ return platform_driver_register(&speyside_driver);
+}
+module_init(speyside_audio_init);
+
+static void __exit speyside_audio_exit(void)
+{
+ platform_driver_unregister(&speyside_driver);
+}
+module_exit(speyside_audio_exit);
+
+MODULE_DESCRIPTION("Speyside audio support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:speyside");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 23c0e83d4c19..4a9da6b5f4e1 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -86,8 +86,8 @@
#define SE (1 << 0) /* Fix the master clock */
/* CLK_RST */
-#define B_CLK 0x00000010
-#define A_CLK 0x00000001
+#define CRB (1 << 4)
+#define CRA (1 << 0)
/* IO SHIFT / MACRO */
#define BI_SHIFT 12
@@ -146,11 +146,20 @@ struct fsi_priv {
void __iomem *base;
struct fsi_master *master;
- int chan_num;
struct fsi_stream playback;
struct fsi_stream capture;
+ int chan_num:16;
+ int clk_master:1;
+
long rate;
+
+ /* for suspend/resume */
+ u32 saved_do_fmt;
+ u32 saved_di_fmt;
+ u32 saved_ckg1;
+ u32 saved_ckg2;
+ u32 saved_out_sel;
};
struct fsi_core {
@@ -171,6 +180,14 @@ struct fsi_master {
struct fsi_core *core;
struct sh_fsi_platform_info *info;
spinlock_t lock;
+
+ /* for suspend/resume */
+ u32 saved_a_mclk;
+ u32 saved_b_mclk;
+ u32 saved_iemsk;
+ u32 saved_imsk;
+ u32 saved_clk_rst;
+ u32 saved_soft_rst;
};
/*
@@ -244,6 +261,11 @@ static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
return fsi->master;
}
+static int fsi_is_clk_master(struct fsi_priv *fsi)
+{
+ return fsi->clk_master;
+}
+
static int fsi_is_port_a(struct fsi_priv *fsi)
{
return fsi->master->base == fsi->base;
@@ -535,20 +557,45 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
}
/*
- * ctrl function
+ * clock function
*/
+#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1)
+#define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0)
+static void __fsi_module_clk_ctrl(struct fsi_master *master,
+ struct device *dev,
+ int enable)
+{
+ pm_runtime_get_sync(dev);
+
+ if (enable) {
+ /* enable only SR */
+ fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
+ fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
+ } else {
+ /* clear all registers */
+ fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
+ }
-static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
+ pm_runtime_put_sync(dev);
+}
+
+#define fsi_port_start(f) __fsi_port_clk_ctrl(f, 1)
+#define fsi_port_stop(f) __fsi_port_clk_ctrl(f, 0)
+static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
{
- u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
struct fsi_master *master = fsi_get_master(fsi);
+ u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR;
+ u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
+ int is_master = fsi_is_clk_master(fsi);
- if (enable)
- fsi_master_mask_set(master, CLK_RST, val, val);
- else
- fsi_master_mask_set(master, CLK_RST, val, 0);
+ fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0);
+ if (is_master)
+ fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
}
+/*
+ * ctrl function
+ */
static void fsi_fifo_init(struct fsi_priv *fsi,
int is_play,
struct snd_soc_dai *dai)
@@ -601,18 +648,6 @@ static void fsi_fifo_init(struct fsi_priv *fsi,
}
}
-static void fsi_soft_all_reset(struct fsi_master *master)
-{
- /* port AB reset */
- fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
- mdelay(10);
-
- /* soft reset */
- fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
- fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
- mdelay(10);
-}
-
static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
{
struct snd_pcm_runtime *runtime;
@@ -793,14 +828,13 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
struct fsi_priv *fsi = fsi_get_priv(substream);
int is_play = fsi_is_play(substream);
struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate;
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
fsi_irq_disable(fsi, is_play);
- fsi_clk_ctrl(fsi, 0);
- set_rate = fsi_get_info_set_rate(master);
- if (set_rate && fsi->rate)
+ if (fsi_is_clk_master(fsi))
set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
+
fsi->rate = 0;
pm_runtime_put_sync(dai->dev);
@@ -821,8 +855,10 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
frames_to_bytes(runtime, runtime->period_size));
ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
fsi_irq_enable(fsi, is_play);
+ fsi_port_start(fsi);
break;
case SNDRV_PCM_TRIGGER_STOP:
+ fsi_port_stop(fsi);
fsi_irq_disable(fsi, is_play);
fsi_stream_pop(fsi, is_play);
break;
@@ -876,6 +912,8 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
+ struct fsi_master *master = fsi_get_master(fsi);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
u32 flags = fsi_get_info_flags(fsi);
u32 data = 0;
int ret;
@@ -886,6 +924,7 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
data = DIMD | DOMD;
+ fsi->clk_master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
@@ -893,6 +932,13 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
ret = -EINVAL;
goto set_fmt_exit;
}
+
+ if (fsi_is_clk_master(fsi) && !set_rate) {
+ dev_err(dai->dev, "platform doesn't have set_rate\n");
+ ret = -EINVAL;
+ goto set_fmt_exit;
+ }
+
fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
/* set format */
@@ -919,13 +965,12 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
{
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate;
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
int fsi_ver = master->core->ver;
long rate = params_rate(params);
int ret;
- set_rate = fsi_get_info_set_rate(master);
- if (!set_rate)
+ if (!fsi_is_clk_master(fsi))
return 0;
ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1);
@@ -987,7 +1032,6 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
udelay(10);
- fsi_clk_ctrl(fsi, 1);
ret = 0;
}
@@ -1202,9 +1246,7 @@ static int fsi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master);
- pm_runtime_get_sync(&pdev->dev);
- fsi_soft_all_reset(master);
- pm_runtime_put_sync(&pdev->dev);
+ fsi_module_init(master, &pdev->dev);
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
id_entry->name, master);
@@ -1248,6 +1290,8 @@ static int fsi_remove(struct platform_device *pdev)
master = dev_get_drvdata(&pdev->dev);
+ fsi_module_kill(master, &pdev->dev);
+
free_irq(master->irq, master);
pm_runtime_disable(&pdev->dev);
@@ -1260,6 +1304,82 @@ static int fsi_remove(struct platform_device *pdev)
return 0;
}
+static void __fsi_suspend(struct fsi_priv *fsi,
+ struct device *dev,
+ set_rate_func set_rate)
+{
+ fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT);
+ fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT);
+ fsi->saved_ckg1 = fsi_reg_read(fsi, CKG1);
+ fsi->saved_ckg2 = fsi_reg_read(fsi, CKG2);
+ fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL);
+
+ if (fsi_is_clk_master(fsi))
+ set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0);
+}
+
+static void __fsi_resume(struct fsi_priv *fsi,
+ struct device *dev,
+ set_rate_func set_rate)
+{
+ fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt);
+ fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt);
+ fsi_reg_write(fsi, CKG1, fsi->saved_ckg1);
+ fsi_reg_write(fsi, CKG2, fsi->saved_ckg2);
+ fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel);
+
+ if (fsi_is_clk_master(fsi))
+ set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1);
+}
+
+static int fsi_suspend(struct device *dev)
+{
+ struct fsi_master *master = dev_get_drvdata(dev);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
+
+ pm_runtime_get_sync(dev);
+
+ __fsi_suspend(&master->fsia, dev, set_rate);
+ __fsi_suspend(&master->fsib, dev, set_rate);
+
+ master->saved_a_mclk = fsi_core_read(master, a_mclk);
+ master->saved_b_mclk = fsi_core_read(master, b_mclk);
+ master->saved_iemsk = fsi_core_read(master, iemsk);
+ master->saved_imsk = fsi_core_read(master, imsk);
+ master->saved_clk_rst = fsi_master_read(master, CLK_RST);
+ master->saved_soft_rst = fsi_master_read(master, SOFT_RST);
+
+ fsi_module_kill(master, dev);
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int fsi_resume(struct device *dev)
+{
+ struct fsi_master *master = dev_get_drvdata(dev);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
+
+ pm_runtime_get_sync(dev);
+
+ fsi_module_init(master, dev);
+
+ fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst);
+ fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst);
+ fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk);
+ fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk);
+ fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk);
+ fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk);
+
+ __fsi_resume(&master->fsia, dev, set_rate);
+ __fsi_resume(&master->fsib, dev, set_rate);
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
static int fsi_runtime_nop(struct device *dev)
{
/* Runtime PM callback shared between ->runtime_suspend()
@@ -1273,6 +1393,8 @@ static int fsi_runtime_nop(struct device *dev)
}
static struct dev_pm_ops fsi_pm_ops = {
+ .suspend = fsi_suspend,
+ .resume = fsi_resume,
.runtime_suspend = fsi_runtime_nop,
.runtime_resume = fsi_runtime_nop,
};
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 5d76da43b14c..06b7b81a1601 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -20,40 +20,28 @@
#include <trace/events/asoc.h>
-static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
- unsigned int reg)
+#ifdef CONFIG_SPI_MASTER
+static int do_spi_write(void *control, const char *data, int len)
{
+ struct spi_device *spi = control;
int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
- ret = snd_soc_cache_read(codec, reg, &val);
+ ret = spi_write(spi, data, len);
if (ret < 0)
- return -1;
- return val;
+ return ret;
+
+ return len;
}
+#endif
-static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value, const void *data, int len)
{
- u8 data[2];
int ret;
- data[0] = (reg << 4) | ((value >> 8) & 0x000f);
- data[1] = value & 0x00ff;
-
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -64,8 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret == 2)
+ ret = codec->hw_write(codec->control_data, data, len);
+ if (ret == len)
return 0;
if (ret < 0)
return ret;
@@ -73,50 +61,19 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_4_12_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[1];
- msg[1] = data[0];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-#else
-#define snd_soc_4_12_spi_write NULL
-#endif
-
-static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
- unsigned int reg)
+static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg)
{
int ret;
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
+ if (codec->cache_only)
+ return -1;
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
+ BUG_ON(!codec->hw_read);
+ return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
@@ -125,259 +82,117 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
return val;
}
-static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
+ unsigned int reg)
{
- u8 data[2];
- int ret;
-
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret == 2)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
+ return do_hw_read(codec, reg);
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_7_9_spi_write(void *control_data, const char *data,
- int len)
+static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
+ u16 data;
- if (len <= 0)
- return 0;
+ data = cpu_to_be16((reg << 12) | (value & 0xffffff));
- msg[0] = data[0];
- msg[1] = data[1];
+ return do_hw_write(codec, reg, value, &data, 2);
+}
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
+static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ return do_hw_read(codec, reg);
+}
- t.tx_buf = &msg[0];
- t.len = len;
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
- return len;
+ return do_hw_write(codec, reg, value, data, 2);
}
-#else
-#define snd_soc_7_9_spi_write NULL
-#endif
static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
- int ret;
reg &= 0xff;
data[0] = reg;
data[1] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
+ return do_hw_write(codec, reg, value, data, 2);
}
static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- reg &= 0xff;
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_8_8_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_read(codec, reg);
}
-#else
-#define snd_soc_8_8_spi_write NULL
-#endif
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
- int ret;
data[0] = reg;
data[1] = (value >> 8) & 0xff;
data[2] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- if (codec->hw_write(codec->control_data, data, 3) == 3)
- return 0;
- else
- return -EIO;
+ return do_hw_write(codec, reg, value, data, 3);
}
static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_8_16_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[3];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_read(codec, reg);
}
-#else
-#define snd_soc_8_16_spi_write NULL
-#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
- unsigned int r)
+static unsigned int do_i2c_read(struct snd_soc_codec *codec,
+ void *reg, int reglen,
+ void *data, int datalen)
{
struct i2c_msg xfer[2];
- u8 reg = r;
- u8 data;
int ret;
struct i2c_client *client = codec->control_data;
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
+ xfer[0].len = reglen;
+ xfer[0].buf = reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
- xfer[1].len = 1;
- xfer[1].buf = &data;
+ xfer[1].len = datalen;
+ xfer[1].buf = data;
ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ if (ret == 2)
return 0;
- }
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+#endif
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
+ unsigned int r)
+{
+ u8 reg = r;
+ u8 data;
+ int ret;
+
+ ret = do_i2c_read(codec, &reg, 1, &data, 1);
+ if (ret < 0)
+ return 0;
return data;
}
#else
@@ -388,30 +203,13 @@ static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u8 reg = r;
u16 data;
int ret;
- struct i2c_client *client = codec->control_data;
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, &reg, 1, &data, 2);
+ if (ret < 0)
return 0;
- }
-
return (data >> 8) | ((data & 0xff) << 8);
}
#else
@@ -422,30 +220,13 @@ static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u16 reg = r;
u8 data;
int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (u8 *)&reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 1;
- xfer[1].buf = &data;
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, &reg, 2, &data, 1);
+ if (ret < 0)
return 0;
- }
-
return data;
}
#else
@@ -453,120 +234,34 @@ static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
#endif
static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
- unsigned int reg)
+ unsigned int reg)
{
- int ret;
- unsigned int val;
-
- reg &= 0xff;
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
+ return do_hw_read(codec, reg);
}
static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+ unsigned int value)
{
u8 data[3];
- int ret;
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = value;
- reg &= 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- ret = codec->hw_write(codec->control_data, data, 3);
- if (ret == 3)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_16_8_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[3];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_write(codec, reg, value, data, 3);
}
-#else
-#define snd_soc_16_8_spi_write NULL
-#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u16 reg = cpu_to_be16(r);
u16 data;
int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (u8 *)&reg;
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, &reg, 2, &data, 2);
+ if (ret < 0)
return 0;
- }
-
return be16_to_cpu(data);
}
#else
@@ -576,52 +271,59 @@ static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
-
- return val;
+ return do_hw_read(codec, reg);
}
static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[4];
- int ret;
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = (value >> 8) & 0xff;
data[3] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
+ return do_hw_write(codec, reg, value, data, 4);
+}
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
+/* Primitive bulk write support for soc-cache. The data pointed to by
+ * `data' needs to already be in the form the hardware expects
+ * including any leading register specific data. Any data written
+ * through this function will not go through the cache as it only
+ * handles writing to volatile or out of bounds registers.
+ */
+static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
+ const void *data, size_t len)
+{
+ int ret;
+
+ /* To ensure that we don't get out of sync with the cache, check
+ * whether the base register is volatile or if we've directly asked
+ * to bypass the cache. Out of bounds registers are considered
+ * volatile.
+ */
+ if (!codec->cache_bypass
+ && !snd_soc_codec_volatile_register(codec, reg)
+ && reg < codec->driver->reg_cache_size)
+ return -EINVAL;
+
+ switch (codec->control_type) {
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+ case SND_SOC_I2C:
+ ret = i2c_master_send(codec->control_data, data, len);
+ break;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ case SND_SOC_SPI:
+ ret = spi_write(codec->control_data, data, len);
+ break;
+#endif
+ default:
+ BUG();
}
- ret = codec->hw_write(codec->control_data, data, 4);
- if (ret == 4)
+ if (ret == len)
return 0;
if (ret < 0)
return ret;
@@ -629,79 +331,40 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_16_16_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[4];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
- msg[3] = data[3];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-#else
-#define snd_soc_16_16_spi_write NULL
-#endif
-
static struct {
int addr_bits;
int data_bits;
int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
- int (*spi_write)(void *, const char *, int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
{
.addr_bits = 4, .data_bits = 12,
.write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
- .spi_write = snd_soc_4_12_spi_write,
},
{
.addr_bits = 7, .data_bits = 9,
.write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
- .spi_write = snd_soc_7_9_spi_write,
},
{
.addr_bits = 8, .data_bits = 8,
.write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
.i2c_read = snd_soc_8_8_read_i2c,
- .spi_write = snd_soc_8_8_spi_write,
},
{
.addr_bits = 8, .data_bits = 16,
.write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
.i2c_read = snd_soc_8_16_read_i2c,
- .spi_write = snd_soc_8_16_spi_write,
},
{
.addr_bits = 16, .data_bits = 8,
.write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
.i2c_read = snd_soc_16_8_read_i2c,
- .spi_write = snd_soc_16_8_spi_write,
},
{
.addr_bits = 16, .data_bits = 16,
.write = snd_soc_16_16_write, .read = snd_soc_16_16_read,
.i2c_read = snd_soc_16_16_read_i2c,
- .spi_write = snd_soc_16_16_spi_write,
},
};
@@ -709,7 +372,6 @@ static struct {
* snd_soc_codec_set_cache_io: Set up standard I/O functions.
*
* @codec: CODEC to configure.
- * @type: Type of cache.
* @addr_bits: Number of bits of register address data.
* @data_bits: Number of bits of data per register.
* @control: Control bus used.
@@ -744,6 +406,7 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
codec->write = io_types[i].write;
codec->read = io_types[i].read;
+ codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
switch (control) {
case SND_SOC_CUSTOM:
@@ -762,8 +425,9 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
break;
case SND_SOC_SPI:
- if (io_types[i].spi_write)
- codec->hw_write = io_types[i].spi_write;
+#ifdef CONFIG_SPI_MASTER
+ codec->hw_write = do_spi_write;
+#endif
codec->control_data = container_of(codec->dev,
struct spi_device,
@@ -889,6 +553,8 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
rbnode = rb_entry(node, struct snd_soc_rbtree_node, node);
if (rbnode->value == rbnode->defval)
continue;
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, rbnode->reg));
ret = snd_soc_cache_read(codec, rbnode->reg, &val);
if (ret)
return ret;
@@ -1149,6 +815,8 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
lzo_blocks = codec->reg_cache;
for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
@@ -1407,6 +1075,8 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
codec_drv = codec->driver;
for (i = 0; i < codec_drv->reg_cache_size; ++i) {
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
@@ -1523,7 +1193,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
codec->cache_ops->name, codec->name);
return codec->cache_ops->init(codec);
}
- return -EINVAL;
+ return -ENOSYS;
}
/*
@@ -1538,7 +1208,7 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
codec->cache_ops->name, codec->name);
return codec->cache_ops->exit(codec);
}
- return -EINVAL;
+ return -ENOSYS;
}
/**
@@ -1562,7 +1232,7 @@ int snd_soc_cache_read(struct snd_soc_codec *codec,
}
mutex_unlock(&codec->cache_rw_mutex);
- return -EINVAL;
+ return -ENOSYS;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_read);
@@ -1587,7 +1257,7 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
}
mutex_unlock(&codec->cache_rw_mutex);
- return -EINVAL;
+ return -ENOSYS;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_write);
@@ -1610,7 +1280,7 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec)
}
if (!codec->cache_ops || !codec->cache_ops->sync)
- return -EINVAL;
+ return -ENOSYS;
if (codec->cache_ops->name)
name = codec->cache_ops->name;
@@ -1677,3 +1347,17 @@ int snd_soc_default_readable_register(struct snd_soc_codec *codec,
return codec->driver->reg_access_default[index].read;
}
EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);
+
+int snd_soc_default_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ int index;
+
+ if (reg >= codec->driver->reg_cache_size)
+ return 1;
+ index = snd_soc_get_reg_access_index(codec, reg);
+ if (index < 0)
+ return 0;
+ return codec->driver->reg_access_default[index].write;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_writable_register);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index d8562ce4de7a..bb7cd5812945 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -242,7 +242,7 @@ static ssize_t codec_reg_write_file(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[32];
- int buf_size;
+ size_t buf_size;
char *start = buf;
unsigned long reg, value;
int step = 1;
@@ -302,13 +302,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
printk(KERN_WARNING
"ASoC: Failed to create codec register debugfs file\n");
- codec->dapm.debugfs_dapm = debugfs_create_dir("dapm",
- codec->debugfs_codec_root);
- if (!codec->dapm.debugfs_dapm)
- printk(KERN_WARNING
- "Failed to create DAPM debugfs directory\n");
-
- snd_soc_dapm_debugfs_init(&codec->dapm);
+ snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
}
static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
@@ -555,7 +549,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
}
}
- if (platform->driver->ops->open) {
+ if (platform->driver->ops && platform->driver->ops->open) {
ret = platform->driver->ops->open(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
@@ -685,7 +679,7 @@ machine_err:
codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
- if (platform->driver->ops->close)
+ if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
platform_err:
@@ -767,7 +761,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
- if (platform->driver->ops->close)
+ if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
cpu_dai->runtime = NULL;
@@ -810,7 +804,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
}
- if (platform->driver->ops->prepare) {
+ if (platform->driver->ops && platform->driver->ops->prepare) {
ret = platform->driver->ops->prepare(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: platform prepare error\n");
@@ -899,7 +893,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
}
- if (platform->driver->ops->hw_params) {
+ if (platform->driver->ops && platform->driver->ops->hw_params) {
ret = platform->driver->ops->hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: platform %s hw params failed\n",
@@ -952,7 +946,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
rtd->dai_link->ops->hw_free(substream);
/* free any DMA resources */
- if (platform->driver->ops->hw_free)
+ if (platform->driver->ops && platform->driver->ops->hw_free)
platform->driver->ops->hw_free(substream);
/* now free hw params for the DAIs */
@@ -980,7 +974,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
- if (platform->driver->ops->trigger) {
+ if (platform->driver->ops && platform->driver->ops->trigger) {
ret = platform->driver->ops->trigger(substream, cmd);
if (ret < 0)
return ret;
@@ -1009,7 +1003,7 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t offset = 0;
snd_pcm_sframes_t delay = 0;
- if (platform->driver->ops->pointer)
+ if (platform->driver->ops && platform->driver->ops->pointer)
offset = platform->driver->ops->pointer(substream);
if (cpu_dai->driver->ops->delay)
@@ -1299,6 +1293,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *codec_dai, *cpu_dai;
+ const char *platform_name;
if (rtd->complete)
return 1;
@@ -1351,13 +1346,18 @@ find_codec:
dai_link->codec_name);
find_platform:
- /* do we already have the CODEC DAI for this link ? */
- if (rtd->platform) {
+ /* do we need a platform? */
+ if (rtd->platform)
goto out;
- }
- /* no, then find CPU DAI from registered DAIs*/
+
+ /* if there's no platform we match on the empty platform */
+ platform_name = dai_link->platform_name;
+ if (!platform_name)
+ platform_name = "snd-soc-dummy";
+
+ /* no, then find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) {
- if (!strcmp(platform->name, dai_link->platform_name)) {
+ if (!strcmp(platform->name, platform_name)) {
rtd->platform = platform;
goto out;
}
@@ -1453,6 +1453,16 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
}
+static void soc_remove_dai_links(struct snd_soc_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->num_rtd; i++)
+ soc_remove_dai_link(card, i);
+
+ card->num_rtd = 0;
+}
+
static void soc_set_name_prefix(struct snd_soc_card *card,
struct snd_soc_codec *codec)
{
@@ -1483,6 +1493,12 @@ static int soc_probe_codec(struct snd_soc_card *card,
if (!try_module_get(codec->dev->driver->owner))
return -ENODEV;
+ soc_init_codec_debugfs(codec);
+
+ if (driver->dapm_widgets)
+ snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
+ driver->num_dapm_widgets);
+
if (driver->probe) {
ret = driver->probe(codec);
if (ret < 0) {
@@ -1493,15 +1509,13 @@ static int soc_probe_codec(struct snd_soc_card *card,
}
}
- if (driver->dapm_widgets)
- snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
- driver->num_dapm_widgets);
+ if (driver->controls)
+ snd_soc_add_controls(codec, driver->controls,
+ driver->num_controls);
if (driver->dapm_routes)
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
driver->num_dapm_routes);
- soc_init_codec_debugfs(codec);
-
/* mark codec as probed and add to card codec list */
codec->probed = 1;
list_add(&codec->card_list, &card->codec_dev_list);
@@ -1510,6 +1524,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
return 0;
err_probe:
+ soc_cleanup_codec_debugfs(codec);
module_put(codec->dev->driver->owner);
return ret;
@@ -1860,11 +1875,19 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);
+#ifdef CONFIG_DEBUG_FS
+ snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
+#endif
+
#ifdef CONFIG_PM_SLEEP
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
+ if (card->dapm_widgets)
+ snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
+ card->num_dapm_widgets);
+
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card);
@@ -1890,27 +1913,24 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
- if (card->dapm_widgets)
- snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
- card->num_dapm_widgets);
+ /* We should have a non-codec control add function but we don't */
+ if (card->controls)
+ snd_soc_add_controls(list_first_entry(&card->codec_dev_list,
+ struct snd_soc_codec,
+ card_list),
+ card->controls,
+ card->num_controls);
+
if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
-#ifdef CONFIG_DEBUG_FS
- card->dapm.debugfs_dapm = debugfs_create_dir("dapm",
- card->debugfs_card_root);
- if (!card->dapm.debugfs_dapm)
- printk(KERN_WARNING
- "Failed to create card DAPM debugfs directory\n");
-
- snd_soc_dapm_debugfs_init(&card->dapm);
-#endif
-
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
- "%s", card->name);
- snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
"%s", card->name);
+ snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
+ "%s", card->long_name ? card->long_name : card->name);
+ snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
+ "%s", card->driver_name ? card->driver_name : card->name);
if (card->late_probe) {
ret = card->late_probe(card);
@@ -1949,8 +1969,7 @@ probe_aux_dev_err:
soc_remove_aux_dev(card, i);
probe_dai_err:
- for (i = 0; i < card->num_links; i++)
- soc_remove_dai_link(card, i);
+ soc_remove_dai_links(card);
card_probe_error:
if (card->remove)
@@ -2012,8 +2031,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
soc_remove_aux_dev(card, i);
/* remove and free each DAI */
- for (i = 0; i < card->num_rtd; i++)
- soc_remove_dai_link(card, i);
+ soc_remove_dai_links(card);
soc_cleanup_card_debugfs(card);
@@ -2021,6 +2039,8 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
if (card->remove)
card->remove(card);
+ snd_soc_dapm_free(&card->dapm);
+
kfree(card->rtd);
snd_card_free(card->snd_card);
return 0;
@@ -2105,13 +2125,15 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->pcm = pcm;
pcm->private_data = rtd;
- soc_pcm_ops.mmap = platform->driver->ops->mmap;
- soc_pcm_ops.pointer = platform->driver->ops->pointer;
- soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
- soc_pcm_ops.copy = platform->driver->ops->copy;
- soc_pcm_ops.silence = platform->driver->ops->silence;
- soc_pcm_ops.ack = platform->driver->ops->ack;
- soc_pcm_ops.page = platform->driver->ops->page;
+ if (platform->driver->ops) {
+ soc_pcm_ops.mmap = platform->driver->ops->mmap;
+ soc_pcm_ops.pointer = platform->driver->ops->pointer;
+ soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
+ soc_pcm_ops.copy = platform->driver->ops->copy;
+ soc_pcm_ops.silence = platform->driver->ops->silence;
+ soc_pcm_ops.ack = platform->driver->ops->ack;
+ soc_pcm_ops.page = platform->driver->ops->page;
+ }
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
@@ -2119,10 +2141,13 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
- ret = platform->driver->pcm_new(rtd->card->snd_card, codec_dai, pcm);
- if (ret < 0) {
- printk(KERN_ERR "asoc: platform pcm constructor failed\n");
- return ret;
+ if (platform->driver->pcm_new) {
+ ret = platform->driver->pcm_new(rtd->card->snd_card,
+ codec_dai, pcm);
+ if (ret < 0) {
+ pr_err("asoc: platform pcm constructor failed\n");
+ return ret;
+ }
}
pcm->private_free = platform->driver->pcm_free;
@@ -2150,6 +2175,42 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
/**
+ * snd_soc_codec_readable_register: Report if a register is readable.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indicating if a CODEC register is readable.
+ */
+int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ if (codec->readable_register)
+ return codec->readable_register(codec, reg);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register);
+
+/**
+ * snd_soc_codec_writable_register: Report if a register is writable.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indicating if a CODEC register is writable.
+ */
+int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ if (codec->writable_register)
+ return codec->writable_register(codec, reg);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register);
+
+/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @codec: audio codec
* @ops: AC97 bus operations
@@ -2231,6 +2292,13 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_write);
+unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec,
+ unsigned int reg, const void *data, size_t len)
+{
+ return codec->bulk_write_raw(codec, reg, data, len);
+}
+EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw);
+
/**
* snd_soc_update_bits - update codec register bits
* @codec: audio codec
@@ -3291,6 +3359,8 @@ int snd_soc_register_card(struct snd_soc_card *card)
if (!card->name || !card->dev)
return -EINVAL;
+ dev_set_drvdata(card->dev, card);
+
snd_soc_initialize_card_lists(card);
soc_init_card_debugfs(card);
@@ -3412,7 +3482,7 @@ int snd_soc_register_dai(struct device *dev,
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
if (dai == NULL)
- return -ENOMEM;
+ return -ENOMEM;
/* create DAI component name */
dai->name = fmt_single_name(dev, &dai->id);
@@ -3551,7 +3621,7 @@ int snd_soc_register_platform(struct device *dev,
platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
if (platform == NULL)
- return -ENOMEM;
+ return -ENOMEM;
/* create platform component name */
platform->name = fmt_single_name(dev, &platform->id);
@@ -3669,6 +3739,7 @@ int snd_soc_register_codec(struct device *dev,
codec->read = codec_drv->read;
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
+ codec->writable_register = codec_drv->writable_register;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;
@@ -3703,6 +3774,8 @@ int snd_soc_register_codec(struct device *dev,
codec->volatile_register = snd_soc_default_volatile_register;
if (!codec->readable_register)
codec->readable_register = snd_soc_default_readable_register;
+ if (!codec->writable_register)
+ codec->writable_register = snd_soc_default_writable_register;
}
for (i = 0; i < num_dai; i++) {
@@ -3791,12 +3864,16 @@ static int __init snd_soc_init(void)
pr_warn("ASoC: Failed to create platform list debugfs file\n");
#endif
+ snd_soc_util_init();
+
return platform_driver_register(&soc_driver);
}
module_init(snd_soc_init);
static void __exit snd_soc_exit(void)
{
+ snd_soc_util_exit();
+
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(snd_soc_debugfs_root);
#endif
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 81c4052c127c..456617e63789 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -187,7 +187,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_mixer_named_ctl: {
int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *)
- w->kcontrols[i].private_value;
+ w->kcontrol_news[i].private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
int max = mc->max;
@@ -204,7 +204,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
}
break;
case snd_soc_dapm_mux: {
- struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+ struct soc_enum *e = (struct soc_enum *)
+ w->kcontrol_news[i].private_value;
int val, item, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
@@ -220,7 +221,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
}
break;
case snd_soc_dapm_virt_mux: {
- struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+ struct soc_enum *e = (struct soc_enum *)
+ w->kcontrol_news[i].private_value;
p->connect = 0;
/* since a virtual mux has no backing registers to
@@ -235,7 +237,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
break;
case snd_soc_dapm_value_mux: {
struct soc_enum *e = (struct soc_enum *)
- w->kcontrols[i].private_value;
+ w->kcontrol_news[i].private_value;
int val, item;
val = snd_soc_read(w->codec, e->reg);
@@ -310,11 +312,11 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
/* search for mixer kcontrol */
for (i = 0; i < dest->num_kcontrols; i++) {
- if (!strcmp(control_name, dest->kcontrols[i].name)) {
+ if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &dest->sources);
list_add(&path->list_source, &src->sinks);
- path->name = dest->kcontrols[i].name;
+ path->name = dest->kcontrol_news[i].name;
dapm_set_path_status(dest, path, i);
return 0;
}
@@ -322,43 +324,26 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
return -ENODEV;
}
-/* update dapm codec register bits */
-static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
+static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
+ const struct snd_kcontrol_new *kcontrol_new,
+ struct snd_kcontrol **kcontrol)
{
- int change, power;
- unsigned int old, new;
- struct snd_soc_codec *codec = widget->codec;
- struct snd_soc_dapm_context *dapm = widget->dapm;
- struct snd_soc_card *card = dapm->card;
-
- /* check for valid widgets */
- if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
- widget->id == snd_soc_dapm_output ||
- widget->id == snd_soc_dapm_hp ||
- widget->id == snd_soc_dapm_mic ||
- widget->id == snd_soc_dapm_line ||
- widget->id == snd_soc_dapm_spk)
- return 0;
-
- power = widget->power;
- if (widget->invert)
- power = (power ? 0:1);
+ struct snd_soc_dapm_widget *w;
+ int i;
- old = snd_soc_read(codec, widget->reg);
- new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
+ *kcontrol = NULL;
- change = old != new;
- if (change) {
- pop_dbg(dapm->dev, card->pop_time,
- "pop test %s : %s in %d ms\n",
- widget->name, widget->power ? "on" : "off",
- card->pop_time);
- pop_wait(card->pop_time);
- snd_soc_write(codec, widget->reg, new);
+ list_for_each_entry(w, &dapm->card->widgets, list) {
+ for (i = 0; i < w->num_kcontrols; i++) {
+ if (&w->kcontrol_news[i] == kcontrol_new) {
+ if (w->kcontrols)
+ *kcontrol = w->kcontrols[i];
+ return 1;
+ }
+ }
}
- dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg,
- old, new, change);
- return change;
+
+ return 0;
}
/* create new dapm mixer control */
@@ -370,6 +355,8 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path;
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
+ struct snd_soc_dapm_widget_list *wlist;
+ size_t wlistsize;
if (dapm->codec)
prefix = dapm->codec->name_prefix;
@@ -388,23 +375,37 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
list_for_each_entry(path, &w->sources, list_sink) {
/* mixer/mux paths name must match control name */
- if (path->name != (char*)w->kcontrols[i].name)
+ if (path->name != (char *)w->kcontrol_news[i].name)
continue;
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ sizeof(struct snd_soc_dapm_widget *),
+ wlist = kzalloc(wlistsize, GFP_KERNEL);
+ if (wlist == NULL) {
+ dev_err(dapm->dev,
+ "asoc: can't allocate widget list for %s\n",
+ w->name);
+ return -ENOMEM;
+ }
+ wlist->num_widgets = 1;
+ wlist->widgets[0] = w;
+
/* add dapm control with long name.
* for dapm_mixer this is the concatenation of the
* mixer and kcontrol name.
* for dapm_mixer_named_ctl this is simply the
* kcontrol name.
*/
- name_len = strlen(w->kcontrols[i].name) + 1;
+ name_len = strlen(w->kcontrol_news[i].name) + 1;
if (w->id != snd_soc_dapm_mixer_named_ctl)
name_len += 1 + strlen(w->name);
path->long_name = kmalloc(name_len, GFP_KERNEL);
- if (path->long_name == NULL)
+ if (path->long_name == NULL) {
+ kfree(wlist);
return -ENOMEM;
+ }
switch (w->id) {
default:
@@ -416,27 +417,30 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
*/
snprintf(path->long_name, name_len, "%s %s",
w->name + prefix_len,
- w->kcontrols[i].name);
+ w->kcontrol_news[i].name);
break;
case snd_soc_dapm_mixer_named_ctl:
snprintf(path->long_name, name_len, "%s",
- w->kcontrols[i].name);
+ w->kcontrol_news[i].name);
break;
}
path->long_name[name_len - 1] = '\0';
- path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
- path->long_name, prefix);
+ path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
+ wlist, path->long_name,
+ prefix);
ret = snd_ctl_add(card, path->kcontrol);
if (ret < 0) {
dev_err(dapm->dev,
"asoc: failed to add dapm kcontrol %s: %d\n",
path->long_name, ret);
+ kfree(wlist);
kfree(path->long_name);
path->long_name = NULL;
return ret;
}
+ w->kcontrols[i] = path->kcontrol;
}
}
return ret;
@@ -451,42 +455,80 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
size_t prefix_len;
- int ret = 0;
-
- if (!w->num_kcontrols) {
- dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name);
+ int ret;
+ struct snd_soc_dapm_widget_list *wlist;
+ int shared, wlistentries;
+ size_t wlistsize;
+ char *name;
+
+ if (w->num_kcontrols != 1) {
+ dev_err(dapm->dev,
+ "asoc: mux %s has incorrect number of controls\n",
+ w->name);
return -EINVAL;
}
- if (dapm->codec)
- prefix = dapm->codec->name_prefix;
- else
- prefix = NULL;
+ shared = dapm_is_shared_kcontrol(dapm, &w->kcontrol_news[0],
+ &kcontrol);
+ if (kcontrol) {
+ wlist = kcontrol->private_data;
+ wlistentries = wlist->num_widgets + 1;
+ } else {
+ wlist = NULL;
+ wlistentries = 1;
+ }
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ wlistentries * sizeof(struct snd_soc_dapm_widget *),
+ wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
+ if (wlist == NULL) {
+ dev_err(dapm->dev,
+ "asoc: can't allocate widget list for %s\n", w->name);
+ return -ENOMEM;
+ }
+ wlist->num_widgets = wlistentries;
+ wlist->widgets[wlistentries - 1] = w;
- if (prefix)
- prefix_len = strlen(prefix) + 1;
- else
- prefix_len = 0;
+ if (!kcontrol) {
+ if (dapm->codec)
+ prefix = dapm->codec->name_prefix;
+ else
+ prefix = NULL;
+
+ if (shared) {
+ name = w->kcontrol_news[0].name;
+ prefix_len = 0;
+ } else {
+ name = w->name;
+ if (prefix)
+ prefix_len = strlen(prefix) + 1;
+ else
+ prefix_len = 0;
+ }
- /* The control will get a prefix from the control creation
- * process but we're also using the same prefix for widgets so
- * cut the prefix off the front of the widget name.
- */
- kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len,
- prefix);
- ret = snd_ctl_add(card, kcontrol);
+ /*
+ * The control will get a prefix from the control creation
+ * process but we're also using the same prefix for widgets so
+ * cut the prefix off the front of the widget name.
+ */
+ kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,
+ name + prefix_len, prefix);
+ ret = snd_ctl_add(card, kcontrol);
+ if (ret < 0) {
+ dev_err(dapm->dev,
+ "asoc: failed to add kcontrol %s\n", w->name);
+ kfree(wlist);
+ return ret;
+ }
+ }
- if (ret < 0)
- goto err;
+ kcontrol->private_data = wlist;
+
+ w->kcontrols[0] = kcontrol;
list_for_each_entry(path, &w->sources, list_sink)
path->kcontrol = kcontrol;
- return ret;
-
-err:
- dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name);
- return ret;
+ return 0;
}
/* create new dapm volume control */
@@ -644,57 +686,6 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(dapm_reg_event);
-/* Standard power change method, used to apply power changes to most
- * widgets.
- */
-static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
-{
- int ret;
-
- /* call any power change event handlers */
- if (w->event)
- dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n",
- w->power ? "on" : "off",
- w->name, w->event_flags);
-
- /* power up pre event */
- if (w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down pre event */
- if (!w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
- if (ret < 0)
- return ret;
- }
-
- dapm_update_bits(w);
-
- /* power up post event */
- if (w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down post event */
- if (!w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
/* Generic check to see if a widget should be powered.
*/
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
@@ -981,16 +972,6 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
NULL, SND_SOC_DAPM_POST_PMD);
break;
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
- case snd_soc_dapm_line:
- case snd_soc_dapm_spk:
- /* No register support currently */
- ret = dapm_generic_apply_power(w);
- break;
-
default:
/* Queue it up for application */
cur_sort = sort[w->id];
@@ -1201,6 +1182,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
}
}
+ /* Force all contexts in the card to the same bias state */
+ power = 0;
+ list_for_each_entry(d, &card->dapm_list, list)
+ if (d->dev_power)
+ power = 1;
+ list_for_each_entry(d, &card->dapm_list, list)
+ d->dev_power = power;
+
+
/* Run all the bias changes in parallel */
list_for_each_entry(d, &dapm->card->dapm_list, list)
async_schedule_domain(dapm_pre_sequence_async, d,
@@ -1304,31 +1294,104 @@ static const struct file_operations dapm_widget_power_fops = {
.llseek = default_llseek,
};
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+static int dapm_bias_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_dapm_context *dapm = file->private_data;
+ char *level;
+
+ switch (dapm->bias_level) {
+ case SND_SOC_BIAS_ON:
+ level = "On\n";
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ level = "Prepare\n";
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ level = "Standby\n";
+ break;
+ case SND_SOC_BIAS_OFF:
+ level = "Off\n";
+ break;
+ default:
+ BUG();
+ level = "Unknown\n";
+ break;
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, level,
+ strlen(level));
+}
+
+static const struct file_operations dapm_bias_fops = {
+ .open = dapm_bias_open_file,
+ .read = dapm_bias_read_file,
+ .llseek = default_llseek,
+};
+
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+ struct dentry *parent)
{
- struct snd_soc_dapm_widget *w;
struct dentry *d;
- if (!dapm->debugfs_dapm)
+ dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
+
+ if (!dapm->debugfs_dapm) {
+ printk(KERN_WARNING
+ "Failed to create DAPM debugfs directory\n");
return;
+ }
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (!w->name || w->dapm != dapm)
- continue;
+ d = debugfs_create_file("bias_level", 0444,
+ dapm->debugfs_dapm, dapm,
+ &dapm_bias_fops);
+ if (!d)
+ dev_warn(dapm->dev,
+ "ASoC: Failed to create bias level debugfs file\n");
+}
- d = debugfs_create_file(w->name, 0444,
- dapm->debugfs_dapm, w,
- &dapm_widget_power_fops);
- if (!d)
- dev_warn(w->dapm->dev,
- "ASoC: Failed to create %s debugfs file\n",
- w->name);
- }
+static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct dentry *d;
+
+ if (!dapm->debugfs_dapm || !w->name)
+ return;
+
+ d = debugfs_create_file(w->name, 0444,
+ dapm->debugfs_dapm, w,
+ &dapm_widget_power_fops);
+ if (!d)
+ dev_warn(w->dapm->dev,
+ "ASoC: Failed to create %s debugfs file\n",
+ w->name);
+}
+
+static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
+{
+ debugfs_remove_recursive(dapm->debugfs_dapm);
}
+
#else
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+ struct dentry *parent)
+{
+}
+
+static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+}
+
+static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
}
+
#endif
/* test and update the power status of a mux widget */
@@ -1496,32 +1559,49 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
kfree(p->long_name);
kfree(p);
}
+ kfree(w->kcontrols);
kfree(w->name);
kfree(w);
}
}
-static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
- const char *pin, int status)
+static struct snd_soc_dapm_widget *dapm_find_widget(
+ struct snd_soc_dapm_context *dapm, const char *pin,
+ bool search_other_contexts)
{
struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *fallback = NULL;
list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
if (!strcmp(w->name, pin)) {
- dev_dbg(w->dapm->dev, "dapm: pin %s = %d\n",
- pin, status);
- w->connected = status;
- /* Allow disabling of forced pins */
- if (status == 0)
- w->force = 0;
- return 0;
+ if (w->dapm == dapm)
+ return w;
+ else
+ fallback = w;
}
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ if (search_other_contexts)
+ return fallback;
+
+ return NULL;
+}
+
+static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
+ const char *pin, int status)
+{
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
+
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
+ }
+
+ w->connected = status;
+ if (status == 0)
+ w->force = 0;
+
+ return 0;
}
/**
@@ -1627,7 +1707,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
}
/* connect dynamic paths */
- switch(wsink->id) {
+ switch (wsink->id) {
case snd_soc_dapm_adc:
case snd_soc_dapm_dac:
case snd_soc_dapm_pga:
@@ -1650,7 +1730,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
- &wsink->kcontrols[0]);
+ &wsink->kcontrol_news[0]);
if (ret != 0)
goto err;
break;
@@ -1730,6 +1810,14 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
if (w->new)
continue;
+ if (w->num_kcontrols) {
+ w->kcontrols = kzalloc(w->num_kcontrols *
+ sizeof(struct snd_kcontrol *),
+ GFP_KERNEL);
+ if (!w->kcontrols)
+ return -ENOMEM;
+ }
+
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
@@ -1785,6 +1873,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
}
w->new = 1;
+
+ dapm_debugfs_add_widget(w);
}
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
@@ -1804,7 +1894,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -1843,7 +1934,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -1854,6 +1947,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
+ int wi;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1862,31 +1956,36 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
mask = mask << shift;
val = val << shift;
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ if (val)
+ /* new connection */
+ connect = invert ? 0 : 1;
+ else
+ /* old connection must be powered down */
+ connect = invert ? 1 : 0;
+
+ mutex_lock(&codec->mutex);
change = snd_soc_test_bits(widget->codec, reg, mask, val);
if (change) {
- if (val)
- /* new connection */
- connect = invert ? 0:1;
- else
- /* old connection must be powered down */
- connect = invert ? 1:0;
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mixer_update_power(widget, kcontrol, connect);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mixer_update_power(widget, kcontrol, connect);
+
+ widget->dapm->update = NULL;
+ }
}
- mutex_unlock(&widget->codec->mutex);
+ mutex_unlock(&codec->mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@@ -1903,7 +2002,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, bitmask;
@@ -1931,11 +2031,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask, bitmask;
struct snd_soc_dapm_update update;
+ int wi;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
@@ -1951,22 +2054,29 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mask |= (bitmask - 1) << e->shift_r;
}
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ mutex_lock(&codec->mutex);
+
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
- mutex_unlock(&widget->codec->mutex);
+ widget->dapm->update = NULL;
+ }
+ }
+
+ mutex_unlock(&codec->mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@@ -1981,7 +2091,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
ucontrol->value.enumerated.item[0] = widget->value;
@@ -1999,22 +2110,33 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
int ret = 0;
+ int wi;
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
- mutex_lock(&widget->codec->mutex);
+ mutex_lock(&codec->mutex);
change = widget->value != ucontrol->value.enumerated.item[0];
- widget->value = ucontrol->value.enumerated.item[0];
- dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- mutex_unlock(&widget->codec->mutex);
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ dapm_mux_update_power(widget, kcontrol, change,
+ widget->value, e);
+ }
+ }
+
+ mutex_unlock(&codec->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@@ -2035,7 +2157,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_val, val, mux;
@@ -2075,11 +2198,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
+ int wi;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -2093,22 +2219,29 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mask |= e->mask << e->shift_r;
}
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ mutex_lock(&codec->mutex);
+
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+ widget->dapm->update = NULL;
+ }
+ }
- mutex_unlock(&widget->codec->mutex);
+ mutex_unlock(&codec->mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
@@ -2346,22 +2479,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin)) {
- dev_dbg(w->dapm->dev,
- "dapm: force enable pin %s\n", pin);
- w->connected = 1;
- w->force = 1;
- return 0;
- }
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin);
+ w->connected = 1;
+ w->force = 1;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
@@ -2413,14 +2542,10 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin))
- return w->connected;
- }
+ if (w)
+ return w->connected;
return 0;
}
@@ -2440,19 +2565,16 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin)) {
- w->ignore_suspend = 1;
- return 0;
- }
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ w->ignore_suspend = 1;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
@@ -2465,6 +2587,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
{
snd_soc_dapm_sys_remove(dapm->dev);
+ dapm_debugfs_cleanup(dapm);
dapm_free_widgets(dapm);
list_del(&dapm->list);
}
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index fc017c0a7b5d..7c17b98d5846 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -325,7 +325,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
gpio_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
- jack->codec->dev->driver->name,
+ gpios[i].name,
&gpios[i]);
if (ret)
goto err;
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 3f45e6a439bf..ec921ec99c26 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -13,6 +13,7 @@
* option) any later version.
*/
+#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -55,3 +56,55 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
+
+static struct snd_soc_platform_driver dummy_platform;
+
+static __devinit int snd_soc_dummy_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &dummy_platform);
+}
+
+static __devexit int snd_soc_dummy_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver soc_dummy_driver = {
+ .driver = {
+ .name = "snd-soc-dummy",
+ .owner = THIS_MODULE,
+ },
+ .probe = snd_soc_dummy_probe,
+ .remove = __devexit_p(snd_soc_dummy_remove),
+};
+
+static struct platform_device *soc_dummy_dev;
+
+int __init snd_soc_util_init(void)
+{
+ int ret;
+
+ soc_dummy_dev = platform_device_alloc("snd-soc-dummy", -1);
+ if (!soc_dummy_dev)
+ return -ENOMEM;
+
+ ret = platform_device_add(soc_dummy_dev);
+ if (ret != 0) {
+ platform_device_put(soc_dummy_dev);
+ return ret;
+ }
+
+ ret = platform_driver_register(&soc_dummy_driver);
+ if (ret != 0)
+ platform_device_unregister(soc_dummy_dev);
+
+ return ret;
+}
+
+void __exit snd_soc_util_exit(void)
+{
+ platform_device_unregister(soc_dummy_dev);
+ platform_driver_unregister(&soc_dummy_driver);
+}
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 66b504f06c23..035d39a4beb4 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -1,26 +1,40 @@
-config SND_TEGRA_SOC
+config SND_SOC_TEGRA
tristate "SoC Audio for the Tegra System-on-Chip"
depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
- default m
help
Say Y or M here if you want support for SoC audio on Tegra.
-config SND_TEGRA_SOC_I2S
+config SND_SOC_TEGRA_I2S
tristate
- depends on SND_TEGRA_SOC
- default m
+ depends on SND_SOC_TEGRA
help
Say Y or M if you want to add support for codecs attached to the
Tegra I2S interface. You will also need to select the individual
machine drivers to support below.
-config SND_TEGRA_SOC_HARMONY
- tristate "SoC Audio support for Tegra Harmony reference board"
- depends on SND_TEGRA_SOC && MACH_HARMONY && I2C
- default m
- select SND_TEGRA_SOC_I2S
+config MACH_HAS_SND_SOC_TEGRA_WM8903
+ bool
+ help
+ Machines that use the SND_SOC_TEGRA_WM8903 driver should select
+ this config option, in order to allow the user to enable
+ SND_SOC_TEGRA_WM8903.
+
+config SND_SOC_TEGRA_WM8903
+ tristate "SoC Audio support for Tegra boards using a WM8903 codec"
+ depends on SND_SOC_TEGRA && I2C
+ depends on MACH_HAS_SND_SOC_TEGRA_WM8903
+ select SND_SOC_TEGRA_I2S
select SND_SOC_WM8903
help
- Say Y or M here if you want to add support for SoC audio on the
- Tegra Harmony reference board.
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the WM8093 codec. Currently, the supported boards are
+ Harmony, Ventana, Seaboard, Kaen, and Aebl.
+config SND_SOC_TEGRA_TRIMSLICE
+ tristate "SoC Audio support for TrimSlice board"
+ depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C
+ select SND_SOC_TEGRA_I2S
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y or M here if you want to add support for SoC audio on the
+ TrimSlice platform.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index fd183d3ab4f1..fa6574d92a31 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -4,12 +4,14 @@ snd-soc-tegra-pcm-objs := tegra_pcm.o
snd-soc-tegra-i2s-objs := tegra_i2s.o
snd-soc-tegra-utils-objs += tegra_asoc_utils.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-utils.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o
-obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
+obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
# Tegra machine Support
-snd-soc-tegra-harmony-objs := harmony.o
+snd-soc-tegra-wm8903-objs := tegra_wm8903.o
+snd-soc-tegra-trimslice-objs := trimslice.o
-obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o
+obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
+obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c
deleted file mode 100644
index 556a57133925..000000000000
--- a/sound/soc/tegra/harmony.c
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * harmony.c - Harmony machine ASoC driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010-2011 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- *
- * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
- *
- * Copyright 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory
- * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <asm/mach-types.h>
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/gpio.h>
-
-#include <mach/harmony_audio.h>
-
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "../codecs/wm8903.h"
-
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
-#include "tegra_asoc_utils.h"
-
-#define DRV_NAME "tegra-snd-harmony"
-
-#define GPIO_SPKR_EN BIT(0)
-#define GPIO_INT_MIC_EN BIT(1)
-#define GPIO_EXT_MIC_EN BIT(2)
-
-struct tegra_harmony {
- struct tegra_asoc_utils_data util_data;
- struct harmony_audio_platform_data *pdata;
- int gpio_requested;
-};
-
-static int harmony_asoc_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- int srate, mclk, mclk_change;
- int err;
-
- srate = params_rate(params);
- switch (srate) {
- case 64000:
- case 88200:
- case 96000:
- mclk = 128 * srate;
- break;
- default:
- mclk = 256 * srate;
- break;
- }
- /* FIXME: Codec only requires >= 3MHz if OSR==0 */
- while (mclk < 6000000)
- mclk *= 2;
-
- err = tegra_asoc_utils_set_rate(&harmony->util_data, srate, mclk,
- &mclk_change);
- if (err < 0) {
- dev_err(card->dev, "Can't configure clocks\n");
- return err;
- }
-
- err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (err < 0) {
- dev_err(card->dev, "codec_dai fmt not set\n");
- return err;
- }
-
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (err < 0) {
- dev_err(card->dev, "cpu_dai fmt not set\n");
- return err;
- }
-
- if (mclk_change) {
- err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
- SND_SOC_CLOCK_IN);
- if (err < 0) {
- dev_err(card->dev, "codec_dai clock not set\n");
- return err;
- }
- }
-
- return 0;
-}
-
-static struct snd_soc_ops harmony_asoc_ops = {
- .hw_params = harmony_asoc_hw_params,
-};
-
-static struct snd_soc_jack harmony_hp_jack;
-
-static struct snd_soc_jack_pin harmony_hp_jack_pins[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-static struct snd_soc_jack_gpio harmony_hp_jack_gpios[] = {
- {
- .name = "headphone detect",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 150,
- .invert = 1,
- }
-};
-
-static struct snd_soc_jack harmony_mic_jack;
-
-static struct snd_soc_jack_pin harmony_mic_jack_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-
-static int harmony_event_int_spk(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
-
- gpio_set_value_cansleep(pdata->gpio_spkr_en,
- SND_SOC_DAPM_EVENT_ON(event));
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Int Spk", harmony_event_int_spk),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route harmony_audio_map[] = {
- {"Headphone Jack", NULL, "HPOUTR"},
- {"Headphone Jack", NULL, "HPOUTL"},
- {"Int Spk", NULL, "ROP"},
- {"Int Spk", NULL, "RON"},
- {"Int Spk", NULL, "LOP"},
- {"Int Spk", NULL, "LON"},
- {"Mic Bias", NULL, "Mic Jack"},
- {"IN1L", NULL, "Mic Bias"},
-};
-
-static const struct snd_kcontrol_new harmony_controls[] = {
- SOC_DAPM_PIN_SWITCH("Int Spk"),
-};
-
-static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
- int ret;
-
- ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
- if (ret) {
- dev_err(card->dev, "cannot get spkr_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_SPKR_EN;
-
- gpio_direction_output(pdata->gpio_spkr_en, 0);
-
- ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
- if (ret) {
- dev_err(card->dev, "cannot get int_mic_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_INT_MIC_EN;
-
- /* Disable int mic; enable signal is active-high */
- gpio_direction_output(pdata->gpio_int_mic_en, 0);
-
- ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
- if (ret) {
- dev_err(card->dev, "cannot get ext_mic_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_EXT_MIC_EN;
-
- /* Enable ext mic; enable signal is active-low */
- gpio_direction_output(pdata->gpio_ext_mic_en, 0);
-
- ret = snd_soc_add_controls(codec, harmony_controls,
- ARRAY_SIZE(harmony_controls));
- if (ret < 0)
- return ret;
-
- snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets,
- ARRAY_SIZE(harmony_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, harmony_audio_map,
- ARRAY_SIZE(harmony_audio_map));
-
- harmony_hp_jack_gpios[0].gpio = pdata->gpio_hp_det;
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
- &harmony_hp_jack);
- snd_soc_jack_add_pins(&harmony_hp_jack,
- ARRAY_SIZE(harmony_hp_jack_pins),
- harmony_hp_jack_pins);
- snd_soc_jack_add_gpios(&harmony_hp_jack,
- ARRAY_SIZE(harmony_hp_jack_gpios),
- harmony_hp_jack_gpios);
-
- snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
- &harmony_mic_jack);
- snd_soc_jack_add_pins(&harmony_mic_jack,
- ARRAY_SIZE(harmony_mic_jack_pins),
- harmony_mic_jack_pins);
- wm8903_mic_detect(codec, &harmony_mic_jack, SND_JACK_MICROPHONE, 0);
-
- snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
-
- snd_soc_dapm_nc_pin(dapm, "IN3L");
- snd_soc_dapm_nc_pin(dapm, "IN3R");
- snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
- snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
-
- snd_soc_dapm_sync(dapm);
-
- return 0;
-}
-
-static struct snd_soc_dai_link harmony_wm8903_dai = {
- .name = "WM8903",
- .stream_name = "WM8903 PCM",
- .codec_name = "wm8903.0-001a",
- .platform_name = "tegra-pcm-audio",
- .cpu_dai_name = "tegra-i2s.0",
- .codec_dai_name = "wm8903-hifi",
- .init = harmony_asoc_init,
- .ops = &harmony_asoc_ops,
-};
-
-static struct snd_soc_card snd_soc_harmony = {
- .name = "tegra-harmony",
- .dai_link = &harmony_wm8903_dai,
- .num_links = 1,
-};
-
-static __devinit int tegra_snd_harmony_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_harmony;
- struct tegra_harmony *harmony;
- struct harmony_audio_platform_data *pdata;
- int ret;
-
- if (!machine_is_harmony()) {
- dev_err(&pdev->dev, "Not running on Tegra Harmony!\n");
- return -ENODEV;
- }
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data supplied\n");
- return -EINVAL;
- }
-
- harmony = kzalloc(sizeof(struct tegra_harmony), GFP_KERNEL);
- if (!harmony) {
- dev_err(&pdev->dev, "Can't allocate tegra_harmony\n");
- return -ENOMEM;
- }
-
- harmony->pdata = pdata;
-
- ret = tegra_asoc_utils_init(&harmony->util_data, &pdev->dev);
- if (ret)
- goto err_free_harmony;
-
- card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
- snd_soc_card_set_drvdata(card, harmony);
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
- goto err_clear_drvdata;
- }
-
- return 0;
-
-err_clear_drvdata:
- snd_soc_card_set_drvdata(card, NULL);
- platform_set_drvdata(pdev, NULL);
- card->dev = NULL;
- tegra_asoc_utils_fini(&harmony->util_data);
-err_free_harmony:
- kfree(harmony);
- return ret;
-}
-
-static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
-
- snd_soc_unregister_card(card);
-
- snd_soc_card_set_drvdata(card, NULL);
- platform_set_drvdata(pdev, NULL);
- card->dev = NULL;
-
- tegra_asoc_utils_fini(&harmony->util_data);
-
- if (harmony->gpio_requested & GPIO_EXT_MIC_EN)
- gpio_free(pdata->gpio_ext_mic_en);
- if (harmony->gpio_requested & GPIO_INT_MIC_EN)
- gpio_free(pdata->gpio_int_mic_en);
- if (harmony->gpio_requested & GPIO_SPKR_EN)
- gpio_free(pdata->gpio_spkr_en);
-
- kfree(harmony);
-
- return 0;
-}
-
-static struct platform_driver tegra_snd_harmony_driver = {
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- .pm = &snd_soc_pm_ops,
- },
- .probe = tegra_snd_harmony_probe,
- .remove = __devexit_p(tegra_snd_harmony_remove),
-};
-
-static int __init snd_tegra_harmony_init(void)
-{
- return platform_driver_register(&tegra_snd_harmony_driver);
-}
-module_init(snd_tegra_harmony_init);
-
-static void __exit snd_tegra_harmony_exit(void)
-{
- platform_driver_unregister(&tegra_snd_harmony_driver);
-}
-module_exit(snd_tegra_harmony_exit);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Harmony machine ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index 52f0a3f9ce40..dfa85cbb05c8 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -28,9 +28,10 @@
#include "tegra_asoc_utils.h"
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
- int mclk, int *mclk_change)
+ int mclk)
{
int new_baseclock;
+ bool clk_change;
int err;
switch (srate) {
@@ -52,10 +53,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
return -EINVAL;
}
- *mclk_change = ((new_baseclock != data->set_baseclock) ||
+ clk_change = ((new_baseclock != data->set_baseclock) ||
(mclk != data->set_mclk));
- if (!*mclk_change)
- return 0;
+ if (!clk_change)
+ return 0;
data->set_baseclock = 0;
data->set_mclk = 0;
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
index bbba7afdfc2c..4818195da25c 100644
--- a/sound/soc/tegra/tegra_asoc_utils.h
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -36,7 +36,7 @@ struct tegra_asoc_utils_data {
};
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
- int mclk, int *mclk_change);
+ int mclk);
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
struct device *dev);
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
index 4f5e2c90b020..6b817e20548c 100644
--- a/sound/soc/tegra/tegra_i2s.c
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -114,7 +114,7 @@ static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
debugfs_remove(i2s->debug);
}
#else
-static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
+static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
{
}
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
new file mode 100644
index 000000000000..0d6738a8b29a
--- /dev/null
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -0,0 +1,475 @@
+/*
+ * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <mach/tegra_wm8903_pdata.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8903.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-wm8903"
+
+#define GPIO_SPKR_EN BIT(0)
+#define GPIO_HP_MUTE BIT(1)
+#define GPIO_INT_MIC_EN BIT(2)
+#define GPIO_EXT_MIC_EN BIT(3)
+
+struct tegra_wm8903 {
+ struct tegra_asoc_utils_data util_data;
+ struct tegra_wm8903_platform_data *pdata;
+ int gpio_requested;
+};
+
+static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 64000:
+ case 88200:
+ case 96000:
+ mclk = 128 * srate;
+ break;
+ default:
+ mclk = 256 * srate;
+ break;
+ }
+ /* FIXME: Codec only requires >= 3MHz if OSR==0 */
+ while (mclk < 6000000)
+ mclk *= 2;
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_wm8903_ops = {
+ .hw_params = tegra_wm8903_hw_params,
+};
+
+static struct snd_soc_jack tegra_wm8903_hp_jack;
+
+static struct snd_soc_jack_pin tegra_wm8903_hp_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio tegra_wm8903_hp_jack_gpio = {
+ .name = "headphone detect",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150,
+ .invert = 1,
+};
+
+static struct snd_soc_jack tegra_wm8903_mic_jack;
+
+static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_SPKR_EN))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_spkr_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_HP_MUTE))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_hp_mute,
+ !SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk),
+ SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route harmony_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1L", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route seaboard_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1R", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route kaen_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN2R", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route aebl_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "LINEOUTR"},
+ {"Int Spk", NULL, "LINEOUTL"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1R", NULL, "Mic Bias"},
+};
+
+static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Int Spk"),
+};
+
+static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+ int ret;
+
+ if (gpio_is_valid(pdata->gpio_spkr_en)) {
+ ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get spkr_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_SPKR_EN;
+
+ gpio_direction_output(pdata->gpio_spkr_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_hp_mute)) {
+ ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
+ if (ret) {
+ dev_err(card->dev, "cannot get hp_mute gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_HP_MUTE;
+
+ gpio_direction_output(pdata->gpio_hp_mute, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_int_mic_en)) {
+ ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get int_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_INT_MIC_EN;
+
+ /* Disable int mic; enable signal is active-high */
+ gpio_direction_output(pdata->gpio_int_mic_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
+ ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get ext_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_EXT_MIC_EN;
+
+ /* Enable ext mic; enable signal is active-low */
+ gpio_direction_output(pdata->gpio_ext_mic_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_hp_det)) {
+ tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det;
+ snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
+ &tegra_wm8903_hp_jack);
+ snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
+ ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
+ tegra_wm8903_hp_jack_pins);
+ snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
+ 1,
+ &tegra_wm8903_hp_jack_gpio);
+ }
+
+ snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
+ &tegra_wm8903_mic_jack);
+ snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
+ ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
+ tegra_wm8903_mic_jack_pins);
+ wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
+ 0);
+
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+
+ /* FIXME: Calculate automatically based on DAPM routes? */
+ if (!machine_is_harmony() && !machine_is_ventana())
+ snd_soc_dapm_nc_pin(dapm, "IN1L");
+ if (!machine_is_seaboard() && !machine_is_aebl())
+ snd_soc_dapm_nc_pin(dapm, "IN1R");
+ snd_soc_dapm_nc_pin(dapm, "IN2L");
+ if (!machine_is_kaen())
+ snd_soc_dapm_nc_pin(dapm, "IN2R");
+ snd_soc_dapm_nc_pin(dapm, "IN3L");
+ snd_soc_dapm_nc_pin(dapm, "IN3R");
+
+ if (machine_is_aebl()) {
+ snd_soc_dapm_nc_pin(dapm, "LON");
+ snd_soc_dapm_nc_pin(dapm, "RON");
+ snd_soc_dapm_nc_pin(dapm, "ROP");
+ snd_soc_dapm_nc_pin(dapm, "LOP");
+ } else {
+ snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
+ snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link tegra_wm8903_dai = {
+ .name = "WM8903",
+ .stream_name = "WM8903 PCM",
+ .codec_name = "wm8903.0-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "wm8903-hifi",
+ .init = tegra_wm8903_init,
+ .ops = &tegra_wm8903_ops,
+};
+
+static struct snd_soc_card snd_soc_tegra_wm8903 = {
+ .name = "tegra-wm8903",
+ .dai_link = &tegra_wm8903_dai,
+ .num_links = 1,
+
+ .controls = tegra_wm8903_controls,
+ .num_controls = ARRAY_SIZE(tegra_wm8903_controls),
+ .dapm_widgets = tegra_wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
+};
+
+static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_tegra_wm8903;
+ struct tegra_wm8903 *machine;
+ struct tegra_wm8903_platform_data *pdata;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ machine = kzalloc(sizeof(struct tegra_wm8903), GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
+ return -ENOMEM;
+ }
+
+ machine->pdata = pdata;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err_free_machine;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ if (machine_is_harmony() || machine_is_ventana()) {
+ card->dapm_routes = harmony_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
+ } else if (machine_is_seaboard()) {
+ card->dapm_routes = seaboard_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
+ } else if (machine_is_kaen()) {
+ card->dapm_routes = kaen_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
+ } else {
+ card->dapm_routes = aebl_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err_free_machine:
+ kfree(machine);
+ return ret;
+}
+
+static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ if (machine->gpio_requested & GPIO_EXT_MIC_EN)
+ gpio_free(pdata->gpio_ext_mic_en);
+ if (machine->gpio_requested & GPIO_INT_MIC_EN)
+ gpio_free(pdata->gpio_int_mic_en);
+ if (machine->gpio_requested & GPIO_HP_MUTE)
+ gpio_free(pdata->gpio_hp_mute);
+ if (machine->gpio_requested & GPIO_SPKR_EN)
+ gpio_free(pdata->gpio_spkr_en);
+
+ kfree(machine);
+
+ return 0;
+}
+
+static struct platform_driver tegra_wm8903_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = tegra_wm8903_driver_probe,
+ .remove = __devexit_p(tegra_wm8903_driver_remove),
+};
+
+static int __init tegra_wm8903_modinit(void)
+{
+ return platform_driver_register(&tegra_wm8903_driver);
+}
+module_init(tegra_wm8903_modinit);
+
+static void __exit tegra_wm8903_modexit(void)
+{
+ platform_driver_unregister(&tegra_wm8903_driver);
+}
+module_exit(tegra_wm8903_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
new file mode 100644
index 000000000000..8fc07e9adf2e
--- /dev/null
+++ b/sound/soc/tegra/trimslice.c
@@ -0,0 +1,228 @@
+/*
+ * trimslice.c - TrimSlice machine ASoC driver
+ *
+ * Copyright (C) 2011 - CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on code copyright/by:
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/tlv320aic23.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-trimslice"
+
+struct tegra_trimslice {
+ struct tegra_asoc_utils_data util_data;
+};
+
+static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ mclk = 128 * srate;
+
+ err = tegra_asoc_utils_set_rate(&trimslice->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops trimslice_asoc_ops = {
+ .hw_params = trimslice_asoc_hw_params,
+};
+
+static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Line Out", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route trimslice_audio_map[] = {
+ {"Line Out", NULL, "LOUT"},
+ {"Line Out", NULL, "ROUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+};
+
+static int trimslice_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_nc_pin(dapm, "LHPOUT");
+ snd_soc_dapm_nc_pin(dapm, "RHPOUT");
+ snd_soc_dapm_nc_pin(dapm, "MICIN");
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .codec_name = "tlv320aic23-codec.2-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "tlv320aic23-hifi",
+ .init = trimslice_asoc_init,
+ .ops = &trimslice_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_trimslice = {
+ .name = "tegra-trimslice",
+ .dai_link = &trimslice_tlv320aic23_dai,
+ .num_links = 1,
+
+ .dapm_widgets = trimslice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
+ .dapm_routes = trimslice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
+};
+
+static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_trimslice;
+ struct tegra_trimslice *trimslice;
+ int ret;
+
+ trimslice = kzalloc(sizeof(struct tegra_trimslice), GFP_KERNEL);
+ if (!trimslice) {
+ dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
+ return -ENOMEM;
+ }
+
+ ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
+ if (ret)
+ goto err_free_trimslice;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, trimslice);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&trimslice->util_data);
+err_free_trimslice:
+ kfree(trimslice);
+ return ret;
+}
+
+static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&trimslice->util_data);
+
+ kfree(trimslice);
+
+ return 0;
+}
+
+static struct platform_driver tegra_snd_trimslice_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_snd_trimslice_probe,
+ .remove = __devexit_p(tegra_snd_trimslice_remove),
+};
+
+static int __init snd_tegra_trimslice_init(void)
+{
+ return platform_driver_register(&tegra_snd_trimslice_driver);
+}
+module_init(snd_tegra_trimslice_init);
+
+static void __exit snd_tegra_trimslice_exit(void)
+{
+ platform_driver_unregister(&tegra_snd_trimslice_driver);
+}
+module_exit(snd_tegra_trimslice_exit);
+
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Trimslice machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c
index 248463511186..ac828eff1a63 100644
--- a/sound/usb/6fire/control.c
+++ b/sound/usb/6fire/control.c
@@ -65,6 +65,15 @@ init_data[] = {
{ 0 } /* TERMINATING ENTRY */
};
+static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
+/* values to write to soundcard register for all samplerates */
+static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
+static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
+
+enum {
+ DIGITAL_THRU_ONLY_SAMPLERATE = 3
+};
+
static void usb6fire_control_master_vol_update(struct control_runtime *rt)
{
struct comm_runtime *comm_rt = rt->chip->comm;
@@ -95,6 +104,67 @@ static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
}
}
+static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
+{
+ int ret;
+ struct usb_device *device = rt->chip->dev;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (rate < 0 || rate >= CONTROL_N_RATES)
+ return -EINVAL;
+
+ ret = usb_set_interface(device, 1, rates_altsetting[rate]);
+ if (ret < 0)
+ return ret;
+
+ /* set soundcard clock */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
+ rates_6fire_vh[rate]);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_set_channels(
+ struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in)
+{
+ int ret;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ /* enable analog inputs and outputs
+ * (one bit per stereo-channel) */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x02,
+ (1 << (n_analog_out / 2)) - 1,
+ (1 << (n_analog_in / 2)) - 1);
+ if (ret < 0)
+ return ret;
+
+ /* disable digital inputs and outputs */
+ /* TODO: use spdif_x to enable/disable digital channels */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_streaming_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (comm_rt) {
+ if (!rt->usb_streaming && rt->digital_thru_switch)
+ usb6fire_control_set_rate(rt,
+ DIGITAL_THRU_ONLY_SAMPLERATE);
+ return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
+ (rt->usb_streaming ? 0x01 : 0x00) |
+ (rt->digital_thru_switch ? 0x08 : 0x00));
+ }
+ return -EINVAL;
+}
+
static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -195,6 +265,28 @@ static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
+ rt->digital_thru_switch = ucontrol->value.integer.value[0];
+ usb6fire_control_streaming_update(rt);
+ changed = 1;
+ }
+ return changed;
+}
+
+static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = rt->digital_thru_switch;
+ return 0;
+}
+
static struct __devinitdata snd_kcontrol_new elements[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -223,6 +315,15 @@ static struct __devinitdata snd_kcontrol_new elements[] = {
.get = usb6fire_control_opt_coax_get,
.put = usb6fire_control_opt_coax_put
},
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Thru Playback Route",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = usb6fire_control_digital_thru_get,
+ .put = usb6fire_control_digital_thru_put
+ },
{}
};
@@ -238,6 +339,9 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
return -ENOMEM;
rt->chip = chip;
+ rt->update_streaming = usb6fire_control_streaming_update;
+ rt->set_rate = usb6fire_control_set_rate;
+ rt->set_channels = usb6fire_control_set_channels;
i = 0;
while (init_data[i].type) {
@@ -249,6 +353,7 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
usb6fire_control_opt_coax_update(rt);
usb6fire_control_line_phono_update(rt);
usb6fire_control_master_vol_update(rt);
+ usb6fire_control_streaming_update(rt);
i = 0;
while (elements[i].name) {
diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h
index b534c777ab02..8f5aeead2e3d 100644
--- a/sound/usb/6fire/control.h
+++ b/sound/usb/6fire/control.h
@@ -21,12 +21,29 @@ enum {
CONTROL_MAX_ELEMENTS = 32
};
+enum {
+ CONTROL_RATE_44KHZ,
+ CONTROL_RATE_48KHZ,
+ CONTROL_RATE_88KHZ,
+ CONTROL_RATE_96KHZ,
+ CONTROL_RATE_176KHZ,
+ CONTROL_RATE_192KHZ,
+ CONTROL_N_RATES
+};
+
struct control_runtime {
+ int (*update_streaming)(struct control_runtime *rt);
+ int (*set_rate)(struct control_runtime *rt, int rate);
+ int (*set_channels)(struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in);
+
struct sfire_chip *chip;
struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
bool opt_coax_switch;
bool line_phono_switch;
+ bool digital_thru_switch;
+ bool usb_streaming;
u8 master_vol;
};
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
index 86c1a3103760..d47beffedb0f 100644
--- a/sound/usb/6fire/firmware.c
+++ b/sound/usb/6fire/firmware.c
@@ -3,12 +3,6 @@
*
* Firmware loader
*
- * Currently not working for all devices. To be able to use the device
- * in linux, it is also possible to let the windows driver upload the firmware.
- * For that, start the computer in windows and reboot.
- * As long as the device is connected to the power supply, no firmware reload
- * needs to be performed.
- *
* Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011
* Version: 0.3.0
@@ -21,6 +15,7 @@
*/
#include <linux/firmware.h>
+#include <linux/bitrev.h>
#include "firmware.h"
#include "chip.h"
@@ -33,32 +28,6 @@ enum {
FPGA_BUFSIZE = 512, FPGA_EP = 2
};
-static const u8 BIT_REVERSE_TABLE[256] = {
- 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50,
- 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8,
- 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04,
- 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4,
- 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c,
- 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82,
- 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32,
- 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
- 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46,
- 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6,
- 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e,
- 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
- 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71,
- 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99,
- 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25,
- 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
- 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d,
- 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3,
- 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b,
- 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb,
- 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67,
- 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f,
- 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f,
- 0xbf, 0x7f, 0xff };
-
/*
* wMaxPacketSize of pcm endpoints.
* keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c
@@ -72,6 +41,10 @@ static const u8 ep_w_max_packet_size[] = {
0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
};
+static const u8 known_fw_versions[][4] = {
+ { 0x03, 0x01, 0x0b, 0x00 }
+};
+
struct ihex_record {
u16 address;
u8 len;
@@ -340,7 +313,7 @@ static int usb6fire_fw_fpga_upload(
while (c != end) {
for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
- buffer[i] = BIT_REVERSE_TABLE[(u8) *c];
+ buffer[i] = byte_rev_table[(u8) *c];
ret = usb6fire_fw_fpga_write(device, buffer, i);
if (ret < 0) {
@@ -363,6 +336,25 @@ static int usb6fire_fw_fpga_upload(
return 0;
}
+/* check, if the firmware version the devices has currently loaded
+ * is known by this driver. 'version' needs to have 4 bytes version
+ * info data. */
+static int usb6fire_fw_check(u8 *version)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++)
+ if (!memcmp(version, known_fw_versions + i, 4))
+ return 0;
+
+ snd_printk(KERN_ERR PREFIX "invalid fimware version in device: "
+ "%02x %02x %02x %02x. "
+ "please reconnect to power. if this failure "
+ "still happens, check your firmware installation.",
+ version[0], version[1], version[2], version[3]);
+ return -EINVAL;
+}
+
int usb6fire_fw_init(struct usb_interface *intf)
{
int i;
@@ -378,9 +370,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
"firmware state.\n");
return ret;
}
- if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55
- || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7]
- != 0x00) {
+ if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
"received from device: ");
for (i = 0; i < 8; i++)
@@ -389,7 +379,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
return -EIO;
}
/* do we need fpga loader ezusb firmware? */
- if (buffer[3] == 0x01 && buffer[6] == 0x19) {
+ if (buffer[3] == 0x01) {
ret = usb6fire_fw_ezusb_upload(intf,
"6fire/dmx6firel2.ihx", 0, NULL, 0);
if (ret < 0)
@@ -397,7 +387,10 @@ int usb6fire_fw_init(struct usb_interface *intf)
return FW_NOT_READY;
}
/* do we need fpga firmware and application ezusb firmware? */
- else if (buffer[3] == 0x02 && buffer[6] == 0x0b) {
+ else if (buffer[3] == 0x02) {
+ ret = usb6fire_fw_check(buffer + 4);
+ if (ret < 0)
+ return ret;
ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
if (ret < 0)
return ret;
@@ -410,8 +403,8 @@ int usb6fire_fw_init(struct usb_interface *intf)
return FW_NOT_READY;
}
/* all fw loaded? */
- else if (buffer[3] == 0x03 && buffer[6] == 0x0b)
- return 0;
+ else if (buffer[3] == 0x03)
+ return usb6fire_fw_check(buffer + 4);
/* unknown data? */
else {
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
index ba62c7468ba8..b137b25865cc 100644
--- a/sound/usb/6fire/pcm.c
+++ b/sound/usb/6fire/pcm.c
@@ -17,26 +17,23 @@
#include "pcm.h"
#include "chip.h"
#include "comm.h"
+#include "control.h"
enum {
OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
};
/* keep next two synced with
- * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */
+ * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE
+ * and CONTROL_RATE_XXX in control.h */
static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 };
static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
-static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
static const int rates_alsaid[] = {
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 };
-/* values to write to soundcard register for all samplerates */
-static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
-static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
-
enum { /* settings for pcm */
OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
};
@@ -48,15 +45,6 @@ enum { /* pcm streaming states */
STREAM_STOPPING
};
-enum { /* pcm sample rates (also index into RATES_XXX[]) */
- RATE_44KHZ,
- RATE_48KHZ,
- RATE_88KHZ,
- RATE_96KHZ,
- RATE_176KHZ,
- RATE_192KHZ
-};
-
static const struct snd_pcm_hardware pcm_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -64,7 +52,7 @@ static const struct snd_pcm_hardware pcm_hw = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
@@ -87,57 +75,34 @@ static const struct snd_pcm_hardware pcm_hw = {
static int usb6fire_pcm_set_rate(struct pcm_runtime *rt)
{
int ret;
- struct usb_device *device = rt->chip->dev;
- struct comm_runtime *comm_rt = rt->chip->comm;
+ struct control_runtime *ctrl_rt = rt->chip->control;
- if (rt->rate >= ARRAY_SIZE(rates))
- return -EINVAL;
- /* disable streaming */
- ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00);
+ ctrl_rt->usb_streaming = false;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error stopping streaming while "
"setting samplerate %d.\n", rates[rt->rate]);
return ret;
}
- ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]);
- if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error setting interface "
- "altsetting %d for samplerate %d.\n",
- rates_altsetting[rt->rate], rates[rt->rate]);
- return ret;
- }
-
- /* set soundcard clock */
- ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate],
- rates_6fire_vh[rt->rate]);
+ ret = ctrl_rt->set_rate(ctrl_rt, rt->rate);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n",
rates[rt->rate]);
return ret;
}
- /* enable analog inputs and outputs
- * (one bit per stereo-channel) */
- ret = comm_rt->write16(comm_rt, 0x02, 0x02,
- (1 << (OUT_N_CHANNELS / 2)) - 1,
- (1 << (IN_N_CHANNELS / 2)) - 1);
+ ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS,
+ false, false);
if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error initializing analog channels "
+ snd_printk(KERN_ERR PREFIX "error initializing channels "
"while setting samplerate %d.\n",
rates[rt->rate]);
return ret;
}
- /* disable digital inputs and outputs */
- ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
- if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error initializing digital "
- "channels while setting samplerate %d.\n",
- rates[rt->rate]);
- return ret;
- }
- ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01);
+ ctrl_rt->usb_streaming = true;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error starting streaming while "
"setting samplerate %d.\n", rates[rt->rate]);
@@ -168,12 +133,15 @@ static struct pcm_substream *usb6fire_pcm_get_substream(
static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
{
int i;
+ struct control_runtime *ctrl_rt = rt->chip->control;
if (rt->stream_state != STREAM_DISABLED) {
for (i = 0; i < PCM_N_URBS; i++) {
usb_kill_urb(&rt->in_urbs[i].instance);
usb_kill_urb(&rt->out_urbs[i].instance);
}
+ ctrl_rt->usb_streaming = false;
+ ctrl_rt->update_streaming(ctrl_rt);
rt->stream_state = STREAM_DISABLED;
}
}
@@ -228,7 +196,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
unsigned int total_length = 0;
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
- u32 *src = (u32 *) urb->buffer;
+ u32 *src = NULL;
u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
* (alsa_rt->frame_bits >> 3));
u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
@@ -244,7 +212,12 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
else
frame_count = 0;
- src = (u32 *) (urb->buffer + total_length);
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ src = (u32 *) (urb->buffer + total_length);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ src = (u32 *) (urb->buffer - 1 + total_length);
+ else
+ return;
src++; /* skip leading 4 bytes of every packet */
total_length += urb->packets[i].length;
for (frame = 0; frame < frame_count; frame++) {
@@ -274,9 +247,18 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub,
* (alsa_rt->frame_bits >> 3));
u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
* (alsa_rt->frame_bits >> 3));
- u32 *dest = (u32 *) urb->buffer;
+ u32 *dest;
int bytes_per_frame = alsa_rt->channels << 2;
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ dest = (u32 *) (urb->buffer - 1);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ dest = (u32 *) (urb->buffer);
+ else {
+ snd_printk(KERN_ERR PREFIX "Unknown sample format.");
+ return;
+ }
+
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
/* at least 4 header bytes for valid packet.
* after that: 32 bits per sample for analog channels */
@@ -456,7 +438,7 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
/* all substreams closed? if so, stop streaming */
if (!rt->playback.instance && !rt->capture.instance) {
usb6fire_pcm_stream_stop(rt);
- rt->rate = -1;
+ rt->rate = ARRAY_SIZE(rates);
}
}
mutex_unlock(&rt->stream_mutex);
@@ -480,7 +462,6 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
- int i;
int ret;
if (rt->panic)
@@ -493,12 +474,10 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
sub->period_off = 0;
if (rt->stream_state == STREAM_DISABLED) {
- for (i = 0; i < ARRAY_SIZE(rates); i++)
- if (alsa_rt->rate == rates[i]) {
- rt->rate = i;
+ for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++)
+ if (alsa_rt->rate == rates[rt->rate])
break;
- }
- if (i == ARRAY_SIZE(rates)) {
+ if (rt->rate == ARRAY_SIZE(rates)) {
mutex_unlock(&rt->stream_mutex);
snd_printk("invalid rate %d in prepare.\n",
alsa_rt->rate);
@@ -613,7 +592,7 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip)
rt->chip = chip;
rt->stream_state = STREAM_DISABLED;
- rt->rate = -1;
+ rt->rate = ARRAY_SIZE(rates);
init_waitqueue_head(&rt->stream_wait_queue);
mutex_init(&rt->stream_mutex);
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 97724d8fa9f6..8beb77563da2 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -100,19 +100,17 @@ config SND_USB_US122L
config SND_USB_6FIRE
tristate "TerraTec DMX 6Fire USB"
- depends on EXPERIMENTAL
select FW_LOADER
+ select BITREVERSE
select SND_RAWMIDI
select SND_PCM
help
Say Y here to include support for TerraTec 6fire DMX USB interface.
You will need firmware files in order to be able to use the device
- after it has been coldstarted. This driver currently does not support
- firmware loading for all devices. If you own such a device,
- you could start windows and let the windows driver upload
- the firmware. As long as you do not unplug your device from power,
- it should be usable.
+ after it has been coldstarted. An install script for the firmware
+ and further help can be found at
+ http://sixfireusb.sourceforge.net
endif # SND_USB
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 7754a1034545..075195e8661a 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -104,6 +104,15 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
int err;
unsigned char data;
struct usb_device *dev = chip->dev;
+ struct uac_clock_source_descriptor *cs_desc =
+ snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+ if (!cs_desc)
+ return 0;
+
+ /* If a clock source can't tell us whether it's valid, we assume it is */
+ if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID))
+ return 1;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
@@ -114,7 +123,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
if (err < 0) {
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
__func__, source_id);
- return err;
+ return 0;
}
return !!data;
diff --git a/sound/usb/debug.h b/sound/usb/debug.h
index 343ec2d9ee66..58030176f008 100644
--- a/sound/usb/debug.h
+++ b/sound/usb/debug.h
@@ -8,7 +8,7 @@
#ifdef HW_CONST_DEBUG
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
-#define hwc_debug(fmt, args...) /**/
+#define hwc_debug(fmt, args...) do { } while(0)
#endif
#endif /* __USBAUDIO_DEBUG_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
index f079b5e2ab28..8d042dce0d16 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -30,6 +30,7 @@
#include "helper.h"
#include "debug.h"
#include "clock.h"
+#include "format.h"
/*
* parse the audio format type I descriptor
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 6ec33b62e6cf..eab06edcc9b7 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1097,11 +1097,13 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
append_ctl_name(kctl, control == UAC_FU_MUTE ?
" Switch" : " Volume");
if (control == UAC_FU_VOLUME) {
- kctl->tlv.c = mixer_vol_tlv;
- kctl->vd[0].access |=
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
check_mapped_dB(map, cval);
+ if (cval->dBmin < cval->dBmax) {
+ kctl->tlv.c = mixer_vol_tlv;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
}
break;
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 73dcc8256bc0..9146cffa6ede 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -61,6 +61,7 @@ static const struct rc_config {
{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
+ { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
};
@@ -188,6 +189,12 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
!value, 0, NULL, 0, 100);
+ /* USB X-Fi S51 Pro */
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df))
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ !value, 0, NULL, 0, 100);
else
err = snd_usb_ctl_msg(mixer->chip->dev,
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
@@ -234,9 +241,13 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
/* USB X-Fi S51 doesn't have a CMSS LED */
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
continue;
+ /* USB X-Fi S51 Pro doesn't have one either */
+ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
+ continue;
if (i > 1 && /* Live24ext has 2 LEDs only */
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
break;
err = snd_ctl_add(mixer->chip->card,
@@ -512,6 +523,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x041e, 0x3020):
case USB_ID(0x041e, 0x3040):
case USB_ID(0x041e, 0x3042):
+ case USB_ID(0x041e, 0x30df):
case USB_ID(0x041e, 0x3048):
err = snd_audigy2nx_controls_create(mixer);
if (err < 0)
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index c66d3f64dcf8..78792a8900c3 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -1651,6 +1651,32 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ USB_DEVICE(0x0582, 0x0127),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "GR-55", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
@@ -1953,7 +1979,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE(0x0763, 0x2080),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra", */
@@ -2020,7 +2046,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE(0x0763, 0x2081),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra 8R", */
@@ -2179,6 +2205,17 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+/* KORG devices */
+{
+ USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "KORG, Inc.",
+ /* .product_name = "PANDORA PX5D", */
+ .ifnum = 3,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ }
+},
+
/* AKAI devices */
{
USB_DEVICE(0x09e8, 0x0062),
@@ -2332,6 +2369,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Native Instruments MK2 series */
{
+ /* Komplete Audio 6 */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x17cc,
+ .idProduct = 0x1000,
+},
+{
/* Traktor Audio 6 */
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = 0x17cc,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 1b94ec3a3368..bd13d7257240 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -540,6 +540,7 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
/* Access Music VirusTI Desktop */
return snd_usb_accessmusic_boot_quirk(dev);
+ case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
return snd_usb_nativeinstruments_boot_quirk(dev);