summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/rawmidi.c45
-rw-r--r--sound/firewire/speakers.c2
-rw-r--r--sound/pci/ad1889.c4
-rw-r--r--sound/pci/ali5451/ali5451.c4
-rw-r--r--sound/pci/als300.c4
-rw-r--r--sound/pci/als4000.c2
-rw-r--r--sound/pci/asihpi/asihpi.c81
-rw-r--r--sound/pci/asihpi/hpi.h24
-rw-r--r--sound/pci/asihpi/hpi6000.c11
-rw-r--r--sound/pci/asihpi/hpi6205.c52
-rw-r--r--sound/pci/asihpi/hpi6205.h25
-rw-r--r--sound/pci/asihpi/hpi_internal.h155
-rw-r--r--sound/pci/asihpi/hpicmn.c17
-rw-r--r--sound/pci/asihpi/hpidspcd.c136
-rw-r--r--sound/pci/asihpi/hpidspcd.h72
-rw-r--r--sound/pci/asihpi/hpifunc.c86
-rw-r--r--sound/pci/asihpi/hpimsginit.c4
-rw-r--r--sound/pci/asihpi/hpimsgx.c6
-rw-r--r--sound/pci/asihpi/hpioctl.c10
-rw-r--r--sound/pci/asihpi/hpios.c8
-rw-r--r--sound/pci/asihpi/hpios.h1
-rw-r--r--sound/pci/atiixp.c4
-rw-r--r--sound/pci/atiixp_modem.c4
-rw-r--r--sound/pci/au88x0/au88x0.c4
-rw-r--r--sound/pci/aw2/aw2-alsa.c4
-rw-r--r--sound/pci/azt3328.c4
-rw-r--r--sound/pci/bt87x.c4
-rw-r--r--sound/pci/ca0106/ca0106_main.c4
-rw-r--r--sound/pci/cmipci.c4
-rw-r--r--sound/pci/cs4281.c4
-rw-r--r--sound/pci/cs46xx/cs46xx.c2
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c2
-rw-r--r--sound/pci/cs5530.c2
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c4
-rw-r--r--sound/pci/ctxfi/ct20k2reg.h1
-rw-r--r--sound/pci/ctxfi/ctatc.c107
-rw-r--r--sound/pci/ctxfi/ctatc.h8
-rw-r--r--sound/pci/ctxfi/ctdaio.c23
-rw-r--r--sound/pci/ctxfi/ctdaio.h1
-rw-r--r--sound/pci/ctxfi/cthardware.h14
-rw-r--r--sound/pci/ctxfi/cthw20k1.c15
-rw-r--r--sound/pci/ctxfi/cthw20k2.c337
-rw-r--r--sound/pci/ctxfi/ctmixer.c145
-rw-r--r--sound/pci/ctxfi/xfi.c6
-rw-r--r--sound/pci/echoaudio/echoaudio.c6
-rw-r--r--sound/pci/emu10k1/emu10k1.c2
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c2
-rw-r--r--sound/pci/emu10k1/emu10k1x.c4
-rw-r--r--sound/pci/ens1370.c4
-rw-r--r--sound/pci/es1938.c6
-rw-r--r--sound/pci/es1968.c68
-rw-r--r--sound/pci/fm801.c4
-rw-r--r--sound/pci/hda/hda_codec.c10
-rw-r--r--sound/pci/hda/hda_intel.c4
-rw-r--r--sound/pci/ice1712/ice1712.c4
-rw-r--r--sound/pci/ice1712/ice1724.c4
-rw-r--r--sound/pci/intel8x0.c12
-rw-r--r--sound/pci/intel8x0m.c6
-rw-r--r--sound/pci/korg1212/korg1212.c4
-rw-r--r--sound/pci/lola/lola.c4
-rw-r--r--sound/pci/lola/lola.h2
-rw-r--r--sound/pci/lola/lola_mixer.c130
-rw-r--r--sound/pci/lx6464es/lx6464es.c25
-rw-r--r--sound/pci/lx6464es/lx6464es.h2
-rw-r--r--sound/pci/lx6464es/lx_core.c14
-rw-r--r--sound/pci/lx6464es/lx_core.h2
-rw-r--r--sound/pci/maestro3.c75
-rw-r--r--sound/pci/mixart/mixart.c4
-rw-r--r--sound/pci/nm256/nm256.c4
-rw-r--r--sound/pci/oxygen/oxygen.c2
-rw-r--r--sound/pci/oxygen/oxygen_lib.c2
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c6
-rw-r--r--sound/pci/oxygen/virtuoso.c2
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c5
-rw-r--r--sound/pci/pcxhr/pcxhr.c4
-rw-r--r--sound/pci/riptide/riptide.c6
-rw-r--r--sound/pci/rme32.c4
-rw-r--r--sound/pci/rme96.c4
-rw-r--r--sound/pci/rme9652/hdsp.c4
-rw-r--r--sound/pci/rme9652/hdspm.c4
-rw-r--r--sound/pci/rme9652/rme9652.c4
-rw-r--r--sound/pci/sis7019.c6
-rw-r--r--sound/pci/sonicvibes.c4
-rw-r--r--sound/pci/trident/trident.c2
-rw-r--r--sound/pci/trident/trident_main.c2
-rw-r--r--sound/pci/via82xx.c4
-rw-r--r--sound/pci/via82xx_modem.c4
-rw-r--r--sound/pci/vx222/vx222.c4
-rw-r--r--sound/pci/ymfpci/ymfpci.c2
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c2
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c2
-rw-r--r--sound/pcmcia/vx/vxpocket.c2
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/atmel/atmel-pcm.c8
-rw-r--r--sound/soc/atmel/atmel-pcm.h2
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c6
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c1
-rw-r--r--sound/soc/au1x/dbdma2.c7
-rw-r--r--sound/soc/blackfin/Kconfig27
-rw-r--r--sound/soc/blackfin/Makefile4
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c6
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c25
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c6
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1701.c139
-rw-r--r--sound/soc/blackfin/bfin-eval-adav80x.c173
-rw-r--r--sound/soc/codecs/Kconfig22
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ad1836.c313
-rw-r--r--sound/soc/codecs/ad1836.h44
-rw-r--r--sound/soc/codecs/adau1701.c549
-rw-r--r--sound/soc/codecs/adau1701.h17
-rw-r--r--sound/soc/codecs/adav80x.c951
-rw-r--r--sound/soc/codecs/adav80x.h35
-rw-r--r--sound/soc/codecs/ak4641.c2
-rw-r--r--sound/soc/codecs/ak4642.c2
-rw-r--r--sound/soc/codecs/cs4270.c5
-rw-r--r--sound/soc/codecs/max98088.c2
-rw-r--r--sound/soc/codecs/max98095.c10
-rw-r--r--sound/soc/codecs/sta32x.c917
-rw-r--r--sound/soc/codecs/sta32x.h210
-rw-r--r--sound/soc/codecs/tlv320aic26.c14
-rw-r--r--sound/soc/codecs/tlv320aic3x.c43
-rw-r--r--sound/soc/codecs/twl6040.c4
-rw-r--r--sound/soc/codecs/wm8731.c29
-rw-r--r--sound/soc/codecs/wm8782.c80
-rw-r--r--sound/soc/codecs/wm8900.c1
-rw-r--r--sound/soc/codecs/wm8904.c1
-rw-r--r--sound/soc/codecs/wm8915.c156
-rw-r--r--sound/soc/codecs/wm8940.c7
-rw-r--r--sound/soc/codecs/wm8962.c132
-rw-r--r--sound/soc/codecs/wm8983.c1203
-rw-r--r--sound/soc/codecs/wm8983.h1029
-rw-r--r--sound/soc/codecs/wm8993.c3
-rw-r--r--sound/soc/codecs/wm8994.c157
-rw-r--r--sound/soc/codecs/wm8994.h3
-rw-r--r--sound/soc/codecs/wm9081.c2
-rw-r--r--sound/soc/codecs/wm_hubs.c54
-rw-r--r--sound/soc/codecs/wm_hubs.h10
-rw-r--r--sound/soc/davinci/davinci-pcm.c154
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.c6
-rw-r--r--sound/soc/fsl/fsl_dma.c8
-rw-r--r--sound/soc/fsl/fsl_ssi.c9
-rw-r--r--sound/soc/fsl/mpc5200_dma.c7
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c10
-rw-r--r--sound/soc/fsl/p1022_ds.c10
-rw-r--r--sound/soc/imx/imx-pcm-fiq.c8
-rw-r--r--sound/soc/imx/imx-ssi.c7
-rw-r--r--sound/soc/imx/imx-ssi.h3
-rw-r--r--sound/soc/jz4740/jz4740-pcm.c6
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c6
-rw-r--r--sound/soc/mid-x86/sst_platform.c5
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c2
-rw-r--r--sound/soc/nuc900/nuc900-pcm.c7
-rw-r--r--sound/soc/omap/Kconfig11
-rw-r--r--sound/soc/omap/Makefile4
-rw-r--r--sound/soc/omap/ams-delta.c3
-rw-r--r--sound/soc/omap/omap-hdmi.c158
-rw-r--r--sound/soc/omap/omap-hdmi.h36
-rw-r--r--sound/soc/omap/omap-pcm.c6
-rw-r--r--sound/soc/omap/omap4-hdmi-card.c129
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c5
-rw-r--r--sound/soc/s6000/s6000-pcm.c7
-rw-r--r--sound/soc/samsung/Kconfig16
-rw-r--r--sound/soc/samsung/Makefile4
-rw-r--r--sound/soc/samsung/dma.c6
-rw-r--r--sound/soc/samsung/i2s-regs.h143
-rw-r--r--sound/soc/samsung/i2s.c104
-rw-r--r--sound/soc/samsung/smdk_wm8994.c5
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c176
-rw-r--r--sound/soc/samsung/speyside.c61
-rw-r--r--sound/soc/samsung/speyside_wm8962.c264
-rw-r--r--sound/soc/sh/dma-sh7760.c6
-rw-r--r--sound/soc/sh/fsi-ak4642.c16
-rw-r--r--sound/soc/sh/fsi-da7210.c2
-rw-r--r--sound/soc/sh/fsi-hdmi.c4
-rw-r--r--sound/soc/sh/fsi.c582
-rw-r--r--sound/soc/sh/siu_pcm.c5
-rw-r--r--sound/soc/soc-cache.c692
-rw-r--r--sound/soc/soc-core.c826
-rw-r--r--sound/soc/soc-dapm.c275
-rw-r--r--sound/soc/soc-io.c396
-rw-r--r--sound/soc/soc-pcm.c639
-rw-r--r--sound/soc/tegra/Kconfig9
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra_i2s.c10
-rw-r--r--sound/soc/tegra/tegra_pcm.c6
-rw-r--r--sound/soc/tegra/tegra_spdif.c371
-rw-r--r--sound/soc/tegra/tegra_spdif.h473
-rw-r--r--sound/soc/tegra/tegra_wm8903.c2
-rw-r--r--sound/soc/txx9/txx9aclc.c5
-rw-r--r--sound/usb/card.c16
-rw-r--r--sound/usb/endpoint.c2
-rw-r--r--sound/usb/misc/ua101.c2
-rw-r--r--sound/usb/quirks-table.h30
-rw-r--r--sound/usb/quirks.c159
195 files changed, 11252 insertions, 3021 deletions
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index cbbed0d..849a0ed 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -92,16 +92,12 @@ static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substre
(!substream->append || runtime->avail >= count);
}
-static void snd_rawmidi_input_event_tasklet(unsigned long data)
+static void snd_rawmidi_input_event_work(struct work_struct *work)
{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
- substream->runtime->event(substream);
-}
-
-static void snd_rawmidi_output_trigger_tasklet(unsigned long data)
-{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
- substream->ops->trigger(substream, 1);
+ struct snd_rawmidi_runtime *runtime =
+ container_of(work, struct snd_rawmidi_runtime, event_work);
+ if (runtime->event)
+ runtime->event(runtime->substream);
}
static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
@@ -110,16 +106,10 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL)
return -ENOMEM;
+ runtime->substream = substream;
spin_lock_init(&runtime->lock);
init_waitqueue_head(&runtime->sleep);
- if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
- tasklet_init(&runtime->tasklet,
- snd_rawmidi_input_event_tasklet,
- (unsigned long)substream);
- else
- tasklet_init(&runtime->tasklet,
- snd_rawmidi_output_trigger_tasklet,
- (unsigned long)substream);
+ INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
runtime->event = NULL;
runtime->buffer_size = PAGE_SIZE;
runtime->avail_min = 1;
@@ -150,12 +140,7 @@ static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *subs
{
if (!substream->opened)
return;
- if (up) {
- tasklet_schedule(&substream->runtime->tasklet);
- } else {
- tasklet_kill(&substream->runtime->tasklet);
- substream->ops->trigger(substream, 0);
- }
+ substream->ops->trigger(substream, up);
}
static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -163,8 +148,8 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i
if (!substream->opened)
return;
substream->ops->trigger(substream, up);
- if (!up && substream->runtime->event)
- tasklet_kill(&substream->runtime->tasklet);
+ if (!up)
+ cancel_work_sync(&substream->runtime->event_work);
}
int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
@@ -641,10 +626,10 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
return -EINVAL;
}
if (params->buffer_size != runtime->buffer_size) {
- newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
+ newbuf = krealloc(runtime->buffer, params->buffer_size,
+ GFP_KERNEL);
if (!newbuf)
return -ENOMEM;
- kfree(runtime->buffer);
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
runtime->avail = runtime->buffer_size;
@@ -668,10 +653,10 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
return -EINVAL;
}
if (params->buffer_size != runtime->buffer_size) {
- newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
+ newbuf = krealloc(runtime->buffer, params->buffer_size,
+ GFP_KERNEL);
if (!newbuf)
return -ENOMEM;
- kfree(runtime->buffer);
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
}
@@ -926,7 +911,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
}
if (result > 0) {
if (runtime->event)
- tasklet_schedule(&runtime->tasklet);
+ schedule_work(&runtime->event_work);
else if (snd_rawmidi_ready(substream))
wake_up(&runtime->sleep);
}
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 5466de8..3fc257d 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -171,7 +171,7 @@ static int fwspk_open(struct snd_pcm_substream *substream)
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- 5000, 8192000);
+ 5000, UINT_MAX);
if (err < 0)
return err;
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index d8f6fd6..2015036 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -944,7 +944,7 @@ snd_ad1889_create(struct snd_card *card,
spin_lock_init(&chip->lock); /* only now can we call ad1889_free */
if (request_irq(pci->irq, snd_ad1889_interrupt,
- IRQF_SHARED, card->driver, chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
printk(KERN_ERR PFX "cannot obtain IRQ %d\n", pci->irq);
snd_ad1889_free(chip);
return -EBUSY;
@@ -1055,7 +1055,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = {
MODULE_DEVICE_TABLE(pci, snd_ad1889_ids);
static struct pci_driver ad1889_pci_driver = {
- .name = "AD1889 Audio",
+ .name = KBUILD_MODNAME,
.id_table = snd_ad1889_ids,
.probe = snd_ad1889_probe,
.remove = __devexit_p(snd_ad1889_remove),
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 5c6e322..b444b74 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -2090,7 +2090,7 @@ static int __devinit snd_ali_resources(struct snd_ali *codec)
codec->port = pci_resource_start(codec->pci, 0);
if (request_irq(codec->pci->irq, snd_ali_card_interrupt,
- IRQF_SHARED, "ALI 5451", codec)) {
+ IRQF_SHARED, KBUILD_MODNAME, codec)) {
snd_printk(KERN_ERR "Unable to request irq.\n");
return -EBUSY;
}
@@ -2295,7 +2295,7 @@ static void __devexit snd_ali_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "ALI 5451",
+ .name = KBUILD_MODNAME,
.id_table = snd_ali_ids,
.probe = snd_ali_probe,
.remove = __devexit_p(snd_ali_remove),
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index d7653cb..736c8e9 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -722,7 +722,7 @@ static int __devinit snd_als300_create(struct snd_card *card,
irq_handler = snd_als300_interrupt;
if (request_irq(pci->irq, irq_handler, IRQF_SHARED,
- card->shortname, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_als300_free(chip);
return -EBUSY;
@@ -846,7 +846,7 @@ static int __devinit snd_als300_probe(struct pci_dev *pci,
}
static struct pci_driver driver = {
- .name = "ALS300",
+ .name = KBUILD_MODNAME,
.id_table = snd_als300_ids,
.probe = snd_als300_probe,
.remove = __devexit_p(snd_als300_remove),
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 0e247cb..a9c1af3 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -1036,7 +1036,7 @@ static int snd_als4000_resume(struct pci_dev *pci)
static struct pci_driver driver = {
- .name = "ALS4000",
+ .name = KBUILD_MODNAME,
.id_table = snd_als4000_ids,
.probe = snd_card_als4000_probe,
.remove = __devexit_p(snd_card_als4000_remove),
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index e3569bd..b941d25 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -49,19 +49,21 @@ 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)
+static inline void
+snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
{
- snprintf(name, len, "pcmC%dD%d%c:%d",
+ snprintf(buf, size, "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)
+static inline void
+snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
+{
+ *buf = 0;
+}
#endif
#if defined CONFIG_SND_DEBUG_VERBOSE
@@ -304,7 +306,8 @@ static u16 handle_error(u16 err, int line, char *filename)
static void print_hwparams(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *p)
{
- DEBUG_NAME(substream, name);
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
snd_printd("%s HWPARAMS\n", name);
snd_printd(" samplerate %d Hz\n", params_rate(p));
snd_printd(" channels %d\n", params_channels(p));
@@ -576,8 +579,9 @@ 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);
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
snd_printdd("%s trigger\n", name);
switch (cmd) {
@@ -741,7 +745,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);
+ char name[16];
+
+ snd_pcm_debug_name(substream, name, sizeof(name));
snd_printdd("%s snd_card_asihpi_timer_function\n", name);
@@ -1323,10 +1329,12 @@ static const char * const asihpi_src_names[] = {
"RF",
"Clock",
"Bitstream",
- "Microphone",
- "Cobranet",
+ "Mic",
+ "Net",
"Analog",
"Adapter",
+ "RTP",
+ "GPI",
};
compile_time_assert(
@@ -1341,8 +1349,10 @@ static const char * const asihpi_dst_names[] = {
"Digital",
"RF",
"Speaker",
- "Cobranet Out",
- "Analog"
+ "Net",
+ "Analog",
+ "RTP",
+ "GPO",
};
compile_time_assert(
@@ -1476,11 +1486,40 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
+#define snd_asihpi_volume_mute_info snd_ctl_boolean_mono_info
+
+static int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 h_control = kcontrol->private_value;
+ u32 mute;
+
+ hpi_handle_error(hpi_volume_get_mute(h_control, &mute));
+ ucontrol->value.integer.value[0] = mute ? 0 : 1;
+
+ return 0;
+}
+
+static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 h_control = kcontrol->private_value;
+ int change = 1;
+ /* HPI currently only supports all or none muting of multichannel volume
+ ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted
+ */
+ int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS;
+ hpi_handle_error(hpi_volume_set_mute(h_control, mute));
+ return change;
+}
+
static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
+ int err;
+ u32 mute;
asihpi_ctl_init(&snd_control, hpi_ctl, "Volume");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1490,7 +1529,19 @@ static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
snd_control.put = snd_asihpi_volume_put;
snd_control.tlv.p = db_scale_100;
- return ctl_add(card, &snd_control, asihpi);
+ err = ctl_add(card, &snd_control, asihpi);
+ if (err)
+ return err;
+
+ if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) {
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Switch");
+ snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_control.info = snd_asihpi_volume_mute_info;
+ snd_control.get = snd_asihpi_volume_mute_get;
+ snd_control.put = snd_asihpi_volume_mute_put;
+ err = ctl_add(card, &snd_control, asihpi);
+ }
+ return err;
}
/*------------------------------------------------------------
@@ -2923,7 +2974,7 @@ static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = {
MODULE_DEVICE_TABLE(pci, asihpi_pci_tbl);
static struct pci_driver driver = {
- .name = "asihpi",
+ .name = KBUILD_MODNAME,
.id_table = asihpi_pci_tbl,
.probe = snd_asihpi_probe,
.remove = __devexit_p(snd_asihpi_remove),
diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h
index 255429c..f207272 100644
--- a/sound/pci/asihpi/hpi.h
+++ b/sound/pci/asihpi/hpi.h
@@ -1,7 +1,7 @@
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -42,12 +42,11 @@ i.e 3.05.02 is a development version
#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF))
#define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
-/* Use single digits for versions less that 10 to avoid octal. */
-#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 6, 0)
-#define HPI_VER_STRING "4.06.00"
+#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 8, 0)
+#define HPI_VER_STRING "4.08.00"
/* Library version as documented in hpi-api-versions.txt */
-#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(9, 0, 0)
+#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(10, 0, 0)
#include <linux/types.h>
#define HPI_BUILD_EXCLUDE_DEPRECATED
@@ -211,8 +210,12 @@ enum HPI_SOURCENODES {
HPI_SOURCENODE_COBRANET = 109,
HPI_SOURCENODE_ANALOG = 110, /**< analog input node. */
HPI_SOURCENODE_ADAPTER = 111, /**< adapter node. */
+ /** RTP stream input node - This node is a destination for
+ packets of RTP audio samples from other devices. */
+ HPI_SOURCENODE_RTP_DESTINATION = 112,
+ HPI_SOURCENODE_GP_IN = 113, /**< general purpose input. */
/* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */
- HPI_SOURCENODE_LAST_INDEX = 111 /**< largest ID */
+ HPI_SOURCENODE_LAST_INDEX = 113 /**< largest ID */
/* AX6 max sourcenode types = 15 */
};
@@ -228,7 +231,7 @@ enum HPI_DESTNODES {
HPI_DESTNODE_NONE = 200,
/** In Stream (Record) node. */
HPI_DESTNODE_ISTREAM = 201,
- HPI_DESTNODE_LINEOUT = 202, /**< line out node. */
+ HPI_DESTNODE_LINEOUT = 202, /**< line out node. */
HPI_DESTNODE_AESEBU_OUT = 203, /**< AES/EBU output node. */
HPI_DESTNODE_RF = 204, /**< RF output node. */
HPI_DESTNODE_SPEAKER = 205, /**< speaker output node. */
@@ -236,9 +239,12 @@ enum HPI_DESTNODES {
Audio samples from the device are sent out on the Cobranet network.*/
HPI_DESTNODE_COBRANET = 206,
HPI_DESTNODE_ANALOG = 207, /**< analog output node. */
-
+ /** RTP stream output node - This node is a source for
+ packets of RTP audio samples that are sent to other devices. */
+ HPI_DESTNODE_RTP_SOURCE = 208,
+ HPI_DESTNODE_GP_OUT = 209, /**< general purpose output node. */
/* !!!Update this AND hpidebug.h if you add a new destnode type!!! */
- HPI_DESTNODE_LAST_INDEX = 207 /**< largest ID */
+ HPI_DESTNODE_LAST_INDEX = 209 /**< largest ID */
/* AX6 max destnode types = 15 */
};
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index df4aed5..3cc6f11 100644
--- a/sound/pci/asihpi/hpi6000.c
+++ b/sound/pci/asihpi/hpi6000.c
@@ -359,7 +359,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
HPI_ERROR_PROCESSING_MESSAGE);
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
@@ -538,7 +538,7 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n");
memset(&hm, 0, sizeof(hm));
- hm.type = HPI_TYPE_MESSAGE;
+ hm.type = HPI_TYPE_REQUEST;
hm.size = sizeof(struct hpi_message);
hm.object = HPI_OBJ_ADAPTER;
hm.function = HPI_ADAPTER_GET_INFO;
@@ -946,11 +946,8 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
}
/* write the DSP code down into the DSPs memory */
- /*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */
- dsp_code.ps_dev = pao->pci.pci_dev;
-
- error = hpi_dsp_code_open(boot_load_family, &dsp_code,
- pos_error_code);
+ error = hpi_dsp_code_open(boot_load_family, pao->pci.pci_dev,
+ &dsp_code, pos_error_code);
if (error)
return error;
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index 9d5df54..e041a6a 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -373,6 +373,7 @@ static void instream_message(struct hpi_adapter_obj *pao,
/** Entry point to this HPI backend
* All calls to the HPI start here
*/
+static
void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
struct hpi_response *phr)
{
@@ -392,7 +393,7 @@ void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
HPI_DEBUG_LOG(VERBOSE, "start of switch\n");
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(pao, phm, phr);
@@ -402,7 +403,6 @@ void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
adapter_message(pao, phm, phr);
break;
- case HPI_OBJ_CONTROLEX:
case HPI_OBJ_CONTROL:
control_message(pao, phm, phr);
break;
@@ -634,11 +634,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n");
memset(&hm, 0, sizeof(hm));
- hm.type = HPI_TYPE_MESSAGE;
+ /* wAdapterIndex == version == 0 */
+ hm.type = HPI_TYPE_REQUEST;
hm.size = sizeof(hm);
hm.object = HPI_OBJ_ADAPTER;
hm.function = HPI_ADAPTER_GET_INFO;
- hm.adapter_index = 0;
+
memset(&hr, 0, sizeof(hr));
hr.size = sizeof(hr);
@@ -658,9 +659,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
hr.u.ax.info.num_outstreams +
hr.u.ax.info.num_instreams;
- hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams,
- 65536, pao->pci.pci_dev);
-
HPI_DEBUG_LOG(VERBOSE,
"got adapter info type %x index %d serial %d\n",
hr.u.ax.info.adapter_type, hr.u.ax.info.adapter_index,
@@ -709,9 +707,6 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
[i]);
phw->outstream_host_buffer_size[i] = 0;
}
-
- hpios_locked_mem_unprepare(pao->pci.pci_dev);
-
kfree(phw);
}
@@ -1371,9 +1366,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
return err;
/* write the DSP code down into the DSPs memory */
- dsp_code.ps_dev = pao->pci.pci_dev;
- err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code,
- pos_error_code);
+ err = hpi_dsp_code_open(boot_code_id[dsp], pao->pci.pci_dev,
+ &dsp_code, pos_error_code);
if (err)
return err;
@@ -2084,13 +2078,13 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
u16 err = 0;
message_count++;
- if (phm->size > sizeof(interface->u)) {
+ if (phm->size > sizeof(interface->u.message_buffer)) {
phr->error = HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
- phr->specific_error = sizeof(interface->u);
+ phr->specific_error = sizeof(interface->u.message_buffer);
phr->size = sizeof(struct hpi_response_header);
HPI_DEBUG_LOG(ERROR,
"message len %d too big for buffer %zd \n", phm->size,
- sizeof(interface->u));
+ sizeof(interface->u.message_buffer));
return 0;
}
@@ -2122,18 +2116,19 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
/* read the result */
if (time_out) {
- if (interface->u.response_buffer.size <= phr->size)
+ if (interface->u.response_buffer.response.size <= phr->size)
memcpy(phr, &interface->u.response_buffer,
- interface->u.response_buffer.size);
+ interface->u.response_buffer.response.size);
else {
HPI_DEBUG_LOG(ERROR,
"response len %d too big for buffer %d\n",
- interface->u.response_buffer.size, phr->size);
+ interface->u.response_buffer.response.size,
+ phr->size);
memcpy(phr, &interface->u.response_buffer,
sizeof(struct hpi_response_header));
phr->error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
phr->specific_error =
- interface->u.response_buffer.size;
+ interface->u.response_buffer.response.size;
phr->size = sizeof(struct hpi_response_header);
}
}
@@ -2202,23 +2197,6 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
phm->u.d.u.data.data_size, H620_HIF_GET_DATA);
break;
- case HPI_CONTROL_SET_STATE:
- if (phm->object == HPI_OBJ_CONTROLEX
- && phm->u.cx.attribute == HPI_COBRANET_SET_DATA)
- err = hpi6205_transfer_data(pao,
- phm->u.cx.u.cobranet_bigdata.pb_data,
- phm->u.cx.u.cobranet_bigdata.byte_count,
- H620_HIF_SEND_DATA);
- break;
-
- case HPI_CONTROL_GET_STATE:
- if (phm->object == HPI_OBJ_CONTROLEX
- && phm->u.cx.attribute == HPI_COBRANET_GET_DATA)
- err = hpi6205_transfer_data(pao,
- phm->u.cx.u.cobranet_bigdata.pb_data,
- phr->u.cx.u.cobranet_data.byte_count,
- H620_HIF_GET_DATA);
- break;
}
phr->error = err;
diff --git a/sound/pci/asihpi/hpi6205.h b/sound/pci/asihpi/hpi6205.h
index df2f02c..ec0827b 100644
--- a/sound/pci/asihpi/hpi6205.h
+++ b/sound/pci/asihpi/hpi6205.h
@@ -1,7 +1,7 @@
/*****************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -70,15 +70,28 @@ The Host located memory buffer that the 6205 will bus master
in and out of.
************************************************************/
#define HPI6205_SIZEOF_DATA (16*1024)
+
+struct message_buffer_6205 {
+ struct hpi_message message;
+ char data[256];
+};
+
+struct response_buffer_6205 {
+ struct hpi_response response;
+ char data[256];
+};
+
+union buffer_6205 {
+ struct message_buffer_6205 message_buffer;
+ struct response_buffer_6205 response_buffer;
+ u8 b_data[HPI6205_SIZEOF_DATA];
+};
+
struct bus_master_interface {
u32 host_cmd;
u32 dsp_ack;
u32 transfer_size_in_bytes;
- union {
- struct hpi_message_header message_buffer;
- struct hpi_response_header response_buffer;
- u8 b_data[HPI6205_SIZEOF_DATA];
- } u;
+ union buffer_6205 u;
struct controlcache_6205 control_cache;
struct async_event_buffer_6205 async_buffer;
struct hpi_hostbuffer_status
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index bf5eced..d497030 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -1,7 +1,7 @@
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -32,12 +32,6 @@ HPI internal definitions
#include "hpios.h"
/* physical memory allocation */
-void hpios_locked_mem_init(void
- );
-void hpios_locked_mem_free_all(void
- );
-#define hpios_locked_mem_prepare(a, b, c, d);
-#define hpios_locked_mem_unprepare(a)
/** Allocate and map an area of locked memory for bus master DMA operations.
@@ -226,8 +220,8 @@ enum HPI_CONTROL_ATTRIBUTES {
HPI_COBRANET_SET = HPI_CTL_ATTR(COBRANET, 1),
HPI_COBRANET_GET = HPI_CTL_ATTR(COBRANET, 2),
- HPI_COBRANET_SET_DATA = HPI_CTL_ATTR(COBRANET, 3),
- HPI_COBRANET_GET_DATA = HPI_CTL_ATTR(COBRANET, 4),
+ /*HPI_COBRANET_SET_DATA = HPI_CTL_ATTR(COBRANET, 3), */
+ /*HPI_COBRANET_GET_DATA = HPI_CTL_ATTR(COBRANET, 4), */
HPI_COBRANET_GET_STATUS = HPI_CTL_ATTR(COBRANET, 5),
HPI_COBRANET_SEND_PACKET = HPI_CTL_ATTR(COBRANET, 6),
HPI_COBRANET_GET_PACKET = HPI_CTL_ATTR(COBRANET, 7),
@@ -364,10 +358,12 @@ Used in DLL to indicate device not present
#define HPI_ADAPTER_ASI(f) (f)
enum HPI_MESSAGE_TYPES {
- HPI_TYPE_MESSAGE = 1,
+ HPI_TYPE_REQUEST = 1,
HPI_TYPE_RESPONSE = 2,
HPI_TYPE_DATA = 3,
- HPI_TYPE_SSX2BYPASS_MESSAGE = 4
+ HPI_TYPE_SSX2BYPASS_MESSAGE = 4,
+ HPI_TYPE_COMMAND = 5,
+ HPI_TYPE_NOTIFICATION = 6
};
enum HPI_OBJECT_TYPES {
@@ -383,7 +379,7 @@ enum HPI_OBJECT_TYPES {
HPI_OBJ_WATCHDOG = 10,
HPI_OBJ_CLOCK = 11,
HPI_OBJ_PROFILE = 12,
- HPI_OBJ_CONTROLEX = 13,
+ /* HPI_ OBJ_ CONTROLEX = 13, */
HPI_OBJ_ASYNCEVENT = 14
#define HPI_OBJ_MAXINDEX 14
};
@@ -608,7 +604,7 @@ struct hpi_data_compat32 {
#endif
struct hpi_buffer {
- /** placehoder for backward compatibility (see dwBufferSize) */
+ /** placeholder for backward compatibility (see dwBufferSize) */
struct hpi_msg_format reserved;
u32 command; /**< HPI_BUFFER_CMD_xxx*/
u32 pci_address; /**< PCI physical address of buffer for DSP DMA */
@@ -912,95 +908,13 @@ union hpi_control_union_res {
u32 remaining_chars;
} chars8;
char c_data12[12];
-};
-
-/* HPI_CONTROLX_STRUCTURES */
-
-/* Message */
-
-/** Used for all HMI variables where max length <= 8 bytes
-*/
-struct hpi_controlx_msg_cobranet_data {
- u32 hmi_address;
- u32 byte_count;
- u32 data[2];
-};
-
-/** Used for string data, and for packet bridge
-*/
-struct hpi_controlx_msg_cobranet_bigdata {
- u32 hmi_address;
- u32 byte_count;
- u8 *pb_data;
-#ifndef HPI64BIT
- u32 padding;
-#endif
-};
-
-/** Used for PADS control reading of string fields.
-*/
-struct hpi_controlx_msg_pad_data {
- u32 field;
- u32 byte_count;
- u8 *pb_data;
-#ifndef HPI64BIT
- u32 padding;
-#endif
-};
-
-/** Used for generic data
-*/
-
-struct hpi_controlx_msg_generic {
- u32 param1;
- u32 param2;
-};
-
-struct hpi_controlx_msg {
- u16 attribute; /* control attribute or property */
- u16 saved_index;
- union {
- struct hpi_controlx_msg_cobranet_data cobranet_data;
- struct hpi_controlx_msg_cobranet_bigdata cobranet_bigdata;
- struct hpi_controlx_msg_generic generic;
- struct hpi_controlx_msg_pad_data pad_data;
- /*struct param_value universal_value; */
- /* nothing extra to send for status read */
- } u;
-};
-
-/* Response */
-/**
-*/
-struct hpi_controlx_res_cobranet_data {
- u32 byte_count;
- u32 data[2];
-};
-
-struct hpi_controlx_res_cobranet_bigdata {
- u32 byte_count;
-};
-
-struct hpi_controlx_res_cobranet_status {
- u32 status;
- u32 readable_size;
- u32 writeable_size;
-};
-
-struct hpi_controlx_res_generic {
- u32 param1;
- u32 param2;
-};
-
-struct hpi_controlx_res {
union {
- struct hpi_controlx_res_cobranet_bigdata cobranet_bigdata;
- struct hpi_controlx_res_cobranet_data cobranet_data;
- struct hpi_controlx_res_cobranet_status cobranet_status;
- struct hpi_controlx_res_generic generic;
- /*struct param_info universal_info; */
- /*struct param_value universal_value; */
- } u;
+ struct {
+ u32 status;
+ u32 readable_size;
+ u32 writeable_size;
+ } status;
+ } cobranet;
};
struct hpi_nvmemory_msg {
@@ -1126,7 +1040,6 @@ struct hpi_message {
/* identical to struct hpi_control_msg,
but field naming is improved */
struct hpi_control_union_msg cu;
- struct hpi_controlx_msg cx; /* extended mixer control; */
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l; /* digital i/o */
struct hpi_watchdog_msg w;
@@ -1151,7 +1064,7 @@ struct hpi_message {
sizeof(struct hpi_message_header) + sizeof(struct hpi_watchdog_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_clock_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_profile_msg),\
- sizeof(struct hpi_message_header) + sizeof(struct hpi_controlx_msg),\
+ sizeof(struct hpi_message_header), /* controlx obj removed */ \
sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \
}
@@ -1188,7 +1101,6 @@ struct hpi_response {
struct hpi_control_res c; /* mixer control; */
/* identical to hpi_control_res, but field naming is improved */
union hpi_control_union_res cu;
- struct hpi_controlx_res cx; /* extended mixer control; */
struct hpi_nvmemory_res n;
struct hpi_gpio_res l; /* digital i/o */
struct hpi_watchdog_res w;
@@ -1213,7 +1125,7 @@ struct hpi_response {
sizeof(struct hpi_response_header) + sizeof(struct hpi_watchdog_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_clock_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_profile_res),\
- sizeof(struct hpi_response_header) + sizeof(struct hpi_controlx_res),\
+ sizeof(struct hpi_response_header), /* controlx obj removed */ \
sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \
}
@@ -1308,6 +1220,30 @@ struct hpi_res_adapter_debug_read {
u8 bytes[256];
};
+struct hpi_msg_cobranet_hmi {
+ u16 attribute;
+ u16 padding;
+ u32 hmi_address;
+ u32 byte_count;
+};
+
+struct hpi_msg_cobranet_hmiwrite {
+ struct hpi_message_header h;
+ struct hpi_msg_cobranet_hmi p;
+ u8 bytes[256];
+};
+
+struct hpi_msg_cobranet_hmiread {
+ struct hpi_message_header h;
+ struct hpi_msg_cobranet_hmi p;
+};
+
+struct hpi_res_cobranet_hmiread {
+ struct hpi_response_header h;
+ u32 byte_count;
+ u8 bytes[256];
+};
+
#if 1
#define hpi_message_header_v1 hpi_message_header
#define hpi_response_header_v1 hpi_response_header
@@ -1338,7 +1274,6 @@ struct hpi_msg_payload_v0 {
union hpi_mixerx_msg mx;
struct hpi_control_msg c;
struct hpi_control_union_msg cu;
- struct hpi_controlx_msg cx;
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l;
struct hpi_watchdog_msg w;
@@ -1358,7 +1293,6 @@ struct hpi_res_payload_v0 {
union hpi_mixerx_res mx;
struct hpi_control_res c;
union hpi_control_union_res cu;
- struct hpi_controlx_res cx;
struct hpi_nvmemory_res n;
struct hpi_gpio_res l;
struct hpi_watchdog_res w;
@@ -1493,12 +1427,6 @@ struct hpi_control_cache_microphone {
char temp_padding[6];
};
-struct hpi_control_cache_generic {
- struct hpi_control_cache_info i;
- u32 dw1;
- u32 dw2;
-};
-
struct hpi_control_cache_single {
union {
struct hpi_control_cache_info i;
@@ -1514,7 +1442,6 @@ struct hpi_control_cache_single {
struct hpi_control_cache_silencedetector silence;
struct hpi_control_cache_sampleclock clk;
struct hpi_control_cache_microphone microphone;
- struct hpi_control_cache_generic generic;
} u;
};
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index b15a02e..65b7ca1 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -57,7 +57,7 @@ u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)
}
if (phr->function != phm->function) {
- HPI_DEBUG_LOG(ERROR, "header type %d invalid\n",
+ HPI_DEBUG_LOG(ERROR, "header function %d invalid\n",
phr->function);
return HPI_ERROR_INVALID_RESPONSE;
}
@@ -315,8 +315,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
short found = 1;
struct hpi_control_cache_info *pI;
struct hpi_control_cache_single *pC;
- struct hpi_control_cache_pad *p_pad;
-
+ size_t response_size;
if (!find_control(phm->obj_index, p_cache, &pI)) {
HPI_DEBUG_LOG(VERBOSE,
"HPICMN find_control() failed for adap %d\n",
@@ -326,11 +325,15 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
phr->error = 0;
+ /* set the default response size */
+ response_size =
+ sizeof(struct hpi_response_header) +
+ sizeof(struct hpi_control_res);
+
/* pC is the default cached control strucure. May be cast to
something else in the following switch statement.
*/
pC = (struct hpi_control_cache_single *)pI;
- p_pad = (struct hpi_control_cache_pad *)pI;
switch (pI->control_type) {
@@ -529,9 +532,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
pI->control_index, pI->control_type, phm->u.c.attribute);
if (found)
- phr->size =
- sizeof(struct hpi_response_header) +
- sizeof(struct hpi_control_res);
+ phr->size = (u16)response_size;
return found;
}
@@ -682,7 +683,7 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c
index 5c6ea11..3a7afa3 100644
--- a/sound/pci/asihpi/hpidspcd.c
+++ b/sound/pci/asihpi/hpidspcd.c
@@ -1,8 +1,8 @@
/***********************************************************************/
-/*!
+/**
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -18,90 +18,59 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file
-Functions for reading DSP code to load into DSP
-
-(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using
+Functions for reading DSP code using
hotplug firmware loader from individual dsp code files
-
-If neither of the above is defined, code is read from linked arrays.
-DSPCODE_ARRAY is defined.
-
-HPI_INCLUDE_**** must be defined
-and the appropriate hzz?????.c or hex?????.c linked in
-
- */
+*/
/***********************************************************************/
#define SOURCEFILE_NAME "hpidspcd.c"
#include "hpidspcd.h"
#include "hpidebug.h"
-/**
- Header structure for binary dsp code file (see asidsp.doc)
- This structure must match that used in s2bin.c for generation of asidsp.bin
- */
-
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(push, 1)
-#endif
-
-struct code_header {
- u32 size;
- char type[4];
- u32 adapter;
- u32 version;
- u32 crc;
+struct dsp_code_private {
+ /** Firmware descriptor */
+ const struct firmware *firmware;
+ struct pci_dev *dev;
};
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(pop)
-#endif
-
#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \
HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER)))
-/***********************************************************************/
-#include <linux/pci.h>
/*-------------------------------------------------------------------*/
-short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
- u32 *pos_error_code)
+short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code,
+ u32 *os_error_code)
{
- const struct firmware *ps_firmware = ps_dsp_code->ps_firmware;
+ const struct firmware *firmware;
+ struct pci_dev *dev = os_data;
struct code_header header;
char fw_name[20];
int err;
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
- err = request_firmware(&ps_firmware, fw_name,
- &ps_dsp_code->ps_dev->dev);
+ err = request_firmware(&firmware, fw_name, &dev->dev);
- if (err != 0) {
- dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
+ if (err || !firmware) {
+ dev_printk(KERN_ERR, &dev->dev,
"%d, request_firmware failed for %s\n", err,
fw_name);
goto error1;
}
- if (ps_firmware->size < sizeof(header)) {
- dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
- "Header size too small %s\n", fw_name);
- goto error2;
- }
- memcpy(&header, ps_firmware->data, sizeof(header));
- if (header.adapter != adapter) {
- dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
- "Adapter type incorrect %4x != %4x\n", header.adapter,
- adapter);
+ if (firmware->size < sizeof(header)) {
+ dev_printk(KERN_ERR, &dev->dev, "Header size too small %s\n",
+ fw_name);
goto error2;
}
- if (header.size != ps_firmware->size) {
- dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
- "Code size wrong %d != %ld\n", header.size,
- (unsigned long)ps_firmware->size);
+ memcpy(&header, firmware->data, sizeof(header));
+
+ if ((header.type != 0x45444F43) || /* "CODE" */
+ (header.adapter != adapter)
+ || (header.size != firmware->size)) {
+ dev_printk(KERN_ERR, &dev->dev, "Invalid firmware file\n");
goto error2;
}
- if (header.version / 100 != HPI_VER_DECIMAL / 100) {
- dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
+ if ((header.version / 100 & ~1) != (HPI_VER_DECIMAL / 100 & ~1)) {
+ dev_printk(KERN_ERR, &dev->dev,
"Incompatible firmware version "
"DSP image %d != Driver %d\n", header.version,
HPI_VER_DECIMAL);
@@ -109,67 +78,70 @@ short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
}
if (header.version != HPI_VER_DECIMAL) {
- dev_printk(KERN_WARNING, &ps_dsp_code->ps_dev->dev,
+ dev_printk(KERN_WARNING, &dev->dev,
"Firmware: release version mismatch DSP image %d != Driver %d\n",
header.version, HPI_VER_DECIMAL);
}
HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name);
- ps_dsp_code->ps_firmware = ps_firmware;
- ps_dsp_code->block_length = header.size / sizeof(u32);
- ps_dsp_code->word_count = sizeof(header) / sizeof(u32);
- ps_dsp_code->version = header.version;
- ps_dsp_code->crc = header.crc;
+ dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL);
+ if (!dsp_code->pvt)
+ return HPI_ERROR_MEMORY_ALLOC;
+
+ dsp_code->pvt->dev = dev;
+ dsp_code->pvt->firmware = firmware;
+ dsp_code->header = header;
+ dsp_code->block_length = header.size / sizeof(u32);
+ dsp_code->word_count = sizeof(header) / sizeof(u32);
return 0;
error2:
- release_firmware(ps_firmware);
+ release_firmware(firmware);
error1:
- ps_dsp_code->ps_firmware = NULL;
- ps_dsp_code->block_length = 0;
+ dsp_code->block_length = 0;
return HPI_ERROR_DSP_FILE_NOT_FOUND;
}
/*-------------------------------------------------------------------*/
-void hpi_dsp_code_close(struct dsp_code *ps_dsp_code)
+void hpi_dsp_code_close(struct dsp_code *dsp_code)
{
- if (ps_dsp_code->ps_firmware != NULL) {
+ if (dsp_code->pvt->firmware) {
HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
- release_firmware(ps_dsp_code->ps_firmware);
- ps_dsp_code->ps_firmware = NULL;
+ release_firmware(dsp_code->pvt->firmware);
+ dsp_code->pvt->firmware = NULL;
}
+ kfree(dsp_code->pvt);
}
/*-------------------------------------------------------------------*/
-void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code)
+void hpi_dsp_code_rewind(struct dsp_code *dsp_code)
{
/* Go back to start of data, after header */
- ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
+ dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
}
/*-------------------------------------------------------------------*/
-short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword)
+short hpi_dsp_code_read_word(struct dsp_code *dsp_code, u32 *pword)
{
- if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length)
+ if (dsp_code->word_count + 1 > dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
- *pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code->
+ *pword = ((u32 *)(dsp_code->pvt->firmware->data))[dsp_code->
word_count];
- ps_dsp_code->word_count++;
+ dsp_code->word_count++;
return 0;
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_block(size_t words_requested,
- struct dsp_code *ps_dsp_code, u32 **ppblock)
+ struct dsp_code *dsp_code, u32 **ppblock)
{
- if (ps_dsp_code->word_count + words_requested >
- ps_dsp_code->block_length)
+ if (dsp_code->word_count + words_requested > dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
*ppblock =
- ((u32 *)(ps_dsp_code->ps_firmware->data)) +
- ps_dsp_code->word_count;
- ps_dsp_code->word_count += words_requested;
+ ((u32 *)(dsp_code->pvt->firmware->data)) +
+ dsp_code->word_count;
+ dsp_code->word_count += words_requested;
return 0;
}
diff --git a/sound/pci/asihpi/hpidspcd.h b/sound/pci/asihpi/hpidspcd.h
index 65f0ca7..b228811 100644
--- a/sound/pci/asihpi/hpidspcd.h
+++ b/sound/pci/asihpi/hpidspcd.h
@@ -2,7 +2,7 @@
/**
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -20,19 +20,6 @@
\file
Functions for reading DSP code to load into DSP
- hpi_dspcode_defines HPI DSP code loading method
-Define exactly one of these to select how the DSP code is supplied to
-the adapter.
-
-End users writing applications that use the HPI interface do not have to
-use any of the below defines; they are only necessary for building drivers
-
-HPI_DSPCODE_FILE:
-DSP code is supplied as a file that is opened and read from by the driver.
-
-HPI_DSPCODE_FIRMWARE:
-DSP code is read using the hotplug firmware loader module.
- Only valid when compiling the HPI kernel driver under Linux.
*/
/***********************************************************************/
#ifndef _HPIDSPCD_H_
@@ -40,37 +27,56 @@ DSP code is read using the hotplug firmware loader module.
#include "hpi_internal.h"
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(push, 1)
-#endif
+/** Code header version is decimal encoded e.g. 4.06.10 is 40601 */
+#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \
+HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER)))
+
+/** Header structure for dsp firmware file
+ This structure must match that used in s2bin.c for generation of asidsp.bin
+ */
+/*#ifndef DISABLE_PRAGMA_PACK1 */
+/*#pragma pack(push, 1) */
+/*#endif */
+struct code_header {
+ /** Size in bytes including header */
+ u32 size;
+ /** File type tag "CODE" == 0x45444F43 */
+ u32 type;
+ /** Adapter model number */
+ u32 adapter;
+ /** Firmware version*/
+ u32 version;
+ /** Data checksum */
+ u32 checksum;
+};
+/*#ifndef DISABLE_PRAGMA_PACK1 */
+/*#pragma pack(pop) */
+/*#endif */
+
+/*? Don't need the pragmas? */
+compile_time_assert((sizeof(struct code_header) == 20), code_header_size);
/** Descriptor for dspcode from firmware loader */
struct dsp_code {
- /** Firmware descriptor */
- const struct firmware *ps_firmware;
- struct pci_dev *ps_dev;
+ /** copy of file header */
+ struct code_header header;
/** Expected number of words in the whole dsp code,INCL header */
- long int block_length;
+ u32 block_length;
/** Number of words read so far */
- long int word_count;
- /** Version read from dsp code file */
- u32 version;
- /** CRC read from dsp code file */
- u32 crc;
-};
+ u32 word_count;
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(pop)
-#endif
+ /** internal state of DSP code reader */
+ struct dsp_code_private *pvt;
+};
-/** Prepare *psDspCode to refer to the requuested adapter.
- Searches the file, or selects the appropriate linked array
+/** Prepare *psDspCode to refer to the requested adapter's firmware.
+Code file name is obtained from HpiOs_GetDspCodePath
\return 0 for success, or error code if requested code is not available
*/
short hpi_dsp_code_open(
/** Code identifier, usually adapter family */
- u32 adapter,
+ u32 adapter, void *pci_dev,
/** Pointer to DSP code control structure */
struct dsp_code *ps_dsp_code,
/** Pointer to dword to receive OS specific error code */
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index 7397b16..ebb568d 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -1663,68 +1663,64 @@ u16 hpi_channel_mode_get(u32 h_control, u16 *mode)
u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count,
u8 *pb_data)
{
- struct hpi_message hm;
- struct hpi_response hr;
+ struct hpi_msg_cobranet_hmiwrite hm;
+ struct hpi_response_header hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
- HPI_CONTROL_SET_STATE);
- if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
- return HPI_ERROR_INVALID_HANDLE;
+ hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr, sizeof(hr),
+ HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
- hm.u.cx.u.cobranet_data.byte_count = byte_count;
- hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
+ if (hpi_handle_indexes(h_control, &hm.h.adapter_index,
+ &hm.h.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
- if (byte_count <= 8) {
- memcpy(hm.u.cx.u.cobranet_data.data, pb_data, byte_count);
- hm.u.cx.attribute = HPI_COBRANET_SET;
- } else {
- hm.u.cx.u.cobranet_bigdata.pb_data = pb_data;
- hm.u.cx.attribute = HPI_COBRANET_SET_DATA;
- }
+ if (byte_count > sizeof(hm.bytes))
+ return HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
- hpi_send_recv(&hm, &hr);
+ hm.p.attribute = HPI_COBRANET_SET;
+ hm.p.byte_count = byte_count;
+ hm.p.hmi_address = hmi_address;
+ memcpy(hm.bytes, pb_data, byte_count);
+ hm.h.size = (u16)(sizeof(hm.h) + sizeof(hm.p) + byte_count);
+ hpi_send_recvV1(&hm.h, &hr);
return hr.error;
}
u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count,
u32 *pbyte_count, u8 *pb_data)
{
- struct hpi_message hm;
- struct hpi_response hr;
+ struct hpi_msg_cobranet_hmiread hm;
+ struct hpi_res_cobranet_hmiread hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
- HPI_CONTROL_GET_STATE);
- if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr.h, sizeof(hr),
+ HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
+
+ if (hpi_handle_indexes(h_control, &hm.h.adapter_index,
+ &hm.h.obj_index))
return HPI_ERROR_INVALID_HANDLE;
- hm.u.cx.u.cobranet_data.byte_count = max_byte_count;
- hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
+ if (max_byte_count > sizeof(hr.bytes))
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- if (max_byte_count <= 8) {
- hm.u.cx.attribute = HPI_COBRANET_GET;
- } else {
- hm.u.cx.u.cobranet_bigdata.pb_data = pb_data;
- hm.u.cx.attribute = HPI_COBRANET_GET_DATA;
- }
+ hm.p.attribute = HPI_COBRANET_GET;
+ hm.p.byte_count = max_byte_count;
+ hm.p.hmi_address = hmi_address;
- hpi_send_recv(&hm, &hr);
- if (!hr.error && pb_data) {
+ hpi_send_recvV1(&hm.h, &hr.h);
- *pbyte_count = hr.u.cx.u.cobranet_data.byte_count;
+ if (!hr.h.error && pb_data) {
+ if (hr.byte_count > sizeof(hr.bytes))
- if (*pbyte_count < max_byte_count)
- max_byte_count = *pbyte_count;
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- if (hm.u.cx.attribute == HPI_COBRANET_GET) {
- memcpy(pb_data, hr.u.cx.u.cobranet_data.data,
- max_byte_count);
- } else {
+ *pbyte_count = hr.byte_count;
- }
+ if (hr.byte_count < max_byte_count)
+ max_byte_count = *pbyte_count;
+ memcpy(pb_data, hr.bytes, max_byte_count);
}
- return hr.error;
+ return hr.h.error;
}
u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
@@ -1733,23 +1729,23 @@ u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
struct hpi_message hm;
struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
return HPI_ERROR_INVALID_HANDLE;
- hm.u.cx.attribute = HPI_COBRANET_GET_STATUS;
+ hm.u.c.attribute = HPI_COBRANET_GET_STATUS;
hpi_send_recv(&hm, &hr);
if (!hr.error) {
if (pstatus)
- *pstatus = hr.u.cx.u.cobranet_status.status;
+ *pstatus = hr.u.cu.cobranet.status.status;
if (preadable_size)
*preadable_size =
- hr.u.cx.u.cobranet_status.readable_size;
+ hr.u.cu.cobranet.status.readable_size;
if (pwriteable_size)
*pwriteable_size =
- hr.u.cx.u.cobranet_status.writeable_size;
+ hr.u.cu.cobranet.status.writeable_size;
}
return hr.error;
}
diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c
index 628376c..52400a6 100644
--- a/sound/pci/asihpi/hpimsginit.c
+++ b/sound/pci/asihpi/hpimsginit.c
@@ -46,7 +46,7 @@ static void hpi_init_message(struct hpi_message *phm, u16 object,
if (gwSSX2_bypass)
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
else
- phm->type = HPI_TYPE_MESSAGE;
+ phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 0;
@@ -89,7 +89,7 @@ static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
memset(phm, 0, sizeof(*phm));
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
phm->size = size;
- phm->type = HPI_TYPE_MESSAGE;
+ phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 1;
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index 7352a5f..2e77942 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -16,7 +16,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-Extended Message Function With Response Cacheing
+Extended Message Function With Response Caching
(C) Copyright AudioScience Inc. 2002
*****************************************************************************/
@@ -186,7 +186,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
/* Initialize this module's internal state */
hpios_msgxlock_init(&msgx_lock);
memset(&hpi_entry_points, 0, sizeof(hpi_entry_points));
- hpios_locked_mem_init();
/* Init subsys_findadapters response to no-adapters */
HPIMSGX__reset(HPIMSGX_ALLADAPTERS);
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
@@ -197,7 +196,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
case HPI_SUBSYS_DRIVER_UNLOAD:
HPI_COMMON(phm, phr);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
- hpios_locked_mem_free_all();
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD, 0);
return;
@@ -315,7 +313,7 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
{
HPI_DEBUG_MESSAGE(DEBUG, phm);
- if (phm->type != HPI_TYPE_MESSAGE) {
+ if (phm->type != HPI_TYPE_REQUEST) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_INVALID_TYPE);
return;
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index d8e7047..65fcf47 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -1,7 +1,7 @@
/*******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -157,11 +157,6 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
- if (hm->h.adapter_index >= HPI_MAX_ADAPTERS) {
- err = -EINVAL;
- goto out;
- }
-
switch (hm->h.function) {
case HPI_SUBSYS_CREATE_ADAPTER:
case HPI_ADAPTER_DELETE:
@@ -187,7 +182,6 @@ 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 ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) {
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
@@ -203,6 +197,8 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
+ pa = &adapters[adapter];
+
if (mutex_lock_interruptible(&adapters[adapter].mutex)) {
err = -EINTR;
goto out;
diff --git a/sound/pci/asihpi/hpios.c b/sound/pci/asihpi/hpios.c
index 742ee12..ff2a19b 100644
--- a/sound/pci/asihpi/hpios.c
+++ b/sound/pci/asihpi/hpios.c
@@ -39,10 +39,6 @@ void hpios_delay_micro_seconds(u32 num_micro_sec)
}
-void hpios_locked_mem_init(void)
-{
-}
-
/** Allocated an area of locked memory for bus master DMA operations.
On error, return -ENOMEM, and *pMemArea.size = 0
@@ -85,7 +81,3 @@ u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
return 1;
}
}
-
-void hpios_locked_mem_free_all(void)
-{
-}
diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h
index 03273e7..2f605e3 100644
--- a/sound/pci/asihpi/hpios.h
+++ b/sound/pci/asihpi/hpios.h
@@ -38,6 +38,7 @@ HPI Operating System Specific macros for Linux Kernel driver
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
+#include <linux/mutex.h>
#define HPI_NO_OS_FILE_OPS
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 3119cd9..537e0a2 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1624,7 +1624,7 @@ static int __devinit snd_atiixp_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_atiixp_free(chip);
return -EBUSY;
@@ -1701,7 +1701,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "ATI IXP AC97 controller",
+ .name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
.remove = __devexit_p(snd_atiixp_remove),
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 2f74c2f..45df275 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1260,7 +1260,7 @@ static int __devinit snd_atiixp_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_atiixp_free(chip);
return -EBUSY;
@@ -1332,7 +1332,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "ATI IXP MC97 controller",
+ .name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
.remove = __devexit_p(snd_atiixp_remove),
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 7b72c88..a384699 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -196,7 +196,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
}
if ((err = request_irq(pci->irq, vortex_interrupt,
- IRQF_SHARED, CARD_NAME_SHORT,
+ IRQF_SHARED, KBUILD_MODNAME,
chip)) != 0) {
printk(KERN_ERR "cannot grab irq\n");
goto irq_out;
@@ -375,7 +375,7 @@ static void __devexit snd_vortex_remove(struct pci_dev *pci)
// pci_driver definition
static struct pci_driver driver = {
- .name = CARD_NAME_SHORT,
+ .name = KBUILD_MODNAME,
.id_table = snd_vortex_ids,
.probe = snd_vortex_probe,
.remove = __devexit_p(snd_vortex_remove),
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index c150022..f8569b1 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -171,7 +171,7 @@ MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
/* pci_driver definition */
static struct pci_driver driver = {
- .name = "Emagic Audiowerk 2",
+ .name = KBUILD_MODNAME,
.id_table = snd_aw2_ids,
.probe = snd_aw2_probe,
.remove = __devexit_p(snd_aw2_remove),
@@ -317,7 +317,7 @@ static int __devinit snd_aw2_create(struct snd_card *card,
snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt);
if (request_irq(pci->irq, snd_aw2_saa7146_interrupt,
- IRQF_SHARED, "Audiowerk2", chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq);
iounmap(chip->iobase_virt);
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 9b7a634..e4d76a2 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -2559,7 +2559,7 @@ snd_azf3328_create(struct snd_card *card,
codec_setup->name = "I2S_OUT";
if (request_irq(pci->irq, snd_azf3328_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
goto out_err;
@@ -2860,7 +2860,7 @@ snd_azf3328_resume(struct pci_dev *pci)
static struct pci_driver driver = {
- .name = "AZF3328",
+ .name = KBUILD_MODNAME,
.id_table = snd_azf3328_ids,
.probe = snd_azf3328_probe,
.remove = __devexit_p(snd_azf3328_remove),
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 2958a05..3918033 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -760,7 +760,7 @@ static int __devinit snd_bt87x_create(struct snd_card *card,
snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
err = request_irq(pci->irq, snd_bt87x_interrupt, IRQF_SHARED,
- "Bt87x audio", chip);
+ KBUILD_MODNAME, chip);
if (err < 0) {
snd_printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
goto fail;
@@ -965,7 +965,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = {
};
static struct pci_driver driver = {
- .name = "Bt87x",
+ .name = KBUILD_MODNAME,
.id_table = snd_bt87x_ids,
.probe = snd_bt87x_probe,
.remove = __devexit_p(snd_bt87x_remove),
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 4377592..061b7e6 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1666,7 +1666,7 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
}
if (request_irq(pci->irq, snd_ca0106_interrupt,
- IRQF_SHARED, "snd_ca0106", chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
snd_ca0106_free(chip);
printk(KERN_ERR "cannot grab irq\n");
return -EBUSY;
@@ -1933,7 +1933,7 @@ MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
// pci_driver definition
static struct pci_driver driver = {
- .name = "CA0106",
+ .name = KBUILD_MODNAME,
.id_table = snd_ca0106_ids,
.probe = snd_ca0106_probe,
.remove = __devexit_p(snd_ca0106_remove),
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index f4e5735..9cf99fb 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -3053,7 +3053,7 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
cm->iobase = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_cmipci_interrupt,
- IRQF_SHARED, card->driver, cm)) {
+ IRQF_SHARED, KBUILD_MODNAME, cm)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_cmipci_free(cm);
return -EBUSY;
@@ -3398,7 +3398,7 @@ static int snd_cmipci_resume(struct pci_dev *pci)
#endif /* CONFIG_PM */
static struct pci_driver driver = {
- .name = "C-Media PCI",
+ .name = KBUILD_MODNAME,
.id_table = snd_cmipci_ids,
.probe = snd_cmipci_probe,
.remove = __devexit_p(snd_cmipci_remove),
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 6772070..07f04e3 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1382,7 +1382,7 @@ static int __devinit snd_cs4281_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_SHARED,
- "CS4281", chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_cs4281_free(chip);
return -ENOMEM;
@@ -2085,7 +2085,7 @@ static int cs4281_resume(struct pci_dev *pci)
#endif /* CONFIG_PM */
static struct pci_driver driver = {
- .name = "CS4281",
+ .name = KBUILD_MODNAME,
.id_table = snd_cs4281_ids,
.probe = snd_cs4281_probe,
.remove = __devexit_p(snd_cs4281_remove),
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 767fa7f..1af9555 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -162,7 +162,7 @@ static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Sound Fusion CS46xx",
+ .name = KBUILD_MODNAME,
.id_table = snd_cs46xx_ids,
.probe = snd_card_cs46xx_probe,
.remove = __devexit_p(snd_card_cs46xx_remove),
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index aad3708..9546bf0 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -3835,7 +3835,7 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_cs46xx_interrupt, IRQF_SHARED,
- "CS46XX", chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_cs46xx_free(chip);
return -EBUSY;
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index bc07e27..a466934 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -285,7 +285,7 @@ static int __devinit snd_cs5530_probe(struct pci_dev *pci,
}
static struct pci_driver driver = {
- .name = "CS5530_Audio",
+ .name = KBUILD_MODNAME,
.id_table = snd_cs5530_ids,
.probe = snd_cs5530_probe,
.remove = __devexit_p(snd_cs5530_remove),
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index afb8037..10d22ed 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -311,7 +311,7 @@ static int __devinit snd_cs5535audio_create(struct snd_card *card,
cs5535au->port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_cs5535audio_interrupt,
- IRQF_SHARED, "CS5535 Audio", cs5535au)) {
+ IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
goto sndfail;
@@ -395,7 +395,7 @@ static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = DRIVER_NAME,
+ .name = KBUILD_MODNAME,
.id_table = snd_cs5535audio_ids,
.probe = snd_cs5535audio_probe,
.remove = __devexit_p(snd_cs5535audio_remove),
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
index e0394e3..ca501ba 100644
--- a/sound/pci/ctxfi/ct20k2reg.h
+++ b/sound/pci/ctxfi/ct20k2reg.h
@@ -55,6 +55,7 @@
/* GPIO Registers */
#define GPIO_DATA 0x1B7020
#define GPIO_CTRL 0x1B7024
+#define GPIO_EXT_DATA 0x1B70A0
/* Virtual memory registers */
#define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 13f33c0..d8a4423 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -18,7 +18,6 @@
#include "ctatc.h"
#include "ctpcm.h"
#include "ctmixer.h"
-#include "cthardware.h"
#include "ctsrc.h"
#include "ctamixer.h"
#include "ctdaio.h"
@@ -30,7 +29,6 @@
#include <sound/asoundef.h>
#define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */
-#define DAIONUM 7
#define MAX_MULTI_CHN 8
#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
@@ -53,6 +51,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
"SB0760", CTSB0760),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270,
+ "SB1270", CTSB1270),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
"SB0880", CTSB0880),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
@@ -75,6 +75,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = {
[CTSB0760] = "SB076x",
[CTHENDRIX] = "Hendrix",
[CTSB0880] = "SB0880",
+ [CTSB1270] = "SB1270",
[CT20K2_UNKNOWN] = "Unknown",
};
@@ -459,12 +460,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
apcm->substream->runtime->rate);
*n_srcc = 0;
- if (1 == atc->msr) {
+ if (1 == atc->msr) { /* FIXME: do we really need SRC here if pitch==1 */
*n_srcc = apcm->substream->runtime->channels;
conf[0].pitch = pitch;
conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
conf[0].vo = 1;
- } else if (2 == atc->msr) {
+ } else if (2 <= atc->msr) {
if (0x8000000 < pitch) {
/* Need two-stage SRCs, SRCIMPs and
* AMIXERs for converting format */
@@ -970,11 +971,39 @@ static int atc_select_mic_in(struct ct_atc *atc)
return 0;
}
-static int atc_have_digit_io_switch(struct ct_atc *atc)
+static struct capabilities atc_capabilities(struct ct_atc *atc)
{
struct hw *hw = atc->hw;
- return hw->have_digit_io_switch(hw);
+ return hw->capabilities(hw);
+}
+
+static int atc_output_switch_get(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->output_switch_get(hw);
+}
+
+static int atc_output_switch_put(struct ct_atc *atc, int position)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->output_switch_put(hw, position);
+}
+
+static int atc_mic_source_switch_get(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->mic_source_switch_get(hw);
+}
+
+static int atc_mic_source_switch_put(struct ct_atc *atc, int position)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->mic_source_switch_put(hw, position);
}
static int atc_select_digit_io(struct ct_atc *atc)
@@ -1045,6 +1074,11 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
return atc_daio_unmute(atc, state, LINEIM);
}
+static int atc_mic_unmute(struct ct_atc *atc, unsigned char state)
+{
+ return atc_daio_unmute(atc, state, MIC);
+}
+
static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
{
return atc_daio_unmute(atc, state, SPDIFOO);
@@ -1331,17 +1365,20 @@ static int atc_get_resources(struct ct_atc *atc)
struct srcimp_mgr *srcimp_mgr;
struct sum_desc sum_dsc = {0};
struct sum_mgr *sum_mgr;
- int err, i;
+ int err, i, num_srcs, num_daios;
- atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+ num_daios = ((atc->model == CTSB1270) ? 8 : 7);
+ num_srcs = ((atc->model == CTSB1270) ? 6 : 4);
+
+ atc->daios = kzalloc(sizeof(void *)*num_daios, GFP_KERNEL);
if (!atc->daios)
return -ENOMEM;
- atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+ atc->srcs = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL);
if (!atc->srcs)
return -ENOMEM;
- atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+ atc->srcimps = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL);
if (!atc->srcimps)
return -ENOMEM;
@@ -1351,8 +1388,9 @@ static int atc_get_resources(struct ct_atc *atc)
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
da_desc.msr = atc->msr;
- for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
- da_desc.type = i;
+ for (i = 0, atc->n_daio = 0; i < num_daios; i++) {
+ da_desc.type = (atc->model != CTSB073X) ? i :
+ ((i == SPDIFIO) ? SPDIFI1 : i);
err = daio_mgr->get_daio(daio_mgr, &da_desc,
(struct daio **)&atc->daios[i]);
if (err) {
@@ -1362,23 +1400,12 @@ static int atc_get_resources(struct ct_atc *atc)
}
atc->n_daio++;
}
- if (atc->model == CTSB073X)
- da_desc.type = SPDIFI1;
- else
- da_desc.type = SPDIFIO;
- err = daio_mgr->get_daio(daio_mgr, &da_desc,
- (struct daio **)&atc->daios[i]);
- if (err) {
- printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n");
- return err;
- }
- atc->n_daio++;
src_mgr = atc->rsc_mgrs[SRC];
src_dsc.multi = 1;
src_dsc.msr = atc->msr;
src_dsc.mode = ARCRW;
- for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+ for (i = 0, atc->n_src = 0; i < num_srcs; i++) {
err = src_mgr->get_src(src_mgr, &src_dsc,
(struct src **)&atc->srcs[i]);
if (err)
@@ -1388,8 +1415,8 @@ static int atc_get_resources(struct ct_atc *atc)
}
srcimp_mgr = atc->rsc_mgrs[SRCIMP];
- srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
- for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+ srcimp_dsc.msr = 8;
+ for (i = 0, atc->n_srcimp = 0; i < num_srcs; i++) {
err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
(struct srcimp **)&atc->srcimps[i]);
if (err)
@@ -1397,15 +1424,6 @@ static int atc_get_resources(struct ct_atc *atc)
atc->n_srcimp++;
}
- srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
- for (i = 0; i < (2*1); i++) {
- err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
- (struct srcimp **)&atc->srcimps[2*1+i]);
- if (err)
- return err;
-
- atc->n_srcimp++;
- }
sum_mgr = atc->rsc_mgrs[SUM];
sum_dsc.msr = atc->msr;
@@ -1488,6 +1506,18 @@ static void atc_connect_resources(struct ct_atc *atc)
src = atc->srcs[3];
mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+ if (atc->model == CTSB1270) {
+ /* Titanium HD has a dedicated ADC for the Mic. */
+ dai = container_of(atc->daios[MIC], struct dai, daio);
+ atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+ (struct src **)&atc->srcs[4],
+ (struct srcimp **)&atc->srcimps[4]);
+ src = atc->srcs[4];
+ mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc);
+ src = atc->srcs[5];
+ mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
+ }
+
dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai,
(struct src **)&atc->srcs[0],
@@ -1606,12 +1636,17 @@ static struct ct_atc atc_preset __devinitdata = {
.line_clfe_unmute = atc_line_clfe_unmute,
.line_rear_unmute = atc_line_rear_unmute,
.line_in_unmute = atc_line_in_unmute,
+ .mic_unmute = atc_mic_unmute,
.spdif_out_unmute = atc_spdif_out_unmute,
.spdif_in_unmute = atc_spdif_in_unmute,
.spdif_out_get_status = atc_spdif_out_get_status,
.spdif_out_set_status = atc_spdif_out_set_status,
.spdif_out_passthru = atc_spdif_out_passthru,
- .have_digit_io_switch = atc_have_digit_io_switch,
+ .capabilities = atc_capabilities,
+ .output_switch_get = atc_output_switch_get,
+ .output_switch_put = atc_output_switch_put,
+ .mic_source_switch_get = atc_mic_source_switch_get,
+ .mic_source_switch_put = atc_mic_source_switch_put,
#ifdef CONFIG_PM
.suspend = atc_suspend,
.resume = atc_resume,
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index 7167c01..3a0def6 100644
--- a/sound/pci/ctxfi/ctatc.h
+++ b/sound/pci/ctxfi/ctatc.h
@@ -25,6 +25,7 @@
#include <sound/core.h>
#include "ctvmem.h"
+#include "cthardware.h"
#include "ctresource.h"
enum CTALSADEVS { /* Types of alsa devices */
@@ -115,12 +116,17 @@ struct ct_atc {
int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
+ int (*mic_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
- int (*have_digit_io_switch)(struct ct_atc *atc);
+ struct capabilities (*capabilities)(struct ct_atc *atc);
+ int (*output_switch_get)(struct ct_atc *atc);
+ int (*output_switch_put)(struct ct_atc *atc, int position);
+ int (*mic_source_switch_get)(struct ct_atc *atc);
+ int (*mic_source_switch_put)(struct ct_atc *atc, int position);
/* Don't touch! Used for internal object. */
void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index 47d9ea9..0c00eb4 100644
--- a/sound/pci/ctxfi/ctdaio.c
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -22,20 +22,9 @@
#include <linux/slab.h>
#include <linux/kernel.h>
-#define DAIO_RESOURCE_NUM NUM_DAIOTYP
#define DAIO_OUT_MAX SPDIFOO
-union daio_usage {
- struct {
- unsigned short lineo1:1;
- unsigned short lineo2:1;
- unsigned short lineo3:1;
- unsigned short lineo4:1;
- unsigned short spdifoo:1;
- unsigned short lineim:1;
- unsigned short spdifio:1;
- unsigned short spdifi1:1;
- } bf;
+struct daio_usage {
unsigned short data;
};
@@ -61,6 +50,7 @@ struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[LINEO3] = {.left = 0x50, .right = 0x51},
[LINEO4] = {.left = 0x70, .right = 0x71},
[LINEIM] = {.left = 0x45, .right = 0xc5},
+ [MIC] = {.left = 0x55, .right = 0xd5},
[SPDIFOO] = {.left = 0x00, .right = 0x01},
[SPDIFIO] = {.left = 0x05, .right = 0x85},
};
@@ -138,6 +128,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
case LINEO3: return 5;
case LINEO4: return 6;
case LINEIM: return 4;
+ case MIC: return 5;
default: return -EINVAL;
}
default:
@@ -519,17 +510,17 @@ static int dai_rsc_uninit(struct dai *dai)
static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
{
- if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
+ if (((struct daio_usage *)mgr->rscs)->data & (0x1 << type))
return -ENOENT;
- ((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
+ ((struct daio_usage *)mgr->rscs)->data |= (0x1 << type);
return 0;
}
static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
{
- ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
+ ((struct daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
return 0;
}
@@ -712,7 +703,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
if (!daio_mgr)
return -ENOMEM;
- err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
+ err = rsc_mgr_init(&daio_mgr->mgr, DAIO, NUM_DAIOTYP, hw);
if (err)
goto error1;
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
index 0f52ce5..85ccb6e 100644
--- a/sound/pci/ctxfi/ctdaio.h
+++ b/sound/pci/ctxfi/ctdaio.h
@@ -33,6 +33,7 @@ enum DAIOTYP {
SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */
LINEIM,
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
+ MIC, /* Dedicated mic on Titanium HD */
SPDIFI1, /* S/PDIF In on internal Drive Bay */
NUM_DAIOTYP
};
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index af55405..908315b 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -39,6 +39,7 @@ enum CTCARDS {
CT20K2_MODEL_FIRST = CTSB0760,
CTHENDRIX,
CTSB0880,
+ CTSB1270,
CT20K2_UNKNOWN,
NUM_CTCARDS /* This should always be the last */
};
@@ -60,6 +61,13 @@ struct card_conf {
unsigned int msr; /* master sample rate in rsrs */
};
+struct capabilities {
+ unsigned int digit_io_switch:1;
+ unsigned int dedicated_mic:1;
+ unsigned int output_switch:1;
+ unsigned int mic_source_switch:1;
+};
+
struct hw {
int (*card_init)(struct hw *hw, struct card_conf *info);
int (*card_stop)(struct hw *hw);
@@ -70,7 +78,11 @@ struct hw {
#endif
int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
- int (*have_digit_io_switch)(struct hw *hw);
+ struct capabilities (*capabilities)(struct hw *hw);
+ int (*output_switch_get)(struct hw *hw);
+ int (*output_switch_put)(struct hw *hw, int position);
+ int (*mic_source_switch_get)(struct hw *hw);
+ int (*mic_source_switch_put)(struct hw *hw, int position);
/* SRC operations */
int (*src_rsc_get_ctrl_blk)(void **rblk);
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index a5c957d..a7df197 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1777,10 +1777,17 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
return adc_init_SBx(hw, info->input, info->mic20db);
}
-static int hw_have_digit_io_switch(struct hw *hw)
+static struct capabilities hw_capabilities(struct hw *hw)
{
+ struct capabilities cap;
+
/* SB073x and Vista compatible cards have no digit IO switch */
- return !(hw->model == CTSB073X || hw->model == CTUAA);
+ cap.digit_io_switch = !(hw->model == CTSB073X || hw->model == CTUAA);
+ cap.dedicated_mic = 0;
+ cap.output_switch = 0;
+ cap.mic_source_switch = 0;
+
+ return cap;
}
#define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
@@ -1933,7 +1940,7 @@ static int hw_card_start(struct hw *hw)
if (hw->irq < 0) {
err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
- "ctxfi", hw);
+ KBUILD_MODNAME, hw);
if (err < 0) {
printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
goto error2;
@@ -2172,7 +2179,7 @@ static struct hw ct20k1_preset __devinitdata = {
.pll_init = hw_pll_init,
.is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select,
- .have_digit_io_switch = hw_have_digit_io_switch,
+ .capabilities = hw_capabilities,
#ifdef CONFIG_PM
.suspend = hw_suspend,
.resume = hw_resume,
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 5364164..d6c54b5 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -8,7 +8,7 @@
* @File cthw20k2.c
*
* @Brief
- * This file contains the implementation of hardware access methord for 20k2.
+ * This file contains the implementation of hardware access method for 20k2.
*
* @Author Liu Chun
* @Date May 14 2008
@@ -38,6 +38,8 @@ struct hw20k2 {
unsigned char dev_id;
unsigned char addr_size;
unsigned char data_size;
+
+ int mic_source;
};
static u32 hw_read_20kx(struct hw *hw, u32 reg);
@@ -1163,7 +1165,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else if (2 == info->msr) {
- hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+ if (hw->model != CTSB1270) {
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+ } else {
+ /* PCM4220 on Titanium HD is different. */
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11011111);
+ }
/* Specify all playing 96khz
* EA [0] - Enabled
* RTA [4:5] - 96kHz
@@ -1175,6 +1182,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
* RTD [28:29] - 96kHz */
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+ } else if ((4 == info->msr) && (hw->model == CTSB1270)) {
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111);
+ hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
+ hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else {
printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
return -EINVAL;
@@ -1182,6 +1193,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
for (i = 0; i < 8; i++) {
if (i <= 3) {
+ /* This comment looks wrong since loop is over 4 */
+ /* channels and emu20k2 supports 4 spdif IOs. */
/* 1st 3 channels are SPDIFs (SB0960) */
if (i == 3)
data = 0x1001001;
@@ -1206,12 +1219,16 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
} else {
+ /* Again, loop is over 4 channels not 5. */
/* Next 5 channels are I2S (SB0960) */
data = 0x11;
hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data);
if (2 == info->msr) {
/* Four channels per sample period */
data |= 0x1000;
+ } else if (4 == info->msr) {
+ /* FIXME: check this against the chip spec */
+ data |= 0x2000;
}
hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data);
}
@@ -1299,21 +1316,18 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
pllenb = 0xB;
hw_write_20kx(hw, PLL_ENB, pllenb);
- pllctl = 0x20D00000;
- set_field(&pllctl, PLLCTL_FD, 16 - 4);
+ pllctl = 0x20C00000;
+ set_field(&pllctl, PLLCTL_B, 0);
+ set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 4 : 147 - 4);
+ set_field(&pllctl, PLLCTL_RD, 48000 == rsr ? 1 - 1 : 10 - 1);
hw_write_20kx(hw, PLL_CTL, pllctl);
mdelay(40);
+
pllctl = hw_read_20kx(hw, PLL_CTL);
- set_field(&pllctl, PLLCTL_B, 0);
- if (48000 == rsr) {
- set_field(&pllctl, PLLCTL_FD, 16 - 2);
- set_field(&pllctl, PLLCTL_RD, 1 - 1); /* 3000*16/1 = 48000 */
- } else { /* 44100 */
- set_field(&pllctl, PLLCTL_FD, 147 - 2);
- set_field(&pllctl, PLLCTL_RD, 10 - 1); /* 3000*147/10 = 44100 */
- }
+ set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 2 : 147 - 2);
hw_write_20kx(hw, PLL_CTL, pllctl);
mdelay(40);
+
for (i = 0; i < 1000; i++) {
pllstat = hw_read_20kx(hw, PLL_STAT);
if (get_field(pllstat, PLLSTAT_PD))
@@ -1557,7 +1571,7 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
hw20k2_i2c_wait_data_ready(hw);
- /* Dummy write to trigger the write oprtation */
+ /* Dummy write to trigger the write operation */
hw_write_20kx(hw, I2C_IF_WDATA, 0);
hw20k2_i2c_wait_data_ready(hw);
@@ -1568,6 +1582,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
return 0;
}
+static void hw_dac_stop(struct hw *hw)
+{
+ u32 data;
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= 0xFFFFFFFD;
+ hw_write_20kx(hw, GPIO_DATA, data);
+ mdelay(10);
+}
+
+static void hw_dac_start(struct hw *hw)
+{
+ u32 data;
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= 0x2;
+ hw_write_20kx(hw, GPIO_DATA, data);
+ mdelay(50);
+}
+
+static void hw_dac_reset(struct hw *hw)
+{
+ hw_dac_stop(hw);
+ hw_dac_start(hw);
+}
+
static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
{
int err;
@@ -1594,6 +1632,21 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
0x00000000 /* Vol Control B4 */
};
+ if (hw->model == CTSB1270) {
+ hw_dac_stop(hw);
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= ~0x0600;
+ if (1 == info->msr)
+ data |= 0x0000; /* Single Speed Mode 0-50kHz */
+ else if (2 == info->msr)
+ data |= 0x0200; /* Double Speed Mode 50-100kHz */
+ else
+ data |= 0x0600; /* Quad Speed Mode 100-200kHz */
+ hw_write_20kx(hw, GPIO_DATA, data);
+ hw_dac_start(hw);
+ return 0;
+ }
+
/* Set DAC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL);
data |= 0x02;
@@ -1606,22 +1659,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
for (i = 0; i < 2; i++) {
/* Reset DAC twice just in-case the chip
* didn't initialized properly */
- data = hw_read_20kx(hw, GPIO_DATA);
- /* GPIO data bit 1 */
- data &= 0xFFFFFFFD;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(10);
- data |= 0x2;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
-
- /* Reset the 2nd time */
- data &= 0xFFFFFFFD;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(10);
- data |= 0x2;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
+ hw_dac_reset(hw);
+ hw_dac_reset(hw);
if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1))
continue;
@@ -1725,7 +1764,11 @@ End:
static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
{
u32 data;
-
+ if (hw->model == CTSB1270) {
+ /* Titanium HD has two ADC chips, one for line in and one */
+ /* for MIC. We don't need to switch the ADC input. */
+ return 1;
+ }
data = hw_read_20kx(hw, GPIO_DATA);
switch (type) {
case ADC_MICIN:
@@ -1742,35 +1785,47 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
#define MIC_BOOST_0DB 0xCF
#define MIC_BOOST_STEPS_PER_DB 2
-#define MIC_BOOST_20DB (MIC_BOOST_0DB + 20 * MIC_BOOST_STEPS_PER_DB)
+
+static void hw_wm8775_input_select(struct hw *hw, u8 input, s8 gain_in_db)
+{
+ u32 adcmc, gain;
+
+ if (input > 3)
+ input = 3;
+
+ adcmc = ((u32)1 << input) | 0x100; /* Link L+R gain... */
+
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, adcmc),
+ MAKE_WM8775_DATA(adcmc));
+
+ if (gain_in_db < -103)
+ gain_in_db = -103;
+ if (gain_in_db > 24)
+ gain_in_db = 24;
+
+ gain = gain_in_db * MIC_BOOST_STEPS_PER_DB + MIC_BOOST_0DB;
+
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, gain),
+ MAKE_WM8775_DATA(gain));
+ /* ...so there should be no need for the following. */
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, gain),
+ MAKE_WM8775_DATA(gain));
+}
static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
{
u32 data;
-
data = hw_read_20kx(hw, GPIO_DATA);
switch (type) {
case ADC_MICIN:
data |= (0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data);
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
- MAKE_WM8775_DATA(0x101)); /* Mic-in */
- hw20k2_i2c_write(hw,
- MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB),
- MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
- hw20k2_i2c_write(hw,
- MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB),
- MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
+ hw_wm8775_input_select(hw, 0, 20); /* Mic, 20dB */
break;
case ADC_LINEIN:
data &= ~(0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data);
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
- MAKE_WM8775_DATA(0x102)); /* Line-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
+ hw_wm8775_input_select(hw, 1, 0); /* Line-in, 0dB */
break;
default:
break;
@@ -1782,7 +1837,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
{
int err;
- u32 mux = 2, data, ctl;
+ u32 data, ctl;
/* Set ADC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL);
@@ -1796,19 +1851,42 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
goto error;
}
- /* Make ADC in normal operation */
+ /* Reset the ADC (reset is active low). */
data = hw_read_20kx(hw, GPIO_DATA);
data &= ~(0x1 << 15);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ if (hw->model == CTSB1270) {
+ /* Set up the PCM4220 ADC on Titanium HD */
+ data &= ~0x0C;
+ if (1 == info->msr)
+ data |= 0x00; /* Single Speed Mode 32-50kHz */
+ else if (2 == info->msr)
+ data |= 0x08; /* Double Speed Mode 50-108kHz */
+ else
+ data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */
+ hw_write_20kx(hw, GPIO_DATA, data);
+ }
+
mdelay(10);
+ /* Return the ADC to normal operation. */
data |= (0x1 << 15);
hw_write_20kx(hw, GPIO_DATA, data);
mdelay(50);
+ /* I2C write to register offset 0x0B to set ADC LRCLK polarity */
+ /* invert bit, interface format to I2S, word length to 24-bit, */
+ /* enable ADC high pass filter. Fixes bug 5323? */
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_IC, 0x26),
+ MAKE_WM8775_DATA(0x26));
+
/* Set the master mode (256fs) */
if (1 == info->msr) {
+ /* slave mode, 128x oversampling 256fs */
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
MAKE_WM8775_DATA(0x02));
- } else if (2 == info->msr) {
+ } else if ((2 == info->msr) || (4 == info->msr)) {
+ /* slave mode, 64x oversampling, 256fs */
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
MAKE_WM8775_DATA(0x0A));
} else {
@@ -1818,55 +1896,113 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
goto error;
}
- /* Configure GPIO bit 14 change to line-in/mic-in */
- ctl = hw_read_20kx(hw, GPIO_CTRL);
- ctl |= 0x1 << 14;
- hw_write_20kx(hw, GPIO_CTRL, ctl);
-
- /* Check using Mic-in or Line-in */
- data = hw_read_20kx(hw, GPIO_DATA);
-
- if (mux == 1) {
- /* Configures GPIO data to select Mic-in */
- data |= 0x1 << 14;
- hw_write_20kx(hw, GPIO_DATA, data);
-
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
- MAKE_WM8775_DATA(0x101)); /* Mic-in */
- hw20k2_i2c_write(hw,
- MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB),
- MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
- hw20k2_i2c_write(hw,
- MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB),
- MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
- } else if (mux == 2) {
- /* Configures GPIO data to select Line-in */
- data &= ~(0x1 << 14);
- hw_write_20kx(hw, GPIO_DATA, data);
-
- /* Setup ADC */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
- MAKE_WM8775_DATA(0x102)); /* Line-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
+ if (hw->model != CTSB1270) {
+ /* Configure GPIO bit 14 change to line-in/mic-in */
+ ctl = hw_read_20kx(hw, GPIO_CTRL);
+ ctl |= 0x1 << 14;
+ hw_write_20kx(hw, GPIO_CTRL, ctl);
+ hw_adc_input_select(hw, ADC_LINEIN);
} else {
- printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n");
- err = -EINVAL;
- goto error;
+ hw_wm8775_input_select(hw, 0, 0);
}
return 0;
-
error:
hw20k2_i2c_uninit(hw);
return err;
}
-static int hw_have_digit_io_switch(struct hw *hw)
+static struct capabilities hw_capabilities(struct hw *hw)
{
- return 0;
+ struct capabilities cap;
+
+ cap.digit_io_switch = 0;
+ cap.dedicated_mic = hw->model == CTSB1270;
+ cap.output_switch = hw->model == CTSB1270;
+ cap.mic_source_switch = hw->model == CTSB1270;
+
+ return cap;
+}
+
+static int hw_output_switch_get(struct hw *hw)
+{
+ u32 data = hw_read_20kx(hw, GPIO_EXT_DATA);
+
+ switch (data & 0x30) {
+ case 0x00:
+ return 0;
+ case 0x10:
+ return 1;
+ case 0x20:
+ return 2;
+ default:
+ return 3;
+ }
+}
+
+static int hw_output_switch_put(struct hw *hw, int position)
+{
+ u32 data;
+
+ if (position == hw_output_switch_get(hw))
+ return 0;
+
+ /* Mute line and headphones (intended for anti-pop). */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= (0x03 << 11);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ data = hw_read_20kx(hw, GPIO_EXT_DATA) & ~0x30;
+ switch (position) {
+ case 0:
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ data |= 0x20;
+ }
+ hw_write_20kx(hw, GPIO_EXT_DATA, data);
+
+ /* Unmute line and headphones. */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= ~(0x03 << 11);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ return 1;
+}
+
+static int hw_mic_source_switch_get(struct hw *hw)
+{
+ struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+ return hw20k2->mic_source;
+}
+
+static int hw_mic_source_switch_put(struct hw *hw, int position)
+{
+ struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+ if (position == hw20k2->mic_source)
+ return 0;
+
+ switch (position) {
+ case 0:
+ hw_wm8775_input_select(hw, 0, 0); /* Mic, 0dB */
+ break;
+ case 1:
+ hw_wm8775_input_select(hw, 1, 0); /* FP Mic, 0dB */
+ break;
+ case 2:
+ hw_wm8775_input_select(hw, 3, 0); /* Aux Ext, 0dB */
+ break;
+ default:
+ return 0;
+ }
+
+ hw20k2->mic_source = position;
+
+ return 1;
}
static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
@@ -1925,7 +2061,7 @@ static int hw_card_start(struct hw *hw)
if (hw->irq < 0) {
err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED,
- "ctxfi", hw);
+ KBUILD_MODNAME, hw);
if (err < 0) {
printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
goto error2;
@@ -2023,13 +2159,16 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
/* Reset all SRC pending interrupts */
hw_write_20kx(hw, SRC_IP, 0);
- /* TODO: detect the card ID and configure GPIO accordingly. */
- /* Configures GPIO (0xD802 0x98028) */
- /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
- /* Configures GPIO (SB0880) */
- /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
- hw_write_20kx(hw, GPIO_CTRL, 0xD802);
-
+ if (hw->model != CTSB1270) {
+ /* TODO: detect the card ID and configure GPIO accordingly. */
+ /* Configures GPIO (0xD802 0x98028) */
+ /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
+ /* Configures GPIO (SB0880) */
+ /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
+ hw_write_20kx(hw, GPIO_CTRL, 0xD802);
+ } else {
+ hw_write_20kx(hw, GPIO_CTRL, 0x9E5F);
+ }
/* Enable audio ring */
hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
@@ -2106,7 +2245,11 @@ static struct hw ct20k2_preset __devinitdata = {
.pll_init = hw_pll_init,
.is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select,
- .have_digit_io_switch = hw_have_digit_io_switch,
+ .capabilities = hw_capabilities,
+ .output_switch_get = hw_output_switch_get,
+ .output_switch_put = hw_output_switch_put,
+ .mic_source_switch_get = hw_mic_source_switch_get,
+ .mic_source_switch_put = hw_mic_source_switch_put,
#ifdef CONFIG_PM
.suspend = hw_suspend,
.resume = hw_resume,
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
index c3519ff..0cc13ee 100644
--- a/sound/pci/ctxfi/ctmixer.c
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -86,9 +86,7 @@ enum CTALSA_MIXER_CTL {
MIXER_LINEIN_C_S,
MIXER_MIC_C_S,
MIXER_SPDIFI_C_S,
- MIXER_LINEIN_P_S,
MIXER_SPDIFO_P_S,
- MIXER_SPDIFI_P_S,
MIXER_WAVEF_P_S,
MIXER_WAVER_P_S,
MIXER_WAVEC_P_S,
@@ -137,11 +135,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_LINEIN_P] = {
.ctl = 1,
- .name = "Line-in Playback Volume",
+ .name = "Line Playback Volume",
},
[MIXER_LINEIN_C] = {
.ctl = 1,
- .name = "Line-in Capture Volume",
+ .name = "Line Capture Volume",
},
[MIXER_MIC_P] = {
.ctl = 1,
@@ -153,15 +151,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_SPDIFI_P] = {
.ctl = 1,
- .name = "S/PDIF-in Playback Volume",
+ .name = "IEC958 Playback Volume",
},
[MIXER_SPDIFI_C] = {
.ctl = 1,
- .name = "S/PDIF-in Capture Volume",
+ .name = "IEC958 Capture Volume",
},
[MIXER_SPDIFO_P] = {
.ctl = 1,
- .name = "S/PDIF-out Playback Volume",
+ .name = "Digital Playback Volume",
},
[MIXER_WAVEF_P] = {
.ctl = 1,
@@ -179,14 +177,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
.ctl = 1,
.name = "Surround Playback Volume",
},
-
[MIXER_PCM_C_S] = {
.ctl = 1,
.name = "PCM Capture Switch",
},
[MIXER_LINEIN_C_S] = {
.ctl = 1,
- .name = "Line-in Capture Switch",
+ .name = "Line Capture Switch",
},
[MIXER_MIC_C_S] = {
.ctl = 1,
@@ -194,19 +191,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_SPDIFI_C_S] = {
.ctl = 1,
- .name = "S/PDIF-in Capture Switch",
- },
- [MIXER_LINEIN_P_S] = {
- .ctl = 1,
- .name = "Line-in Playback Switch",
+ .name = "IEC958 Capture Switch",
},
[MIXER_SPDIFO_P_S] = {
.ctl = 1,
- .name = "S/PDIF-out Playback Switch",
- },
- [MIXER_SPDIFI_P_S] = {
- .ctl = 1,
- .name = "S/PDIF-in Playback Switch",
+ .name = "Digital Playback Switch",
},
[MIXER_WAVEF_P_S] = {
.ctl = 1,
@@ -236,6 +225,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
static void
ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+/* FIXME: this static looks like it would fail if more than one card was */
+/* installed. */
static struct snd_kcontrol *kctls[2] = {NULL};
static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
@@ -420,6 +411,77 @@ static struct snd_kcontrol_new vol_ctl = {
.tlv = { .p = ct_vol_db_scale },
};
+static int output_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "FP Headphones", "Headphones", "Speakers"
+ };
+
+ return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int output_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = atc->output_switch_get(atc);
+ return 0;
+}
+
+static int output_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ return atc->output_switch_put(atc, ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new output_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Output Playback Enum",
+ .info = output_switch_info,
+ .get = output_switch_get,
+ .put = output_switch_put,
+};
+
+static int mic_source_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "Mic", "FP Mic", "Aux"
+ };
+
+ return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int mic_source_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = atc->mic_source_switch_get(atc);
+ return 0;
+}
+
+static int mic_source_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ return atc->mic_source_switch_put(atc,
+ ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new mic_source_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Source Capture Enum",
+ .info = mic_source_switch_info,
+ .get = mic_source_switch_get,
+ .put = mic_source_switch_put,
+};
+
static void
do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
{
@@ -465,6 +527,7 @@ do_digit_io_switch(struct ct_atc *atc, int state)
static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
{
struct ct_mixer *mixer = atc->mixer;
+ struct capabilities cap = atc->capabilities(atc);
/* Do changes in mixer. */
if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
@@ -477,8 +540,17 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
}
}
/* Do changes out of mixer. */
- if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
- do_line_mic_switch(atc, type);
+ if (!cap.dedicated_mic &&
+ (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) {
+ if (state)
+ do_line_mic_switch(atc, type);
+ atc->line_in_unmute(atc, state);
+ } else if (cap.dedicated_mic && (MIXER_LINEIN_C_S == type))
+ atc->line_in_unmute(atc, state);
+ else if (cap.dedicated_mic && (MIXER_MIC_C_S == type))
+ atc->mic_unmute(atc, state);
+ else if (MIXER_SPDIFI_C_S == type)
+ atc->spdif_in_unmute(atc, state);
else if (MIXER_WAVEF_P_S == type)
atc->line_front_unmute(atc, state);
else if (MIXER_WAVES_P_S == type)
@@ -487,12 +559,8 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
atc->line_clfe_unmute(atc, state);
else if (MIXER_WAVER_P_S == type)
atc->line_rear_unmute(atc, state);
- else if (MIXER_LINEIN_P_S == type)
- atc->line_in_unmute(atc, state);
else if (MIXER_SPDIFO_P_S == type)
atc->spdif_out_unmute(atc, state);
- else if (MIXER_SPDIFI_P_S == type)
- atc->spdif_in_unmute(atc, state);
else if (MIXER_DIGITAL_IO_S == type)
do_digit_io_switch(atc, state);
@@ -671,6 +739,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
{
enum CTALSA_MIXER_CTL type;
struct ct_atc *atc = mixer->atc;
+ struct capabilities cap = atc->capabilities(atc);
int err;
/* Create snd kcontrol instances on demand */
@@ -684,8 +753,8 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
}
}
- ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
- atc->have_digit_io_switch(atc);
+ ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = cap.digit_io_switch;
+
for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
if (ct_kcontrol_init_table[type].ctl) {
swh_ctl.name = ct_kcontrol_init_table[type].name;
@@ -708,6 +777,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
if (err)
return err;
+ if (cap.output_switch) {
+ err = ct_mixer_kcontrol_new(mixer, &output_ctl);
+ if (err)
+ return err;
+ }
+
+ if (cap.mic_source_switch) {
+ err = ct_mixer_kcontrol_new(mixer, &mic_source_ctl);
+ if (err)
+ return err;
+ }
atc->line_front_unmute(atc, 1);
set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
atc->line_surround_unmute(atc, 0);
@@ -719,13 +799,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
atc->spdif_out_unmute(atc, 0);
set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
atc->line_in_unmute(atc, 0);
- set_switch_state(mixer, MIXER_LINEIN_P_S, 0);
+ if (cap.dedicated_mic)
+ atc->mic_unmute(atc, 0);
atc->spdif_in_unmute(atc, 0);
- set_switch_state(mixer, MIXER_SPDIFI_P_S, 0);
-
- set_switch_state(mixer, MIXER_PCM_C_S, 1);
- set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
- set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
+ set_switch_state(mixer, MIXER_PCM_C_S, 0);
+ set_switch_state(mixer, MIXER_LINEIN_C_S, 0);
+ set_switch_state(mixer, MIXER_SPDIFI_C_S, 0);
return 0;
}
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index f42e7e1..b259aa0 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -80,11 +80,11 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
"are 48000 and 44100, Value 48000 is assumed.\n");
reference_rate = 48000;
}
- if ((multiple != 1) && (multiple != 2)) {
+ if ((multiple != 1) && (multiple != 2) && (multiple != 4)) {
printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
multiple);
printk(KERN_ERR "ctxfi: The valid values for multiple are "
- "1 and 2, Value 2 is assumed.\n");
+ "1, 2 and 4, Value 2 is assumed.\n");
multiple = 2;
}
err = ct_atc_create(card, pci, reference_rate, multiple,
@@ -143,7 +143,7 @@ static int ct_card_resume(struct pci_dev *pci)
#endif
static struct pci_driver ct_driver = {
- .name = "SB-XFi",
+ .name = KBUILD_MODNAME,
.id_table = ct_pci_dev_ids,
.probe = ct_card_probe,
.remove = __devexit_p(ct_card_remove),
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 20763dd..d730698 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1995,7 +1995,7 @@ static __devinit int snd_echo_create(struct snd_card *card,
ioremap_nocache(chip->dsp_registers_phys, sz);
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
- ECHOCARD_NAME, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_echo_free(chip);
snd_printk(KERN_ERR "cannot grab irq\n");
return -EBUSY;
@@ -2286,7 +2286,7 @@ static int snd_echo_resume(struct pci_dev *pci)
kfree(commpage_bak);
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
- ECHOCARD_NAME, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_echo_free(chip);
snd_printk(KERN_ERR "cannot grab irq\n");
return -EBUSY;
@@ -2327,7 +2327,7 @@ static void __devexit snd_echo_remove(struct pci_dev *pci)
/* pci_driver definition */
static struct pci_driver driver = {
- .name = "Echoaudio " ECHOCARD_NAME,
+ .name = KBUILD_MODNAME,
.id_table = snd_echo_ids,
.probe = snd_echo_probe,
.remove = __devexit_p(snd_echo_remove),
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index aff8387..a9c45d2 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -264,7 +264,7 @@ static int snd_emu10k1_resume(struct pci_dev *pci)
#endif
static struct pci_driver driver = {
- .name = "EMU10K1_Audigy",
+ .name = KBUILD_MODNAME,
.id_table = snd_emu10k1_ids,
.probe = snd_card_emu10k1_probe,
.remove = __devexit_p(snd_card_emu10k1_remove),
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 15f0161..fcd4935 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1912,7 +1912,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
/* irq handler must be registered after I/O ports are activated */
if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_SHARED,
- "EMU10K1", emu)) {
+ KBUILD_MODNAME, emu)) {
err = -EBUSY;
goto error;
}
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 0c701e4..d4fde1b 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -925,7 +925,7 @@ static int __devinit snd_emu10k1x_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_emu10k1x_interrupt,
- IRQF_SHARED, "EMU10K1X", chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq);
snd_emu10k1x_free(chip);
return -EBUSY;
@@ -1613,7 +1613,7 @@ MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
// pci_driver definition
static struct pci_driver driver = {
- .name = "EMU10K1X",
+ .name = KBUILD_MODNAME,
.id_table = snd_emu10k1x_ids,
.probe = snd_emu10k1x_probe,
.remove = __devexit_p(snd_emu10k1x_remove),
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 863eafe..f02e2f8 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -2120,7 +2120,7 @@ static int __devinit snd_ensoniq_create(struct snd_card *card,
}
ensoniq->port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_audiopci_interrupt, IRQF_SHARED,
- "Ensoniq AudioPCI", ensoniq)) {
+ KBUILD_MODNAME, ensoniq)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_ensoniq_free(ensoniq);
return -EBUSY;
@@ -2489,7 +2489,7 @@ static void __devexit snd_audiopci_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = DRIVER_NAME,
+ .name = KBUILD_MODNAME,
.id_table = snd_audiopci_ids,
.probe = snd_audiopci_probe,
.remove = __devexit_p(snd_audiopci_remove),
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 553b752..26a5a2f 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1514,7 +1514,7 @@ static int es1938_resume(struct pci_dev *pci)
}
if (request_irq(pci->irq, snd_es1938_interrupt,
- IRQF_SHARED, "ES1938", chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
printk(KERN_ERR "es1938: unable to grab IRQ %d, "
"disabling device\n", pci->irq);
snd_card_disconnect(card);
@@ -1636,7 +1636,7 @@ static int __devinit snd_es1938_create(struct snd_card *card,
chip->mpu_port = pci_resource_start(pci, 3);
chip->game_port = pci_resource_start(pci, 4);
if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_SHARED,
- "ES1938", chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_es1938_free(chip);
return -EBUSY;
@@ -1882,7 +1882,7 @@ static void __devexit snd_es1938_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "ESS ES1938 (Solo-1)",
+ .name = KBUILD_MODNAME,
.id_table = snd_es1938_ids,
.probe = snd_es1938_probe,
.remove = __devexit_p(snd_es1938_remove),
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index ab0a615..99ea932 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -554,9 +554,8 @@ struct es1968 {
#else
struct snd_kcontrol *master_switch; /* for h/w volume control */
struct snd_kcontrol *master_volume;
- spinlock_t ac97_lock;
- struct tasklet_struct hwvol_tq;
#endif
+ struct work_struct hwvol_work;
#ifdef CONFIG_SND_ES1968_RADIO
struct snd_tea575x tea;
@@ -646,38 +645,23 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip)
static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
snd_es1968_ac97_wait(chip);
/* Write the bus */
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
outw(val, chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
outb(reg, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
}
static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
u16 data = 0;
struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
snd_es1968_ac97_wait(chip);
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
@@ -685,9 +669,6 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short
data = inw(chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
}
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
return data;
}
@@ -1904,13 +1885,10 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es)
(without wrap around) in response to volume button presses and then
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
-static void es1968_update_hw_volume(unsigned long private_data)
+static void es1968_update_hw_volume(struct work_struct *work)
{
- struct es1968 *chip = (struct es1968 *) private_data;
+ struct es1968 *chip = container_of(work, struct es1968, hwvol_work);
int x, val;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
/* Figure out which volume control button was pushed,
based on differences from the default register
@@ -1929,18 +1907,11 @@ static void es1968_update_hw_volume(unsigned long private_data)
if (! chip->master_switch || ! chip->master_volume)
return;
- /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
- spin_lock_irqsave(&chip->ac97_lock, flags);
- val = chip->ac97->regs[AC97_MASTER];
+ val = snd_ac97_read(chip->ac97, AC97_MASTER);
switch (x) {
case 0x88:
/* mute */
val ^= 0x8000;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_switch->id);
break;
case 0xaa:
/* volume up */
@@ -1948,11 +1919,6 @@ static void es1968_update_hw_volume(unsigned long private_data)
val--;
if ((val & 0x7f00) > 0)
val -= 0x0100;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
case 0x66:
/* volume down */
@@ -1960,14 +1926,11 @@ static void es1968_update_hw_volume(unsigned long private_data)
val++;
if ((val & 0x7f00) < 0x1f00)
val += 0x0100;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
}
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
+ if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
#else
if (!chip->input_dev)
return;
@@ -2013,11 +1976,7 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
if (event & ESM_HWVOL_IRQ)
-#ifdef CONFIG_SND_ES1968_INPUT
- es1968_update_hw_volume((unsigned long)chip);
-#else
- tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
-#endif
+ schedule_work(&chip->hwvol_work);
/* else ack 'em all, i imagine */
outb(0xFF, chip->io_port + 0x1A);
@@ -2426,6 +2385,7 @@ static int es1968_suspend(struct pci_dev *pci, pm_message_t state)
return 0;
chip->in_suspend = 1;
+ cancel_work_sync(&chip->hwvol_work);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
@@ -2638,6 +2598,7 @@ static struct snd_tea575x_ops snd_es1968_tea_ops = {
static int snd_es1968_free(struct es1968 *chip)
{
+ cancel_work_sync(&chip->hwvol_work);
#ifdef CONFIG_SND_ES1968_INPUT
if (chip->input_dev)
input_unregister_device(chip->input_dev);
@@ -2728,10 +2689,7 @@ static int __devinit snd_es1968_create(struct snd_card *card,
INIT_LIST_HEAD(&chip->buf_list);
INIT_LIST_HEAD(&chip->substream_list);
mutex_init(&chip->memory_mutex);
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_init(&chip->ac97_lock);
- tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
-#endif
+ INIT_WORK(&chip->hwvol_work, es1968_update_hw_volume);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
@@ -2746,7 +2704,7 @@ static int __devinit snd_es1968_create(struct snd_card *card,
}
chip->io_port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_SHARED,
- "ESS Maestro", chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_es1968_free(chip);
return -EBUSY;
@@ -2925,7 +2883,7 @@ static void __devexit snd_es1968_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "ES1968 (ESS Maestro)",
+ .name = KBUILD_MODNAME,
.id_table = snd_es1968_ids,
.probe = snd_es1968_probe,
.remove = __devexit_p(snd_es1968_remove),
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index a7ec703..f9123f0 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1199,7 +1199,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
chip->port = pci_resource_start(pci, 0);
if ((tea575x_tuner & TUNER_ONLY) == 0) {
if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
- "FM801", chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
snd_fm801_free(chip);
return -EBUSY;
@@ -1394,7 +1394,7 @@ static int snd_fm801_resume(struct pci_dev *pci)
#endif
static struct pci_driver driver = {
- .name = "FM801",
+ .name = KBUILD_MODNAME,
.id_table = snd_fm801_ids,
.probe = snd_card_fm801_probe,
.remove = __devexit_p(snd_card_fm801_remove),
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 25b90ee..9c27a3a 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -5128,17 +5128,15 @@ void *snd_array_new(struct snd_array *array)
{
if (array->used >= array->alloced) {
int num = array->alloced + array->alloc_align;
+ int size = (num + 1) * array->elem_size;
+ int oldsize = array->alloced * array->elem_size;
void *nlist;
if (snd_BUG_ON(num >= 4096))
return NULL;
- nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
+ nlist = krealloc(array->list, size, GFP_KERNEL);
if (!nlist)
return NULL;
- if (array->list) {
- memcpy(nlist, array->list,
- array->elem_size * array->alloced);
- kfree(array->list);
- }
+ memset(nlist + oldsize, 0, size - oldsize);
array->list = nlist;
array->alloced = num;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 5ce9531..be69822 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -2169,7 +2169,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
{
if (request_irq(chip->pci->irq, azx_interrupt,
chip->msi ? 0 : IRQF_SHARED,
- "hda_intel", chip)) {
+ KBUILD_MODNAME, chip)) {
printk(KERN_ERR "hda-intel: unable to grab IRQ %d, "
"disabling device\n", chip->pci->irq);
if (do_disconnect)
@@ -2936,7 +2936,7 @@ MODULE_DEVICE_TABLE(pci, azx_ids);
/* pci_driver definition */
static struct pci_driver driver = {
- .name = "HDA Intel",
+ .name = KBUILD_MODNAME,
.id_table = azx_ids,
.probe = azx_probe,
.remove = __devexit_p(azx_remove),
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index f4594d7..be06fb3 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2607,7 +2607,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
ice->profi_port = pci_resource_start(pci, 3);
if (request_irq(pci->irq, snd_ice1712_interrupt, IRQF_SHARED,
- "ICE1712", ice)) {
+ KBUILD_MODNAME, ice)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_ice1712_free(ice);
return -EIO;
@@ -2802,7 +2802,7 @@ static void __devexit snd_ice1712_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "ICE1712",
+ .name = KBUILD_MODNAME,
.id_table = snd_ice1712_ids,
.probe = snd_ice1712_probe,
.remove = __devexit_p(snd_ice1712_remove),
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index c1498fa..c2b7f8b 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -2509,7 +2509,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
ice->profi_port = pci_resource_start(pci, 1);
if (request_irq(pci->irq, snd_vt1724_interrupt,
- IRQF_SHARED, "ICE1724", ice)) {
+ IRQF_SHARED, KBUILD_MODNAME, ice)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_vt1724_free(ice);
return -EIO;
@@ -2802,7 +2802,7 @@ static int snd_vt1724_resume(struct pci_dev *pci)
#endif
static struct pci_driver driver = {
- .name = "ICE1724",
+ .name = KBUILD_MODNAME,
.id_table = snd_vt1724_ids,
.probe = snd_vt1724_probe,
.remove = __devexit_p(snd_vt1724_remove),
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 6c896db..6a5b387 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -1884,6 +1884,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x1028,
+ .subdevice = 0x0189,
+ .name = "Dell Inspiron 9300",
+ .type = AC97_TUNE_HP_MUTE_LED
+ },
+ {
+ .subvendor = 0x1028,
.subdevice = 0x0191,
.name = "Dell Inspiron 8600",
.type = AC97_TUNE_HP_ONLY
@@ -2647,7 +2653,7 @@ static int intel8x0_resume(struct pci_dev *pci)
pci_set_master(pci);
snd_intel8x0_chip_init(chip, 0);
if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
printk(KERN_ERR "intel8x0: unable to grab IRQ %d, "
"disabling device\n", pci->irq);
snd_card_disconnect(card);
@@ -3106,7 +3112,7 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
/* request irq after initializaing int_sta_mask, etc */
if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_intel8x0_free(chip);
return -EBUSY;
@@ -3266,7 +3272,7 @@ static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Intel ICH",
+ .name = KBUILD_MODNAME,
.id_table = snd_intel8x0_ids,
.probe = snd_intel8x0_probe,
.remove = __devexit_p(snd_intel8x0_remove),
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index f3353b4..7c16164 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1047,7 +1047,7 @@ static int intel8x0m_resume(struct pci_dev *pci)
}
pci_set_master(pci);
if (request_irq(pci->irq, snd_intel8x0m_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
printk(KERN_ERR "intel8x0m: unable to grab IRQ %d, "
"disabling device\n", pci->irq);
snd_card_disconnect(card);
@@ -1174,7 +1174,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
port_inited:
if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_intel8x0m_free(chip);
return -EBUSY;
@@ -1325,7 +1325,7 @@ static void __devexit snd_intel8x0m_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Intel ICH Modem",
+ .name = KBUILD_MODNAME,
.id_table = snd_intel8x0m_ids,
.probe = snd_intel8x0m_probe,
.remove = __devexit_p(snd_intel8x0m_remove),
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 6d79570..fc1d573c 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -2241,7 +2241,7 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
err = request_irq(pci->irq, snd_korg1212_interrupt,
IRQF_SHARED,
- "korg1212", korg1212);
+ KBUILD_MODNAME, korg1212);
if (err) {
snd_printk(KERN_ERR "korg1212: unable to grab IRQ %d\n", pci->irq);
@@ -2477,7 +2477,7 @@ static void __devexit snd_korg1212_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "korg1212",
+ .name = KBUILD_MODNAME,
.id_table = snd_korg1212_ids,
.probe = snd_korg1212_probe,
.remove = __devexit_p(snd_korg1212_remove),
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
index 2692e5a..3e92e5b 100644
--- a/sound/pci/lola/lola.c
+++ b/sound/pci/lola/lola.c
@@ -648,7 +648,7 @@ static int __devinit lola_create(struct snd_card *card, struct pci_dev *pci,
goto errout;
if (request_irq(pci->irq, lola_interrupt, IRQF_SHARED,
- DRVNAME, chip)) {
+ KBUILD_MODNAME, chip)) {
printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
goto errout;
@@ -771,7 +771,7 @@ MODULE_DEVICE_TABLE(pci, lola_ids);
/* pci_driver definition */
static struct pci_driver driver = {
- .name = DRVNAME,
+ .name = KBUILD_MODNAME,
.id_table = lola_ids,
.probe = lola_probe,
.remove = __devexit_p(lola_remove),
diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h
index d5708e2..f0b1000 100644
--- a/sound/pci/lola/lola.h
+++ b/sound/pci/lola/lola.h
@@ -480,7 +480,7 @@ struct lola {
/* 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)
+#define LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(res) ((res >> 7) & 0x1f)
int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
unsigned int data, unsigned int extdata);
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
index 5d518f1..6b8d648 100644
--- a/sound/pci/lola/lola_mixer.c
+++ b/sound/pci/lola/lola_mixer.c
@@ -144,40 +144,61 @@ int __devinit lola_init_mixer_widget(struct lola *chip, int nid)
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
+ /* mixer matrix may 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-------------------------------
+ LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(val);
+
+ /* example : MixerMatrix of LoLa881 (LoLa16161 uses unused zones)
+ * +-+ 0-------8------16-------8------16
+ * | | | | | | |
+ * |s| | INPUT | | INPUT | |
+ * | |->| -> |unused | -> |unused |
+ * |r| |CAPTURE| | OUTPUT| |
+ * | | | MIX | | MIX | |
+ * |c| 8--------------------------------
+ * | | | | | | |
+ * | | | | | | |
+ * |g| |unused |unused |unused |unused |
+ * | | | | | | |
+ * |a| | | | | |
+ * | | 16-------------------------------
+ * |i| | | | | |
+ * | | | PLAYBK| | PLAYBK| |
+ * |n|->| -> |unused | -> |unused |
+ * | | |CAPTURE| | OUTPUT| |
+ * | | | MIX | | MIX | |
+ * |a| 8--------------------------------
+ * |r| | | | | |
+ * |r| | | | | |
+ * |a| |unused |unused |unused |unused |
+ * |y| | | | | |
+ * | | | | | | |
+ * +++ 16--|---------------|------------
+ * +---V---------------V-----------+
+ * | dest_mix_gain_enable array |
+ * +-------------------------------+
+ */
+ /* example : MixerMatrix of LoLa280
+ * +-+ 0-------8-2
+ * | | | | |
+ * |s| | INPUT | | INPUT
+ * |r|->| -> | | ->
+ * |c| |CAPTURE| | <- OUTPUT
+ * | | | MIX | | MIX
+ * |g| 8----------
+ * |a| | | |
+ * |i| | PLAYBK| | PLAYBACK
+ * |n|->| -> | | ->
+ * | | |CAPTURE| | <- OUTPUT
+ * |a| | MIX | | MIX
+ * |r| 8---|----|-
+ * |r| +---V----V-------------------+
+ * |a| | dest_mix_gain_enable array |
+ * |y| +----------------------------+
*/
if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
@@ -192,6 +213,9 @@ int __devinit lola_init_mixer_widget(struct lola *chip, int nid)
(((1U << chip->mixer.dest_phys_outs) - 1)
<< chip->mixer.dest_phys_out_ofs);
+ snd_printdd("Mixer src_mask=%x, dest_mask=%x\n",
+ chip->mixer.src_mask, chip->mixer.dest_mask);
+
return 0;
}
@@ -202,12 +226,19 @@ static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
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);
+ /* test if values unchanged */
+ if ((val == oldval) &&
+ (gain == readw(&chip->mixer.array->src_gain[id])))
+ return 0;
+
+ snd_printdd("lola_mixer_set_src_gain (id=%d, gain=%d) enable=%x\n",
+ id, gain, val);
+ writew(gain, &chip->mixer.array->src_gain[id]);
writel(val, &chip->mixer.array->src_gain_enable);
lola_codec_flush(chip);
/* inform micro-controller about the new source gain */
@@ -269,6 +300,7 @@ static int lola_mixer_set_mapping_gain(struct lola *chip,
src, dest);
}
+#if 0 /* not used */
static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
unsigned int mask, unsigned short *gains)
{
@@ -289,6 +321,7 @@ static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
return lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
}
+#endif /* not used */
/*
*/
@@ -376,6 +409,8 @@ static int set_analog_volume(struct lola *chip, int dir,
return 0;
if (external_call)
lola_codec_flush(chip);
+ snd_printdd("set_analog_volume (dir=%d idx=%d, volume=%d)\n",
+ dir, idx, val);
err = lola_codec_write(chip, pin->nid,
LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
if (err < 0)
@@ -427,23 +462,40 @@ static int init_mixer_values(struct lola *chip)
{
int i;
- /* all src on */
+ /* all sample rate converters on */
lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
- /* clear all matrix */
+ /* clear all mixer matrix settings */
memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
- /* set src gain to 0dB */
+ /* inform firmware about all updated matrix columns - capture part */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ i, 0);
+ /* inform firmware about all updated matrix columns - output part */
+ 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);
+
+ /* set all digital input source (master) gains to 0dB */
for (i = 0; i < chip->mixer.src_phys_ins; i++)
lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
+
+ /* set all digital playback source (master) gains to 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 */
+ /* set gain value 0dB diagonally in matrix - part INPUT -> CAPTURE */
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);
}
+ /* set gain value 0dB diagonally in matrix , part PLAYBACK -> OUTPUT
+ * (LoLa280 : playback channel 0,2,4,6 linked to output channel 0)
+ * (LoLa280 : playback channel 1,3,5,7 linked to output channel 1)
+ */
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 +
@@ -693,6 +745,7 @@ static int __devinit create_src_gain_mixer(struct lola *chip,
snd_ctl_new1(&lola_src_gain_mixer, chip));
}
+#if 0 /* not used */
/*
* destination gain (matrix-like) mixer
*/
@@ -781,6 +834,7 @@ static int __devinit create_dest_gain_mixer(struct lola *chip,
return snd_ctl_add(chip->card,
snd_ctl_new1(&lola_dest_gain_mixer, chip));
}
+#endif /* not used */
/*
*/
@@ -798,14 +852,16 @@ int __devinit lola_create_mixer(struct lola *chip)
if (err < 0)
return err;
err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
- "Line Source Gain Volume");
+ "Digital Capture 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");
+ "Digital Playback Volume");
if (err < 0)
return err;
+#if 0
+/* FIXME: buggy mixer matrix handling */
err = create_dest_gain_mixer(chip,
chip->mixer.src_phys_ins, 0,
chip->mixer.dest_stream_ins, 0,
@@ -834,6 +890,6 @@ int __devinit lola_create_mixer(struct lola *chip)
"Stream Playback Volume");
if (err < 0)
return err;
-
+#endif /* FIXME */
return init_mixer_values(chip);
}
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index 1bd7a54..04ae84b2 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -762,7 +762,6 @@ static int lx_set_granularity(struct lx6464es *chip, u32 gran)
static int __devinit lx_init_dsp(struct lx6464es *chip)
{
int err;
- u8 mac_address[6];
int i;
snd_printdd("->lx_init_dsp\n");
@@ -787,11 +786,11 @@ static int __devinit lx_init_dsp(struct lx6464es *chip)
/** \todo the mac address should be ready by not, but it isn't,
* so we wait for it */
for (i = 0; i != 1000; ++i) {
- err = lx_dsp_get_mac(chip, mac_address);
+ err = lx_dsp_get_mac(chip);
if (err)
return err;
- if (mac_address[0] || mac_address[1] || mac_address[2] ||
- mac_address[3] || mac_address[4] || mac_address[5])
+ if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] ||
+ chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5])
goto mac_ready;
msleep(1);
}
@@ -800,8 +799,8 @@ static int __devinit lx_init_dsp(struct lx6464es *chip)
mac_ready:
snd_printd(LXP "mac address ready read after: %dms\n", i);
snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
- mac_address[0], mac_address[1], mac_address[2],
- mac_address[3], mac_address[4], mac_address[5]);
+ chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
err = lx_init_get_version_features(chip);
if (err)
@@ -1031,7 +1030,7 @@ static int __devinit snd_lx6464es_create(struct snd_card *card,
chip->port_dsp_bar = pci_ioremap_bar(pci, 2);
err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED,
- card_name, chip);
+ KBUILD_MODNAME, chip);
if (err) {
snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq);
goto request_irq_failed;
@@ -1108,8 +1107,14 @@ static int __devinit snd_lx6464es_probe(struct pci_dev *pci,
goto out_free;
}
- strcpy(card->driver, "lx6464es");
- strcpy(card->shortname, "Digigram LX6464ES");
+ strcpy(card->driver, "LX6464ES");
+ sprintf(card->id, "LX6464ES_%02X%02X%02X",
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
+
+ sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X",
+ chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
+
sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i",
card->shortname, chip->port_plx,
chip->port_dsp_bar, chip->irq);
@@ -1137,7 +1142,7 @@ static void __devexit snd_lx6464es_remove(struct pci_dev *pci)
static struct pci_driver driver = {
- .name = "Digigram LX6464ES",
+ .name = KBUILD_MODNAME,
.id_table = snd_lx6464es_ids,
.probe = snd_lx6464es_probe,
.remove = __devexit_p(snd_lx6464es_remove),
diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h
index aea621e..e2a124a 100644
--- a/sound/pci/lx6464es/lx6464es.h
+++ b/sound/pci/lx6464es/lx6464es.h
@@ -69,6 +69,8 @@ struct lx6464es {
struct pci_dev *pci;
int irq;
+ u8 mac_address[6];
+
spinlock_t lock; /* interrupt spinlock */
struct mutex setup_mutex; /* mutex used in hw_params, open
* and close */
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
index 617f98b..5c8717e 100644
--- a/sound/pci/lx6464es/lx_core.c
+++ b/sound/pci/lx6464es/lx_core.c
@@ -424,7 +424,7 @@ int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
return ret;
}
-int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address)
+int lx_dsp_get_mac(struct lx6464es *chip)
{
u32 macmsb, maclsb;
@@ -432,12 +432,12 @@ int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address)
maclsb = lx_dsp_reg_read(chip, eReg_ADMACESLSB) & 0x00FFFFFF;
/* todo: endianess handling */
- mac_address[5] = ((u8 *)(&maclsb))[0];
- mac_address[4] = ((u8 *)(&maclsb))[1];
- mac_address[3] = ((u8 *)(&maclsb))[2];
- mac_address[2] = ((u8 *)(&macmsb))[0];
- mac_address[1] = ((u8 *)(&macmsb))[1];
- mac_address[0] = ((u8 *)(&macmsb))[2];
+ chip->mac_address[5] = ((u8 *)(&maclsb))[0];
+ chip->mac_address[4] = ((u8 *)(&maclsb))[1];
+ chip->mac_address[3] = ((u8 *)(&maclsb))[2];
+ chip->mac_address[2] = ((u8 *)(&macmsb))[0];
+ chip->mac_address[1] = ((u8 *)(&macmsb))[1];
+ chip->mac_address[0] = ((u8 *)(&macmsb))[2];
return 0;
}
diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h
index 6bd9cbb..1dd5629 100644
--- a/sound/pci/lx6464es/lx_core.h
+++ b/sound/pci/lx6464es/lx_core.h
@@ -116,7 +116,7 @@ int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version);
int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq);
int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran);
int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data);
-int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address);
+int lx_dsp_get_mac(struct lx6464es *chip);
/* low-level pipe handling */
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 3c40d72..0378126 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -850,11 +850,10 @@ struct snd_m3 {
struct input_dev *input_dev;
char phys[64]; /* physical device path */
#else
- spinlock_t ac97_lock;
struct snd_kcontrol *master_switch;
struct snd_kcontrol *master_volume;
- struct tasklet_struct hwvol_tq;
#endif
+ struct work_struct hwvol_work;
unsigned int in_suspend;
@@ -1609,13 +1608,10 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s)
(without wrap around) in response to volume button presses and then
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
-static void snd_m3_update_hw_volume(unsigned long private_data)
+static void snd_m3_update_hw_volume(struct work_struct *work)
{
- struct snd_m3 *chip = (struct snd_m3 *) private_data;
+ struct snd_m3 *chip = container_of(work, struct snd_m3, hwvol_work);
int x, val;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- unsigned long flags;
-#endif
/* Figure out which volume control button was pushed,
based on differences from the default register
@@ -1645,21 +1641,13 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
if (!chip->master_switch || !chip->master_volume)
return;
- /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
- spin_lock_irqsave(&chip->ac97_lock, flags);
-
- val = chip->ac97->regs[AC97_MASTER_VOL];
+ val = snd_ac97_read(chip->ac97, AC97_MASTER);
switch (x) {
case 0x88:
/* The counters have not changed, yet we've received a HV
interrupt. According to tests run by various people this
happens when pressing the mute button. */
val ^= 0x8000;
- chip->ac97->regs[AC97_MASTER_VOL] = val;
- outw(val, chip->iobase + CODEC_DATA);
- outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_switch->id);
break;
case 0xaa:
/* counters increased by 1 -> volume up */
@@ -1667,11 +1655,6 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
val--;
if ((val & 0x7f00) > 0)
val -= 0x0100;
- chip->ac97->regs[AC97_MASTER_VOL] = val;
- outw(val, chip->iobase + CODEC_DATA);
- outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
case 0x66:
/* counters decreased by 1 -> volume down */
@@ -1679,14 +1662,11 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
val++;
if ((val & 0x7f00) < 0x1f00)
val += 0x0100;
- chip->ac97->regs[AC97_MASTER_VOL] = val;
- outw(val, chip->iobase + CODEC_DATA);
- outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
}
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
+ if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_switch->id);
#else
if (!chip->input_dev)
return;
@@ -1730,11 +1710,7 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (status & HV_INT_PENDING)
-#ifdef CONFIG_SND_MAESTRO3_INPUT
- snd_m3_update_hw_volume((unsigned long)chip);
-#else
- tasklet_schedule(&chip->hwvol_tq);
-#endif
+ schedule_work(&chip->hwvol_work);
/*
* ack an assp int if its running
@@ -2000,24 +1976,14 @@ static unsigned short
snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_m3 *chip = ac97->private_data;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- unsigned long flags;
-#endif
unsigned short data = 0xffff;
if (snd_m3_ac97_wait(chip))
goto fail;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
if (snd_m3_ac97_wait(chip))
- goto fail_unlock;
+ goto fail;
data = snd_m3_inw(chip, CODEC_DATA);
-fail_unlock:
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
fail:
return data;
}
@@ -2026,20 +1992,11 @@ static void
snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct snd_m3 *chip = ac97->private_data;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- unsigned long flags;
-#endif
if (snd_m3_ac97_wait(chip))
return;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
snd_m3_outw(chip, val, CODEC_DATA);
snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
}
@@ -2458,6 +2415,7 @@ static int snd_m3_free(struct snd_m3 *chip)
struct m3_dma *s;
int i;
+ cancel_work_sync(&chip->hwvol_work);
#ifdef CONFIG_SND_MAESTRO3_INPUT
if (chip->input_dev)
input_unregister_device(chip->input_dev);
@@ -2511,6 +2469,7 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state)
return 0;
chip->in_suspend = 1;
+ cancel_work_sync(&chip->hwvol_work);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
@@ -2667,9 +2626,6 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
}
spin_lock_init(&chip->reg_lock);
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_lock_init(&chip->ac97_lock);
-#endif
switch (pci->device) {
case PCI_DEVICE_ID_ESS_ALLEGRO:
@@ -2683,6 +2639,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
+ INIT_WORK(&chip->hwvol_work, snd_m3_update_hw_volume);
chip->external_amp = enable_amp;
if (amp_gpio >= 0 && amp_gpio <= 0x0f)
@@ -2752,12 +2709,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
snd_m3_hv_init(chip);
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
-#endif
-
if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
- card->driver, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_m3_free(chip);
return -ENOMEM;
@@ -2885,7 +2838,7 @@ static void __devexit snd_m3_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Maestro3",
+ .name = KBUILD_MODNAME,
.id_table = snd_m3_ids,
.probe = snd_m3_probe,
.remove = __devexit_p(snd_m3_remove),
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 6c3fd4d..dbee599 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1268,7 +1268,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
}
if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_SHARED,
- CARD_NAME, mgr)) {
+ KBUILD_MODNAME, mgr)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_mixart_free(mgr);
return -EBUSY;
@@ -1381,7 +1381,7 @@ static void __devexit snd_mixart_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Digigram miXart",
+ .name = KBUILD_MODNAME,
.id_table = snd_mixart_ids,
.probe = snd_mixart_probe,
.remove = __devexit_p(snd_mixart_remove),
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 5a60492..83ea7a7 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -465,7 +465,7 @@ static int snd_nm256_acquire_irq(struct nm256 *chip)
mutex_lock(&chip->irq_mutex);
if (chip->irq < 0) {
if (request_irq(chip->pci->irq, chip->interrupt, IRQF_SHARED,
- chip->card->driver, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->pci->irq);
mutex_unlock(&chip->irq_mutex);
return -EBUSY;
@@ -1743,7 +1743,7 @@ static void __devexit snd_nm256_remove(struct pci_dev *pci)
static struct pci_driver driver = {
- .name = "NeoMagic 256",
+ .name = KBUILD_MODNAME,
.id_table = snd_nm256_ids,
.probe = snd_nm256_probe,
.remove = __devexit_p(snd_nm256_remove),
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index d7e8ddd..218d985 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -859,7 +859,7 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,
}
static struct pci_driver oxygen_driver = {
- .name = "CMI8788",
+ .name = KBUILD_MODNAME,
.id_table = oxygen_ids,
.probe = generic_oxygen_probe,
.remove = __devexit_p(oxygen_pci_remove),
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 70b7398..82311fc 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -655,7 +655,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
chip->model.init(chip);
err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
- DRIVER, chip);
+ KBUILD_MODNAME, chip);
if (err < 0) {
snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
goto err_card;
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index d5533e3..cc0bcd9 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -168,12 +168,6 @@ static int oxygen_open(struct snd_pcm_substream *substream,
if (err < 0)
return err;
}
- if (channel == PCM_MULTICH) {
- err = snd_pcm_hw_constraint_minmax
- (runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0, 8192000);
- if (err < 0)
- return err;
- }
snd_pcm_set_sync(substream);
chip->streams[channel] = substream;
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 469010a..773db79 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -88,7 +88,7 @@ static int __devinit xonar_probe(struct pci_dev *pci,
}
static struct pci_driver xonar_driver = {
- .name = "AV200",
+ .name = KBUILD_MODNAME,
.id_table = xonar_ids,
.probe = xonar_probe,
.remove = __devexit_p(oxygen_pci_remove),
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c
index 54cad38..32d096c 100644
--- a/sound/pci/oxygen/xonar_pcm179x.c
+++ b/sound/pci/oxygen/xonar_pcm179x.c
@@ -327,8 +327,10 @@ static void pcm1796_init(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
- data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
+ data->pcm1796_regs[0][18 - PCM1796_REG_BASE] =
PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
+ if (!data->broken_i2c)
+ data->pcm1796_regs[0][18 - PCM1796_REG_BASE] |= PCM1796_MUTE;
data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
PCM1796_FLT_SHARP | PCM1796_ATS_1;
data->pcm1796_regs[0][20 - PCM1796_REG_BASE] =
@@ -1123,6 +1125,7 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
chip->model.control_filter = xonar_st_h6_control_filter;
chip->model.dac_channels_pcm = 8;
chip->model.dac_channels_mixer = 8;
+ chip->model.dac_volume_min = 255;
chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
break;
}
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 95cfde2..046578d 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1501,7 +1501,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
mgr->irq = -1;
if (request_irq(pci->irq, pcxhr_interrupt, IRQF_SHARED,
- card_name, mgr)) {
+ KBUILD_MODNAME, mgr)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
pcxhr_free(mgr);
return -EBUSY;
@@ -1608,7 +1608,7 @@ static void __devexit pcxhr_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Digigram pcxhr",
+ .name = KBUILD_MODNAME,
.id_table = pcxhr_ids,
.probe = pcxhr_probe,
.remove = __devexit_p(pcxhr_remove),
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index ad5202e..e34ae14 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -1890,7 +1890,7 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci,
UNSET_AIE(hwport);
if (request_irq(pci->irq, snd_riptide_interrupt, IRQF_SHARED,
- "RIPTIDE", chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n",
pci->irq);
snd_riptide_free(chip);
@@ -2176,7 +2176,7 @@ static void __devexit snd_card_riptide_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "RIPTIDE",
+ .name = KBUILD_MODNAME,
.id_table = snd_riptide_ids,
.probe = snd_card_riptide_probe,
.remove = __devexit_p(snd_card_riptide_remove),
@@ -2188,7 +2188,7 @@ static struct pci_driver driver = {
#ifdef SUPPORT_JOYSTICK
static struct pci_driver joystick_driver = {
- .name = "Riptide Joystick",
+ .name = KBUILD_MODNAME "-joystick",
.id_table = snd_riptide_joystick_ids,
.probe = snd_riptide_joystick_probe,
.remove = __devexit_p(snd_riptide_joystick_remove),
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 3c04524..6be77a2 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1355,7 +1355,7 @@ static int __devinit snd_rme32_create(struct rme32 * rme32)
}
if (request_irq(pci->irq, snd_rme32_interrupt, IRQF_SHARED,
- "RME32", rme32)) {
+ KBUILD_MODNAME, rme32)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
@@ -1985,7 +1985,7 @@ static void __devexit snd_rme32_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "RME Digi32",
+ .name = KBUILD_MODNAME,
.id_table = snd_rme32_ids,
.probe = snd_rme32_probe,
.remove = __devexit_p(snd_rme32_remove),
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 9ff247f..409e5b8 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1561,7 +1561,7 @@ snd_rme96_create(struct rme96 *rme96)
}
if (request_irq(pci->irq, snd_rme96_interrupt, IRQF_SHARED,
- "RME96", rme96)) {
+ KBUILD_MODNAME, rme96)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
@@ -2396,7 +2396,7 @@ static void __devexit snd_rme96_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "RME Digi96",
+ .name = KBUILD_MODNAME,
.id_table = snd_rme96_ids,
.probe = snd_rme96_probe,
.remove = __devexit_p(snd_rme96_remove),
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 2d83324..1c6d1e1 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -5482,7 +5482,7 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_hdsp_interrupt, IRQF_SHARED,
- "hdsp", hdsp)) {
+ KBUILD_MODNAME, hdsp)) {
snd_printk(KERN_ERR "Hammerfall-DSP: unable to use IRQ %d\n", pci->irq);
return -EBUSY;
}
@@ -5637,7 +5637,7 @@ static void __devexit snd_hdsp_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "RME Hammerfall DSP",
+ .name = KBUILD_MODNAME,
.id_table = snd_hdsp_ids,
.probe = snd_hdsp_probe,
.remove = __devexit_p(snd_hdsp_remove),
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index c8e402f..af130ee 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6441,7 +6441,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card,
hdspm->port + io_extent - 1);
if (request_irq(pci->irq, snd_hdspm_interrupt,
- IRQF_SHARED, "hdspm", hdspm)) {
+ IRQF_SHARED, KBUILD_MODNAME, hdspm)) {
snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
return -EBUSY;
}
@@ -6779,7 +6779,7 @@ static void __devexit snd_hdspm_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "RME Hammerfall DSP MADI",
+ .name = KBUILD_MODNAME,
.id_table = snd_hdspm_ids,
.probe = snd_hdspm_probe,
.remove = __devexit_p(snd_hdspm_remove),
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index c492af5..1c7bc1e 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -2479,7 +2479,7 @@ static int __devinit snd_rme9652_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_rme9652_interrupt, IRQF_SHARED,
- "rme9652", rme9652)) {
+ KBUILD_MODNAME, rme9652)) {
snd_printk(KERN_ERR "unable to request IRQ %d\n", pci->irq);
return -EBUSY;
}
@@ -2632,7 +2632,7 @@ static void __devexit snd_rme9652_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "RME Digi9652 (Hammerfall)",
+ .name = KBUILD_MODNAME,
.id_table = snd_rme9652_ids,
.probe = snd_rme9652_probe,
.remove = __devexit_p(snd_rme9652_remove),
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 2b5c7a95..bcf6152 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1235,7 +1235,7 @@ static int sis_resume(struct pci_dev *pci)
}
if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
- card->shortname, sis)) {
+ KBUILD_MODNAME, sis)) {
printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq);
goto error;
}
@@ -1341,7 +1341,7 @@ static int __devinit sis_chip_create(struct snd_card *card,
goto error_out_cleanup;
if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
- card->shortname, sis)) {
+ KBUILD_MODNAME, sis)) {
printk(KERN_ERR "unable to allocate irq %d\n", sis->irq);
goto error_out_cleanup;
}
@@ -1436,7 +1436,7 @@ static void __devexit snd_sis7019_remove(struct pci_dev *pci)
}
static struct pci_driver sis7019_driver = {
- .name = "SiS7019",
+ .name = KBUILD_MODNAME,
.id_table = snd_sis7019_ids,
.probe = snd_sis7019_probe,
.remove = __devexit_p(snd_sis7019_remove),
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 337b9fa..2571a67 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -1294,7 +1294,7 @@ static int __devinit snd_sonicvibes_create(struct snd_card *card,
sonic->game_port = pci_resource_start(pci, 4);
if (request_irq(pci->irq, snd_sonicvibes_interrupt, IRQF_SHARED,
- "S3 SonicVibes", sonic)) {
+ KBUILD_MODNAME, sonic)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_sonicvibes_free(sonic);
return -EBUSY;
@@ -1530,7 +1530,7 @@ static void __devexit snd_sonic_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "S3 SonicVibes",
+ .name = KBUILD_MODNAME,
.id_table = snd_sonic_ids,
.probe = snd_sonic_probe,
.remove = __devexit_p(snd_sonic_remove),
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 6d05818..d8a128f 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -172,7 +172,7 @@ static void __devexit snd_trident_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Trident4DWaveAudio",
+ .name = KBUILD_MODNAME,
.id_table = snd_trident_ids,
.probe = snd_trident_probe,
.remove = __devexit_p(snd_trident_remove),
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index 2870a4f..5bd57a7 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -3598,7 +3598,7 @@ int __devinit snd_trident_create(struct snd_card *card,
trident->port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_trident_interrupt, IRQF_SHARED,
- "Trident Audio", trident)) {
+ KBUILD_MODNAME, trident)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_trident_free(trident);
return -EBUSY;
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 8c5f8b5..f03fd62 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -2377,7 +2377,7 @@ static int __devinit snd_via82xx_create(struct snd_card *card,
chip_type == TYPE_VIA8233 ?
snd_via8233_interrupt : snd_via686_interrupt,
IRQF_SHARED,
- card->driver, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_via82xx_free(chip);
return -EBUSY;
@@ -2611,7 +2611,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "VIA 82xx Audio",
+ .name = KBUILD_MODNAME,
.id_table = snd_via82xx_ids,
.probe = snd_via82xx_probe,
.remove = __devexit_p(snd_via82xx_remove),
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index f7e8bbbe..a386dd9 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -1129,7 +1129,7 @@ static int __devinit snd_via82xx_create(struct snd_card *card,
}
chip->port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_via82xx_interrupt, IRQF_SHARED,
- card->driver, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_via82xx_free(chip);
return -EBUSY;
@@ -1224,7 +1224,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "VIA 82xx Modem",
+ .name = KBUILD_MODNAME,
.id_table = snd_via82xx_modem_ids,
.probe = snd_via82xx_probe,
.remove = __devexit_p(snd_via82xx_remove),
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 99a9a81..5342d5e 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -169,7 +169,7 @@ static int __devinit snd_vx222_create(struct snd_card *card, struct pci_dev *pci
vx->port[i] = pci_resource_start(pci, i + 1);
if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_SHARED,
- CARD_NAME, chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_vx222_free(chip);
return -EBUSY;
@@ -290,7 +290,7 @@ static int snd_vx222_resume(struct pci_dev *pci)
#endif
static struct pci_driver driver = {
- .name = "Digigram VX222",
+ .name = KBUILD_MODNAME,
.id_table = snd_vx222_ids,
.probe = snd_vx222_probe,
.remove = __devexit_p(snd_vx222_remove),
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 80c6821..511d576 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -345,7 +345,7 @@ static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci)
}
static struct pci_driver driver = {
- .name = "Yamaha DS-1 PCI",
+ .name = KBUILD_MODNAME,
.id_table = snd_ymfpci_ids,
.probe = snd_card_ymfpci_probe,
.remove = __devexit_p(snd_card_ymfpci_remove),
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index c94c051..f3260e6 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -2380,7 +2380,7 @@ int __devinit snd_ymfpci_create(struct snd_card *card,
return -EBUSY;
}
if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED,
- "YMFPCI", chip)) {
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_ymfpci_free(chip);
return -EBUSY;
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
index ce33be0..66488a7 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c
@@ -223,7 +223,7 @@ static int pdacf_config(struct pcmcia_device *link)
if (ret)
goto failed;
- ret = pcmcia_request_exclusive_irq(link, pdacf_interrupt);
+ ret = pcmcia_request_irq(link, pdacf_interrupt);
if (ret)
goto failed;
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index d9ef21d..31777d1 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -229,7 +229,7 @@ static int vxpocket_config(struct pcmcia_device *link)
if (ret)
goto failed;
- ret = pcmcia_request_exclusive_irq(link, snd_vx_irq_handler);
+ ret = pcmcia_request_irq(link, snd_vx_irq_handler);
if (ret)
goto failed;
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 1ed61c5..4f91387 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,5 @@
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
+snd-soc-core-objs += soc-pcm.o soc-io.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
index d0e7532..f81d4c3 100644
--- a/sound/soc/atmel/atmel-pcm.c
+++ b/sound/soc/atmel/atmel-pcm.c
@@ -364,9 +364,11 @@ static struct snd_pcm_ops atmel_pcm_ops = {
\*--------------------------------------------------------------------------*/
static u64 atmel_pcm_dmamask = 0xffffffff;
-static int atmel_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
@@ -382,7 +384,7 @@ static int atmel_pcm_new(struct snd_card *card,
}
if (dai->driver->capture.channels_min) {
- pr_debug("at32-pcm:"
+ pr_debug("atmel-pcm:"
"Allocating PCM capture DMA buffer\n");
ret = atmel_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
index 2597329..5e0a95e 100644
--- a/sound/soc/atmel/atmel-pcm.h
+++ b/sound/soc/atmel/atmel-pcm.h
@@ -60,7 +60,7 @@ struct atmel_ssc_mask {
* This structure, shared between the PCM driver and the interface,
* contains all information required by the PCM driver to perform the
* PDC DMA operation. All fields except dma_intr_handler() are initialized
- * by the interface. The dms_intr_handler() pointer is set by the PCM
+ * by the interface. The dma_intr_handler() pointer is set by the PCM
* driver and called by the interface SSC interrupt handler if it is
* non-NULL.
*/
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index eda955b..7122509 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -402,7 +402,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
&& bits > 16) {
printk(KERN_WARNING
- "atmel_ssc_dai: sample size %d"
+ "atmel_ssc_dai: sample size %d "
"is too large for I2S\n", bits);
return -EINVAL;
}
@@ -838,10 +838,8 @@ int atmel_ssc_set_audio(int ssc_id)
}
ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
- if (!ssc_pdev) {
- ssc_free(ssc);
+ if (!ssc_pdev)
return -ENOMEM;
- }
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 95572d2..bad3aa1 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -92,6 +92,7 @@ static struct snd_soc_ops at91sam9g20ek_ops = {
};
static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
static int mclk_on;
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 10fdd28..20bb53a 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -319,10 +319,11 @@ static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
-static int au1xpsc_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index ae40359..fe9d548 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -10,13 +10,36 @@ config SND_BF5XX_I2S
config SND_BF5XX_SOC_SSM2602
tristate "SoC SSM2602 Audio support for BF52x ezkit"
- depends on SND_BF5XX_I2S
+ depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
select SND_BF5XX_SOC_I2S
select SND_SOC_SSM2602
- select I2C
help
Say Y if you want to add support for SoC audio on BF527-EZKIT.
+config SND_SOC_BFIN_EVAL_ADAU1701
+ tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards"
+ depends on SND_BF5XX_I2S
+ select SND_BF5XX_SOC_I2S
+ select SND_SOC_ADAU1701
+ select I2C
+ help
+ Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ
+ board connected to one of the Blackfin evaluation boards like the
+ BF5XX-STAMP or BF5XX-EZKIT.
+
+config SND_SOC_BFIN_EVAL_ADAV80X
+ tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
+ depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
+ select SND_BF5XX_SOC_I2S
+ select SND_SOC_ADAV80X
+ help
+ Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or
+ EVAL-ADAV803 board connected to one of the Blackfin evaluation boards
+ like the BF5XX-STAMP or BF5XX-EZKIT.
+
+ Note: This driver assumes that the ADAV80X digital record and playback
+ interfaces are connected to the first SPORT port on the BF5XX board.
+
config SND_BF5XX_SOC_AD73311
tristate "SoC AD73311 Audio support for Blackfin"
depends on SND_BF5XX_I2S
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 49af3f3..6018bf5 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -21,9 +21,13 @@ snd-ad1980-objs := bf5xx-ad1980.o
snd-ssm2602-objs := bf5xx-ssm2602.o
snd-ad73311-objs := bf5xx-ad73311.o
snd-ad193x-objs := bf5xx-ad193x.o
+snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
+snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
+obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
+obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 98b44b3..9e59f68 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -418,9 +418,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
-int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
pr_debug("%s enter\n", __func__);
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index b5101ef..61ddf94 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -138,11 +138,20 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
pr_debug("%s enter\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff = sport_curr_offset_tx(sport);
- frames = bytes_to_frames(substream->runtime, diff);
} else {
diff = sport_curr_offset_rx(sport);
- frames = bytes_to_frames(substream->runtime, diff);
}
+
+ /*
+ * TX at least can report one frame beyond the end of the
+ * buffer if we hit the wraparound case - clamp to within the
+ * buffer as the ALSA APIs require.
+ */
+ if (diff == snd_pcm_lib_buffer_bytes(substream))
+ diff = 0;
+
+ frames = bytes_to_frames(substream->runtime, diff);
+
return frames;
}
@@ -159,7 +168,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
- ret = snd_pcm_hw_constraint_integer(runtime, \
+ ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
goto out;
@@ -248,9 +257,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
-int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
pr_debug("%s enter\n", __func__);
@@ -295,8 +306,8 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_i2s_pcm_driver = {
.driver = {
- .name = "bfin-i2s-pcm-audio",
- .owner = THIS_MODULE,
+ .name = "bfin-i2s-pcm-audio",
+ .owner = THIS_MODULE,
},
.probe = bfin_i2s_soc_platform_probe,
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index 07cfc7a..c95cc03 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -283,9 +283,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
-static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c
new file mode 100644
index 0000000..e5550ac
--- /dev/null
+++ b/sound/soc/blackfin/bfin-eval-adau1701.c
@@ -0,0 +1,139 @@
+/*
+ * Machine driver for EVAL-ADAU1701MINIZ on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau1701.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1701_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1701_dapm_routes[] = {
+ { "Speaker", NULL, "OUT0" },
+ { "Speaker", NULL, "OUT1" },
+ { "Line Out", NULL, "OUT2" },
+ { "Line Out", NULL, "OUT3" },
+
+ { "IN0", NULL, "Line In" },
+ { "IN1", NULL, "Line In" },
+};
+
+static int bfin_eval_adau1701_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(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret)
+ return 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)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1701_CLK_SRC_OSC, 12288000,
+ SND_SOC_CLOCK_IN);
+
+ return ret;
+}
+
+static struct snd_soc_ops bfin_eval_adau1701_ops = {
+ .hw_params = bfin_eval_adau1701_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1701_dai[] = {
+ {
+ .name = "adau1701",
+ .stream_name = "adau1701",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "adau1701",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "adau1701.0-0034",
+ .ops = &bfin_eval_adau1701_ops,
+ },
+ {
+ .name = "adau1701",
+ .stream_name = "adau1701",
+ .cpu_dai_name = "bfin-i2s.1",
+ .codec_dai_name = "adau1701",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "adau1701.0-0034",
+ .ops = &bfin_eval_adau1701_ops,
+ },
+};
+
+static struct snd_soc_card bfin_eval_adau1701 = {
+ .name = "bfin-eval-adau1701",
+ .dai_link = &bfin_eval_adau1701_dai[CONFIG_SND_BF5XX_SPORT_NUM],
+ .num_links = 1,
+
+ .dapm_widgets = bfin_eval_adau1701_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1701_dapm_widgets),
+ .dapm_routes = bfin_eval_adau1701_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1701_dapm_routes),
+};
+
+static int bfin_eval_adau1701_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &bfin_eval_adau1701;
+
+ card->dev = &pdev->dev;
+
+ return snd_soc_register_card(&bfin_eval_adau1701);
+}
+
+static int __devexit bfin_eval_adau1701_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 bfin_eval_adau1701_driver = {
+ .driver = {
+ .name = "bfin-eval-adau1701",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = bfin_eval_adau1701_probe,
+ .remove = __devexit_p(bfin_eval_adau1701_remove),
+};
+
+static int __init bfin_eval_adau1701_init(void)
+{
+ return platform_driver_register(&bfin_eval_adau1701_driver);
+}
+module_init(bfin_eval_adau1701_init);
+
+static void __exit bfin_eval_adau1701_exit(void)
+{
+ platform_driver_unregister(&bfin_eval_adau1701_driver);
+}
+module_exit(bfin_eval_adau1701_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin ADAU1701 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1701");
diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c
new file mode 100644
index 0000000..8d014d0
--- /dev/null
+++ b/sound/soc/blackfin/bfin-eval-adav80x.c
@@ -0,0 +1,173 @@
+/*
+ * Machine driver for EVAL-ADAV801 and EVAL-ADAV803 on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "../codecs/adav80x.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adav80x_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adav80x_dapm_routes[] = {
+ { "Line Out", NULL, "VOUTL" },
+ { "Line Out", NULL, "VOUTR" },
+
+ { "VINL", NULL, "Line In" },
+ { "VINR", NULL, "Line In" },
+};
+
+static int bfin_eval_adav80x_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(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret)
+ return 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)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, ADAV80X_PLL1, ADAV80X_PLL_SRC_XTAL,
+ 27000000, params_rate(params) * 256);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_PLL1,
+ params_rate(params) * 256, SND_SOC_CLOCK_IN);
+
+ return ret;
+}
+
+static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK1, 0,
+ SND_SOC_CLOCK_OUT);
+ snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK2, 0,
+ SND_SOC_CLOCK_OUT);
+ snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK3, 0,
+ SND_SOC_CLOCK_OUT);
+
+ snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_XTAL, 2700000, 0);
+
+ return 0;
+}
+
+static struct snd_soc_ops bfin_eval_adav80x_ops = {
+ .hw_params = bfin_eval_adav80x_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adav80x_dais[] = {
+ {
+ .name = "adav80x",
+ .stream_name = "ADAV80x HiFi",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "adav80x-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .init = bfin_eval_adav80x_codec_init,
+ .ops = &bfin_eval_adav80x_ops,
+ },
+};
+
+static struct snd_soc_card bfin_eval_adav80x = {
+ .name = "bfin-eval-adav80x",
+ .dai_link = bfin_eval_adav80x_dais,
+ .num_links = ARRAY_SIZE(bfin_eval_adav80x_dais),
+
+ .dapm_widgets = bfin_eval_adav80x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adav80x_dapm_widgets),
+ .dapm_routes = bfin_eval_adav80x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(bfin_eval_adav80x_dapm_routes),
+};
+
+enum bfin_eval_adav80x_type {
+ BFIN_EVAL_ADAV801,
+ BFIN_EVAL_ADAV803,
+};
+
+static int bfin_eval_adav80x_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &bfin_eval_adav80x;
+ const char *codec_name;
+
+ switch (platform_get_device_id(pdev)->driver_data) {
+ case BFIN_EVAL_ADAV801:
+ codec_name = "spi0.1";
+ break;
+ case BFIN_EVAL_ADAV803:
+ codec_name = "adav803.0-0034";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bfin_eval_adav80x_dais[0].codec_name = codec_name;
+
+ card->dev = &pdev->dev;
+
+ return snd_soc_register_card(&bfin_eval_adav80x);
+}
+
+static int __devexit bfin_eval_adav80x_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static const struct platform_device_id bfin_eval_adav80x_ids[] = {
+ { "bfin-eval-adav801", BFIN_EVAL_ADAV801 },
+ { "bfin-eval-adav803", BFIN_EVAL_ADAV803 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bfin_eval_adav80x_ids);
+
+static struct platform_driver bfin_eval_adav80x_driver = {
+ .driver = {
+ .name = "bfin-eval-adav80x",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = bfin_eval_adav80x_probe,
+ .remove = __devexit_p(bfin_eval_adav80x_remove),
+ .id_table = bfin_eval_adav80x_ids,
+};
+
+static int __init bfin_eval_adav80x_init(void)
+{
+ return platform_driver_register(&bfin_eval_adav80x_driver);
+}
+module_init(bfin_eval_adav80x_init);
+
+static void __exit bfin_eval_adav80x_exit(void)
+{
+ platform_driver_unregister(&bfin_eval_adav80x_driver);
+}
+module_exit(bfin_eval_adav80x_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adav80x driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 98175a0..36a030f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS
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_ADAV80X
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
@@ -42,6 +43,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
+ select SND_SOC_STA32X if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
@@ -71,6 +73,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8770 if SPI_MASTER
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8782
select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
@@ -84,6 +87,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8971 if I2C
select SND_SOC_WM8974 if I2C
select SND_SOC_WM8978 if I2C
+ select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
@@ -130,7 +134,14 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
-
+
+config SND_SOC_ADAU1701
+ select SIGMA
+ tristate
+
+config SND_SOC_ADAV80X
+ tristate
+
config SND_SOC_ADS117X
tristate
@@ -216,6 +227,9 @@ config SND_SOC_SPDIF
config SND_SOC_SSM2602
tristate
+config SND_SOC_STA32X
+ tristate
+
config SND_SOC_STAC9766
tristate
@@ -299,6 +313,9 @@ config SND_SOC_WM8770
config SND_SOC_WM8776
tristate
+config SND_SOC_WM8782
+ tristate
+
config SND_SOC_WM8804
tristate
@@ -338,6 +355,9 @@ config SND_SOC_WM8974
config SND_SOC_WM8978
tristate
+config SND_SOC_WM8983
+ tristate
+
config SND_SOC_WM8985
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fd85584..da9990f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -4,6 +4,8 @@ snd-soc-ad1836-objs := ad1836.o
snd-soc-ad193x-objs := ad193x.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
+snd-soc-adau1701-objs := adau1701.o
+snd-soc-adav80x-objs := adav80x.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
@@ -28,6 +30,7 @@ snd-soc-alc5623-objs := alc5623.o
snd-soc-sn95031-objs := sn95031.o
snd-soc-spdif-objs := spdif_transciever.o
snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-sta32x-objs := sta32x.o
snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
@@ -55,6 +58,7 @@ snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8770-objs := wm8770.o
snd-soc-wm8776-objs := wm8776.o
+snd-soc-wm8782-objs := wm8782.o
snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
@@ -68,6 +72,7 @@ snd-soc-wm8962-objs := wm8962.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8978-objs := wm8978.o
+snd-soc-wm8983-objs := wm8983.o
snd-soc-wm8985-objs := wm8985.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
@@ -95,6 +100,8 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
+obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.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
@@ -120,6 +127,7 @@ obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
@@ -147,6 +155,7 @@ obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
+obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o
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
@@ -160,6 +169,7 @@ obj-$(CONFIG_SND_SOC_WM8962) += snd-soc-wm8962.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
+obj-$(CONFIG_SND_SOC_WM8983) += snd-soc-wm8983.o
obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 754c496..4e5c572 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -1,19 +1,10 @@
-/*
- * File: sound/soc/codecs/ad1836.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Aug 04 2009
- * Description: Driver for AD1836 sound chip
- *
- * Modified:
- * Copyright 2009 Analog Devices Inc.
+ /*
+ * Audio Codec driver supporting:
+ * AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ * Copyright 2009-2011 Analog Devices Inc.
*
- * 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.
+ * Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
@@ -30,10 +21,15 @@
#include <linux/spi/spi.h>
#include "ad1836.h"
+enum ad1836_type {
+ AD1835,
+ AD1836,
+ AD1838,
+};
+
/* codec private data */
struct ad1836_priv {
- enum snd_soc_control_type control_type;
- void *control_data;
+ enum ad1836_type type;
};
/*
@@ -44,29 +40,60 @@ static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
static const struct soc_enum ad1836_deemp_enum =
SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
-static const struct snd_kcontrol_new ad1836_snd_controls[] = {
- /* DAC volume control */
- SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL,
- AD1836_DAC_R1_VOL, 0, 0x3FF, 0),
- SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL,
- AD1836_DAC_R2_VOL, 0, 0x3FF, 0),
- SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL,
- AD1836_DAC_R3_VOL, 0, 0x3FF, 0),
-
- /* ADC switch control */
- SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE,
- AD1836_ADCR1_MUTE, 1, 1),
- SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE,
- AD1836_ADCR2_MUTE, 1, 1),
-
- /* DAC switch control */
- SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE,
- AD1836_DACR1_MUTE, 1, 1),
- SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE,
- AD1836_DACR2_MUTE, 1, 1),
- SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE,
- AD1836_DACR3_MUTE, 1, 1),
+#define AD1836_DAC_VOLUME(x) \
+ SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \
+ AD1836_DAC_R_VOL(x), 0, 0x3FF, 0)
+
+#define AD1836_DAC_SWITCH(x) \
+ SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \
+ AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+#define AD1836_ADC_SWITCH(x) \
+ SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \
+ AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+static const struct snd_kcontrol_new ad183x_dac_controls[] = {
+ AD1836_DAC_VOLUME(1),
+ AD1836_DAC_SWITCH(1),
+ AD1836_DAC_VOLUME(2),
+ AD1836_DAC_SWITCH(2),
+ AD1836_DAC_VOLUME(3),
+ AD1836_DAC_SWITCH(3),
+ AD1836_DAC_VOLUME(4),
+ AD1836_DAC_SWITCH(4),
+};
+
+static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_route ad183x_dac_routes[] = {
+ { "DAC1OUT", NULL, "DAC" },
+ { "DAC2OUT", NULL, "DAC" },
+ { "DAC3OUT", NULL, "DAC" },
+ { "DAC4OUT", NULL, "DAC" },
+};
+
+static const struct snd_kcontrol_new ad183x_adc_controls[] = {
+ AD1836_ADC_SWITCH(1),
+ AD1836_ADC_SWITCH(2),
+ AD1836_ADC_SWITCH(3),
+};
+
+static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("ADC1IN"),
+ SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+static const struct snd_soc_dapm_route ad183x_adc_routes[] = {
+ { "ADC", NULL, "ADC1IN" },
+ { "ADC", NULL, "ADC2IN" },
+};
+static const struct snd_kcontrol_new ad183x_controls[] = {
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
AD1836_ADC_HIGHPASS_FILTER, 1, 0),
@@ -75,27 +102,24 @@ static const struct snd_kcontrol_new ad1836_snd_controls[] = {
SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
};
-static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
AD1836_DAC_POWERDOWN, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
AD1836_ADC_POWERDOWN, 1, NULL, 0),
- SND_SOC_DAPM_OUTPUT("DAC1OUT"),
- SND_SOC_DAPM_OUTPUT("DAC2OUT"),
- SND_SOC_DAPM_OUTPUT("DAC3OUT"),
- SND_SOC_DAPM_INPUT("ADC1IN"),
- SND_SOC_DAPM_INPUT("ADC2IN"),
};
-static const struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route ad183x_dapm_routes[] = {
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
- { "DAC1OUT", "DAC1 Switch", "DAC" },
- { "DAC2OUT", "DAC2 Switch", "DAC" },
- { "DAC3OUT", "DAC3 Switch", "DAC" },
- { "ADC", "ADC1 Switch", "ADC1IN" },
- { "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0);
+
+static const struct snd_kcontrol_new ad1836_controls[] = {
+ SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0,
+ ad1836_in_tlv),
};
/*
@@ -165,64 +189,69 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static struct snd_soc_dai_ops ad1836_dai_ops = {
+ .hw_params = ad1836_hw_params,
+ .set_fmt = ad1836_set_dai_fmt,
+};
+
+#define AD183X_DAI(_name, num_dacs, num_adcs) \
+{ \
+ .name = _name "-hifi", \
+ .playback = { \
+ .stream_name = "Playback", \
+ .channels_min = 2, \
+ .channels_max = (num_dacs) * 2, \
+ .rates = SNDRV_PCM_RATE_48000, \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "Capture", \
+ .channels_min = 2, \
+ .channels_max = (num_adcs) * 2, \
+ .rates = SNDRV_PCM_RATE_48000, \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+ }, \
+ .ops = &ad1836_dai_ops, \
+}
+
+static struct snd_soc_dai_driver ad183x_dais[] = {
+ [AD1835] = AD183X_DAI("ad1835", 4, 1),
+ [AD1836] = AD183X_DAI("ad1836", 3, 2),
+ [AD1838] = AD183X_DAI("ad1838", 3, 1),
+};
+
#ifdef CONFIG_PM
-static int ad1836_soc_suspend(struct snd_soc_codec *codec,
- pm_message_t state)
+static int ad1836_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
/* reset clock control mode */
- u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
- adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
-
- return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+ return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+ AD1836_ADC_SERFMT_MASK, 0);
}
-static int ad1836_soc_resume(struct snd_soc_codec *codec)
+static int ad1836_resume(struct snd_soc_codec *codec)
{
/* restore clock control mode */
- u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
- adc_ctrl2 |= AD1836_ADC_AUX;
-
- return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+ return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+ AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
}
#else
-#define ad1836_soc_suspend NULL
-#define ad1836_soc_resume NULL
+#define ad1836_suspend NULL
+#define ad1836_resume NULL
#endif
-static struct snd_soc_dai_ops ad1836_dai_ops = {
- .hw_params = ad1836_hw_params,
- .set_fmt = ad1836_set_dai_fmt,
-};
-
-/* codec DAI instance */
-static struct snd_soc_dai_driver ad1836_dai = {
- .name = "ad1836-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 6,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
- .channels_max = 4,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .ops = &ad1836_dai_ops,
-};
-
static int ad1836_probe(struct snd_soc_codec *codec)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int num_dacs, num_adcs;
int ret = 0;
+ int i;
+
+ num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2;
+ num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2;
- codec->control_data = ad1836->control_data;
ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O: %d\n",
@@ -239,21 +268,46 @@ static int ad1836_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */
snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
- /* left/right diff:PGA/MUX */
- snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
/* volume */
- snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
-
- snd_soc_add_controls(codec, ad1836_snd_controls,
- ARRAY_SIZE(ad1836_snd_controls));
- snd_soc_dapm_new_controls(dapm, ad1836_dapm_widgets,
- ARRAY_SIZE(ad1836_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
+ for (i = 1; i <= num_dacs; ++i) {
+ snd_soc_write(codec, AD1836_DAC_L_VOL(i), 0x3FF);
+ snd_soc_write(codec, AD1836_DAC_R_VOL(i), 0x3FF);
+ }
+
+ if (ad1836->type == AD1836) {
+ /* left/right diff:PGA/MUX */
+ snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
+ ret = snd_soc_add_controls(codec, ad1836_controls,
+ ARRAY_SIZE(ad1836_controls));
+ if (ret)
+ return ret;
+ } else {
+ snd_soc_write(codec, AD1836_ADC_CTRL3, 0x00);
+ }
+
+ ret = snd_soc_add_controls(codec, ad183x_dac_controls, num_dacs * 2);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_controls(codec, ad183x_adc_controls, num_adcs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
+ if (ret)
+ return ret;
return ret;
}
@@ -262,19 +316,24 @@ static int ad1836_probe(struct snd_soc_codec *codec)
static int ad1836_remove(struct snd_soc_codec *codec)
{
/* reset clock control mode */
- u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
- adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
-
- return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+ return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+ AD1836_ADC_SERFMT_MASK, 0);
}
static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
- .probe = ad1836_probe,
- .remove = ad1836_remove,
- .suspend = ad1836_soc_suspend,
- .resume = ad1836_soc_resume,
+ .probe = ad1836_probe,
+ .remove = ad1836_remove,
+ .suspend = ad1836_suspend,
+ .resume = ad1836_resume,
.reg_cache_size = AD1836_NUM_REGS,
.reg_word_size = sizeof(u16),
+
+ .controls = ad183x_controls,
+ .num_controls = ARRAY_SIZE(ad183x_controls),
+ .dapm_widgets = ad183x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
+ .dapm_routes = ad183x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
};
static int __devinit ad1836_spi_probe(struct spi_device *spi)
@@ -286,12 +345,12 @@ static int __devinit ad1836_spi_probe(struct spi_device *spi)
if (ad1836 == NULL)
return -ENOMEM;
+ ad1836->type = spi_get_device_id(spi)->driver_data;
+
spi_set_drvdata(spi, ad1836);
- ad1836->control_data = spi;
- ad1836->control_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_ad1836, &ad1836_dai, 1);
+ &soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1);
if (ret < 0)
kfree(ad1836);
return ret;
@@ -303,27 +362,29 @@ static int __devexit ad1836_spi_remove(struct spi_device *spi)
kfree(spi_get_drvdata(spi));
return 0;
}
+static const struct spi_device_id ad1836_ids[] = {
+ { "ad1835", AD1835 },
+ { "ad1836", AD1836 },
+ { "ad1837", AD1835 },
+ { "ad1838", AD1838 },
+ { "ad1839", AD1838 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, ad1836_ids);
static struct spi_driver ad1836_spi_driver = {
.driver = {
- .name = "ad1836-codec",
+ .name = "ad1836",
.owner = THIS_MODULE,
},
.probe = ad1836_spi_probe,
.remove = __devexit_p(ad1836_spi_remove),
+ .id_table = ad1836_ids,
};
static int __init ad1836_init(void)
{
- int ret;
-
- ret = spi_register_driver(&ad1836_spi_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n",
- ret);
- }
-
- return ret;
+ return spi_register_driver(&ad1836_spi_driver);
}
module_init(ad1836_init);
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
index 9d6a3f8..444747f 100644
--- a/sound/soc/codecs/ad1836.h
+++ b/sound/soc/codecs/ad1836.h
@@ -1,19 +1,10 @@
/*
- * File: sound/soc/codecs/ad1836.h
- * Based on:
- * Author: Barry Song <Barry.Song@analog.com>
+ * Audio Codec driver supporting:
+ * AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
- * Created: Aug 04, 2009
- * Description: definitions for AD1836 registers
+ * Copyright 2009-2011 Analog Devices Inc.
*
- * Modified:
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.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.
+ * Licensed under the GPL-2 or later.
*/
#ifndef __AD1836_H__
@@ -21,39 +12,30 @@
#define AD1836_DAC_CTRL1 0
#define AD1836_DAC_POWERDOWN 2
-#define AD1836_DAC_SERFMT_MASK 0xE0
+#define AD1836_DAC_SERFMT_MASK 0xE0
#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5)
#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5)
#define AD1836_DAC_WORD_LEN_MASK 0x18
#define AD1836_DAC_WORD_LEN_OFFSET 3
#define AD1836_DAC_CTRL2 1
-#define AD1836_DACL1_MUTE 0
-#define AD1836_DACR1_MUTE 1
-#define AD1836_DACL2_MUTE 2
-#define AD1836_DACR2_MUTE 3
-#define AD1836_DACL3_MUTE 4
-#define AD1836_DACR3_MUTE 5
-#define AD1836_DAC_L1_VOL 2
-#define AD1836_DAC_R1_VOL 3
-#define AD1836_DAC_L2_VOL 4
-#define AD1836_DAC_R2_VOL 5
-#define AD1836_DAC_L3_VOL 6
-#define AD1836_DAC_R3_VOL 7
+/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit
+ * for the first ADC/DAC */
+#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2)
+#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1)
+
+#define AD1836_DAC_L_VOL(x) ((x) * 2)
+#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2))
#define AD1836_ADC_CTRL1 12
#define AD1836_ADC_POWERDOWN 7
#define AD1836_ADC_HIGHPASS_FILTER 8
#define AD1836_ADC_CTRL2 13
-#define AD1836_ADCL1_MUTE 0
-#define AD1836_ADCR1_MUTE 1
-#define AD1836_ADCL2_MUTE 2
-#define AD1836_ADCR2_MUTE 3
#define AD1836_ADC_WORD_LEN_MASK 0x30
#define AD1836_ADC_WORD_OFFSET 5
-#define AD1836_ADC_SERFMT_MASK (7 << 6)
+#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
#define AD1836_ADC_AUX (0x6 << 6)
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
new file mode 100644
index 0000000..2758d5f
--- /dev/null
+++ b/sound/soc/codecs/adau1701.c
@@ -0,0 +1,549 @@
+/*
+ * Driver for ADAU1701 SigmaDSP processor
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ * based on an inital version by Cliff Cai <cliff.cai@analog.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/sigma.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "adau1701.h"
+
+#define ADAU1701_DSPCTRL 0x1c
+#define ADAU1701_SEROCTL 0x1e
+#define ADAU1701_SERICTL 0x1f
+
+#define ADAU1701_AUXNPOW 0x22
+
+#define ADAU1701_OSCIPOW 0x26
+#define ADAU1701_DACSET 0x27
+
+#define ADAU1701_NUM_REGS 0x28
+
+#define ADAU1701_DSPCTRL_CR (1 << 2)
+#define ADAU1701_DSPCTRL_DAM (1 << 3)
+#define ADAU1701_DSPCTRL_ADM (1 << 4)
+#define ADAU1701_DSPCTRL_SR_48 0x00
+#define ADAU1701_DSPCTRL_SR_96 0x01
+#define ADAU1701_DSPCTRL_SR_192 0x02
+#define ADAU1701_DSPCTRL_SR_MASK 0x03
+
+#define ADAU1701_SEROCTL_INV_LRCLK 0x2000
+#define ADAU1701_SEROCTL_INV_BCLK 0x1000
+#define ADAU1701_SEROCTL_MASTER 0x0800
+
+#define ADAU1701_SEROCTL_OBF16 0x0000
+#define ADAU1701_SEROCTL_OBF8 0x0200
+#define ADAU1701_SEROCTL_OBF4 0x0400
+#define ADAU1701_SEROCTL_OBF2 0x0600
+#define ADAU1701_SEROCTL_OBF_MASK 0x0600
+
+#define ADAU1701_SEROCTL_OLF1024 0x0000
+#define ADAU1701_SEROCTL_OLF512 0x0080
+#define ADAU1701_SEROCTL_OLF256 0x0100
+#define ADAU1701_SEROCTL_OLF_MASK 0x0180
+
+#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000
+#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004
+#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008
+#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c
+#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010
+#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c
+
+#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
+#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001
+#define ADAU1701_SEROCTL_WORD_LEN_16 0x0010
+#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003
+
+#define ADAU1701_AUXNPOW_VBPD 0x40
+#define ADAU1701_AUXNPOW_VRPD 0x20
+
+#define ADAU1701_SERICTL_I2S 0
+#define ADAU1701_SERICTL_LEFTJ 1
+#define ADAU1701_SERICTL_TDM 2
+#define ADAU1701_SERICTL_RIGHTJ_24 3
+#define ADAU1701_SERICTL_RIGHTJ_20 4
+#define ADAU1701_SERICTL_RIGHTJ_18 5
+#define ADAU1701_SERICTL_RIGHTJ_16 6
+#define ADAU1701_SERICTL_MODE_MASK 7
+#define ADAU1701_SERICTL_INV_BCLK BIT(3)
+#define ADAU1701_SERICTL_INV_LRCLK BIT(4)
+
+#define ADAU1701_OSCIPOW_OPD 0x04
+#define ADAU1701_DACSET_DACINIT 1
+
+#define ADAU1701_FIRMWARE "adau1701.bin"
+
+struct adau1701 {
+ unsigned int dai_fmt;
+};
+
+static const struct snd_kcontrol_new adau1701_controls[] = {
+ SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1),
+ SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+};
+
+static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
+ { "OUT0", NULL, "DAC0" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+
+ { "ADC", NULL, "IN0" },
+ { "ADC", NULL, "IN1" },
+};
+
+static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_DSPCTRL:
+ case ADAU1701_SEROCTL:
+ case ADAU1701_AUXNPOW:
+ case ADAU1701_OSCIPOW:
+ case ADAU1701_DACSET:
+ return 2;
+ case ADAU1701_SERICTL:
+ return 1;
+ }
+
+ dev_err(codec->dev, "Unsupported register address: %d\n", reg);
+ return 0;
+}
+
+static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ unsigned int i;
+ unsigned int size;
+ uint8_t buf[4];
+ int ret;
+
+ size = adau1701_register_size(codec, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ snd_soc_cache_write(codec, reg, value);
+
+ buf[0] = 0x08;
+ buf[1] = reg;
+
+ for (i = size + 1; i >= 2; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2);
+ if (ret == size + 2)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ unsigned int value;
+ unsigned int ret;
+
+ ret = snd_soc_cache_read(codec, reg, &value);
+ if (ret)
+ return ret;
+
+ return value;
+}
+
+static int adau1701_load_firmware(struct snd_soc_codec *codec)
+{
+ return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE);
+}
+
+static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
+ snd_pcm_format_t format)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
+ unsigned int val;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = ADAU1701_SEROCTL_WORD_LEN_16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val = ADAU1701_SEROCTL_WORD_LEN_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = ADAU1701_SEROCTL_WORD_LEN_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) {
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= ADAU1701_SEROCTL_MSB_DEALY16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= ADAU1701_SEROCTL_MSB_DEALY12;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= ADAU1701_SEROCTL_MSB_DEALY8;
+ break;
+ }
+ mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
+ }
+
+ snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val);
+
+ return 0;
+}
+
+static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
+ snd_pcm_format_t format)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = ADAU1701_SERICTL_RIGHTJ_16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val = ADAU1701_SERICTL_RIGHTJ_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = ADAU1701_SERICTL_RIGHTJ_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, ADAU1701_SERICTL,
+ ADAU1701_SERICTL_MODE_MASK, val);
+
+ return 0;
+}
+
+static int adau1701_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;
+ snd_pcm_format_t format;
+ unsigned int val;
+
+ switch (params_rate(params)) {
+ case 192000:
+ val = ADAU1701_DSPCTRL_SR_192;
+ break;
+ case 96000:
+ val = ADAU1701_DSPCTRL_SR_96;
+ break;
+ case 48000:
+ val = ADAU1701_DSPCTRL_SR_48;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, ADAU1701_DSPCTRL,
+ ADAU1701_DSPCTRL_SR_MASK, val);
+
+ format = params_format(params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return adau1701_set_playback_pcm_format(codec, format);
+ else
+ return adau1701_set_capture_pcm_format(codec, format);
+}
+
+static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int serictl = 0x00, seroctl = 0x00;
+ bool invert_lrclk;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* master, 64-bits per sample, 1 frame per sample */
+ seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16
+ | ADAU1701_SEROCTL_OLF1024;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_lrclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert_lrclk = false;
+ serictl |= ADAU1701_SERICTL_INV_BCLK;
+ seroctl |= ADAU1701_SEROCTL_INV_BCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert_lrclk = true;
+ serictl |= ADAU1701_SERICTL_INV_BCLK;
+ seroctl |= ADAU1701_SEROCTL_INV_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ serictl |= ADAU1701_SERICTL_LEFTJ;
+ seroctl |= ADAU1701_SEROCTL_MSB_DEALY0;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ serictl |= ADAU1701_SERICTL_RIGHTJ_24;
+ seroctl |= ADAU1701_SEROCTL_MSB_DEALY8;
+ invert_lrclk = !invert_lrclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_lrclk) {
+ seroctl |= ADAU1701_SEROCTL_INV_LRCLK;
+ serictl |= ADAU1701_SERICTL_INV_LRCLK;
+ }
+
+ adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ snd_soc_write(codec, ADAU1701_SERICTL, serictl);
+ snd_soc_update_bits(codec, ADAU1701_SEROCTL,
+ ~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
+
+ return 0;
+}
+
+static int adau1701_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* Enable VREF and VREF buffer */
+ snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Disable VREF and VREF buffer */
+ snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int mask = ADAU1701_DSPCTRL_DAM;
+ unsigned int val;
+
+ if (mute)
+ val = 0;
+ else
+ val = mask;
+
+ snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val);
+
+ return 0;
+}
+
+static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ unsigned int freq, int dir)
+{
+ unsigned int val;
+
+ switch (clk_id) {
+ case ADAU1701_CLK_SRC_OSC:
+ val = 0x0;
+ break;
+ case ADAU1701_CLK_SRC_MCLK:
+ val = ADAU1701_OSCIPOW_OPD;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
+
+ return 0;
+}
+
+#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+
+#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops adau1701_dai_ops = {
+ .set_fmt = adau1701_set_dai_fmt,
+ .hw_params = adau1701_hw_params,
+ .digital_mute = adau1701_digital_mute,
+};
+
+static struct snd_soc_dai_driver adau1701_dai = {
+ .name = "adau1701",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = ADAU1701_RATES,
+ .formats = ADAU1701_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = ADAU1701_RATES,
+ .formats = ADAU1701_FORMATS,
+ },
+ .ops = &adau1701_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int adau1701_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ codec->dapm.idle_bias_off = 1;
+
+ ret = adau1701_load_firmware(codec);
+ if (ret)
+ dev_warn(codec->dev, "Failed to load firmware\n");
+
+ snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
+ snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver adau1701_codec_drv = {
+ .probe = adau1701_probe,
+ .set_bias_level = adau1701_set_bias_level,
+
+ .reg_cache_size = ADAU1701_NUM_REGS,
+ .reg_word_size = sizeof(u16),
+
+ .controls = adau1701_controls,
+ .num_controls = ARRAY_SIZE(adau1701_controls),
+ .dapm_widgets = adau1701_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets),
+ .dapm_routes = adau1701_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
+
+ .write = adau1701_write,
+ .read = adau1701_read,
+
+ .set_sysclk = adau1701_set_sysclk,
+};
+
+static __devinit int adau1701_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adau1701 *adau1701;
+ int ret;
+
+ adau1701 = kzalloc(sizeof(*adau1701), GFP_KERNEL);
+ if (!adau1701)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, adau1701);
+ ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
+ &adau1701_dai, 1);
+ if (ret < 0)
+ kfree(adau1701);
+
+ return ret;
+}
+
+static __devexit int adau1701_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 adau1701_i2c_id[] = {
+ { "adau1701", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
+
+static struct i2c_driver adau1701_i2c_driver = {
+ .driver = {
+ .name = "adau1701",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1701_i2c_probe,
+ .remove = __devexit_p(adau1701_i2c_remove),
+ .id_table = adau1701_i2c_id,
+};
+
+static int __init adau1701_init(void)
+{
+ return i2c_add_driver(&adau1701_i2c_driver);
+}
+module_init(adau1701_init);
+
+static void __exit adau1701_exit(void)
+{
+ i2c_del_driver(&adau1701_i2c_driver);
+}
+module_exit(adau1701_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver");
+MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h
new file mode 100644
index 0000000..8d0949a
--- /dev/null
+++ b/sound/soc/codecs/adau1701.h
@@ -0,0 +1,17 @@
+/*
+ * header file for ADAU1701 SigmaDSP processor
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADAU1701_H
+#define _ADAU1701_H
+
+enum adau1701_clk_src {
+ ADAU1701_CLK_SRC_OSC,
+ ADAU1701_CLK_SRC_MCLK,
+};
+
+#endif
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
new file mode 100644
index 0000000..300c04b
--- /dev/null
+++ b/sound/soc/codecs/adav80x.c
@@ -0,0 +1,951 @@
+/*
+ * ADAV80X Audio Codec driver supporting ADAV801, ADAV803
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Yi Li <yi.li@analog.com>
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#include "adav80x.h"
+
+#define ADAV80X_PLAYBACK_CTRL 0x04
+#define ADAV80X_AUX_IN_CTRL 0x05
+#define ADAV80X_REC_CTRL 0x06
+#define ADAV80X_AUX_OUT_CTRL 0x07
+#define ADAV80X_DPATH_CTRL1 0x62
+#define ADAV80X_DPATH_CTRL2 0x63
+#define ADAV80X_DAC_CTRL1 0x64
+#define ADAV80X_DAC_CTRL2 0x65
+#define ADAV80X_DAC_CTRL3 0x66
+#define ADAV80X_DAC_L_VOL 0x68
+#define ADAV80X_DAC_R_VOL 0x69
+#define ADAV80X_PGA_L_VOL 0x6c
+#define ADAV80X_PGA_R_VOL 0x6d
+#define ADAV80X_ADC_CTRL1 0x6e
+#define ADAV80X_ADC_CTRL2 0x6f
+#define ADAV80X_ADC_L_VOL 0x70
+#define ADAV80X_ADC_R_VOL 0x71
+#define ADAV80X_PLL_CTRL1 0x74
+#define ADAV80X_PLL_CTRL2 0x75
+#define ADAV80X_ICLK_CTRL1 0x76
+#define ADAV80X_ICLK_CTRL2 0x77
+#define ADAV80X_PLL_CLK_SRC 0x78
+#define ADAV80X_PLL_OUTE 0x7a
+
+#define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00
+#define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll))
+#define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll))
+
+#define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5)
+#define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2)
+#define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src)
+#define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3)
+
+#define ADAV80X_PLL_CTRL1_PLLDIV 0x10
+#define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll))
+#define ADAV80X_PLL_CTRL1_XTLPD 0x02
+
+#define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4))
+
+#define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00)
+#define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08)
+#define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c)
+
+#define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02)
+#define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01)
+#define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f)
+
+#define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80
+#define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00
+#define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80
+
+#define ADAV80X_DAC_CTRL1_PD 0x80
+
+#define ADAV80X_DAC_CTRL2_DIV1 0x00
+#define ADAV80X_DAC_CTRL2_DIV1_5 0x10
+#define ADAV80X_DAC_CTRL2_DIV2 0x20
+#define ADAV80X_DAC_CTRL2_DIV3 0x30
+#define ADAV80X_DAC_CTRL2_DIV_MASK 0x30
+
+#define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00
+#define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40
+#define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80
+#define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0
+
+#define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00
+#define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01
+#define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02
+#define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03
+#define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01
+
+#define ADAV80X_CAPTURE_MODE_MASTER 0x20
+#define ADAV80X_CAPTURE_WORD_LEN24 0x00
+#define ADAV80X_CAPTURE_WORD_LEN20 0x04
+#define ADAV80X_CAPTRUE_WORD_LEN18 0x08
+#define ADAV80X_CAPTURE_WORD_LEN16 0x0c
+#define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c
+
+#define ADAV80X_CAPTURE_MODE_LEFT_J 0x00
+#define ADAV80X_CAPTURE_MODE_I2S 0x01
+#define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03
+#define ADAV80X_CAPTURE_MODE_MASK 0x03
+
+#define ADAV80X_PLAYBACK_MODE_MASTER 0x10
+#define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00
+#define ADAV80X_PLAYBACK_MODE_I2S 0x01
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07
+#define ADAV80X_PLAYBACK_MODE_MASK 0x07
+
+#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
+
+static u8 adav80x_default_regs[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x80, 0x26, 0x00, 0x00,
+ 0x02, 0x40, 0x20, 0x00, 0x09, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x92, 0xb1, 0x37,
+ 0x48, 0xd2, 0xfb, 0xca, 0xd2, 0x15, 0xe8, 0x29, 0xb9, 0x6a, 0xda, 0x2b,
+ 0xb7, 0xc0, 0x11, 0x65, 0x5c, 0xf6, 0xff, 0x8d, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00,
+ 0x00, 0xe8, 0x46, 0xe1, 0x5b, 0xd3, 0x43, 0x77, 0x93, 0xa7, 0x44, 0xee,
+ 0x32, 0x12, 0xc0, 0x11, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x52, 0x00,
+};
+
+struct adav80x {
+ enum snd_soc_control_type control_type;
+
+ enum adav80x_clk_src clk_src;
+ unsigned int sysclk;
+ enum adav80x_pll_src pll_src;
+
+ unsigned int dai_fmt[2];
+ unsigned int rate;
+ bool deemph;
+ bool sysclk_pd[3];
+};
+
+static const char *adav80x_mux_text[] = {
+ "ADC",
+ "Playback",
+ "Aux Playback",
+};
+
+static const unsigned int adav80x_mux_values[] = {
+ 0, 2, 3,
+};
+
+#define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \
+ SOC_VALUE_ENUM_DOUBLE_DECL(name, reg, shift, 7, \
+ ARRAY_SIZE(adav80x_mux_text), adav80x_mux_text, \
+ adav80x_mux_values)
+
+static ADAV80X_MUX_ENUM_DECL(adav80x_aux_capture_enum, ADAV80X_DPATH_CTRL1, 0);
+static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3);
+static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3);
+
+static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl =
+ SOC_DAPM_VALUE_ENUM("Route", adav80x_aux_capture_enum);
+static const struct snd_kcontrol_new adav80x_capture_mux_ctrl =
+ SOC_DAPM_VALUE_ENUM("Route", adav80x_capture_enum);
+static const struct snd_kcontrol_new adav80x_dac_mux_ctrl =
+ SOC_DAPM_VALUE_ENUM("Route", adav80x_dac_enum);
+
+#define ADAV80X_MUX(name, ctrl) \
+ SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1),
+ SND_SOC_DAPM_ADC("ADC", NULL, ADAV80X_ADC_CTRL1, 5, 1),
+
+ SND_SOC_DAPM_PGA("Right PGA", ADAV80X_ADC_CTRL1, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left PGA", ADAV80X_ADC_CTRL1, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFAUXOUT", "Aux Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFAUXIN", "Aux Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ ADAV80X_MUX("Aux Capture Select", &adav80x_aux_capture_mux_ctrl),
+ ADAV80X_MUX("Capture Select", &adav80x_capture_mux_ctrl),
+ ADAV80X_MUX("DAC Select", &adav80x_dac_mux_ctrl),
+
+ SND_SOC_DAPM_INPUT("VINR"),
+ SND_SOC_DAPM_INPUT("VINL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+
+ SND_SOC_DAPM_SUPPLY("SYSCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", ADAV80X_PLL_CTRL1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2", ADAV80X_PLL_CTRL1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("OSC", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0),
+};
+
+static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = source->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ const char *clk;
+
+ switch (adav80x->clk_src) {
+ case ADAV80X_CLK_PLL1:
+ clk = "PLL1";
+ break;
+ case ADAV80X_CLK_PLL2:
+ clk = "PLL2";
+ break;
+ case ADAV80X_CLK_XTAL:
+ clk = "OSC";
+ break;
+ default:
+ return 0;
+ }
+
+ return strcmp(source->name, clk) == 0;
+}
+
+static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = source->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
+}
+
+
+static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
+ { "DAC Select", "ADC", "ADC" },
+ { "DAC Select", "Playback", "AIFIN" },
+ { "DAC Select", "Aux Playback", "AIFAUXIN" },
+ { "DAC", NULL, "DAC Select" },
+
+ { "Capture Select", "ADC", "ADC" },
+ { "Capture Select", "Playback", "AIFIN" },
+ { "Capture Select", "Aux Playback", "AIFAUXIN" },
+ { "AIFOUT", NULL, "Capture Select" },
+
+ { "Aux Capture Select", "ADC", "ADC" },
+ { "Aux Capture Select", "Playback", "AIFIN" },
+ { "Aux Capture Select", "Aux Playback", "AIFAUXIN" },
+ { "AIFAUXOUT", NULL, "Aux Capture Select" },
+
+ { "VOUTR", NULL, "DAC" },
+ { "VOUTL", NULL, "DAC" },
+
+ { "Left PGA", NULL, "VINL" },
+ { "Right PGA", NULL, "VINR" },
+ { "ADC", NULL, "Left PGA" },
+ { "ADC", NULL, "Right PGA" },
+
+ { "SYSCLK", NULL, "PLL1", adav80x_dapm_sysclk_check },
+ { "SYSCLK", NULL, "PLL2", adav80x_dapm_sysclk_check },
+ { "SYSCLK", NULL, "OSC", adav80x_dapm_sysclk_check },
+ { "PLL1", NULL, "OSC", adav80x_dapm_pll_check },
+ { "PLL2", NULL, "OSC", adav80x_dapm_pll_check },
+
+ { "ADC", NULL, "SYSCLK" },
+ { "DAC", NULL, "SYSCLK" },
+ { "AIFOUT", NULL, "SYSCLK" },
+ { "AIFAUXOUT", NULL, "SYSCLK" },
+ { "AIFIN", NULL, "SYSCLK" },
+ { "AIFAUXIN", NULL, "SYSCLK" },
+};
+
+static int adav80x_set_deemph(struct snd_soc_codec *codec)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (adav80x->deemph) {
+ switch (adav80x->rate) {
+ case 32000:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_32;
+ break;
+ case 44100:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_44;
+ break;
+ case 48000:
+ case 64000:
+ case 88200:
+ case 96000:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_48;
+ break;
+ default:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
+ break;
+ }
+ } else {
+ val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
+ }
+
+ return snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2,
+ ADAV80X_DAC_CTRL2_DEEMPH_MASK, val);
+}
+
+static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int deemph = ucontrol->value.enumerated.item[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ adav80x->deemph = deemph;
+
+ return adav80x_set_deemph(codec);
+}
+
+static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = adav80x->deemph;
+ return 0;
+};
+
+static const DECLARE_TLV_DB_SCALE(adav80x_inpga_tlv, 0, 50, 0);
+static const DECLARE_TLV_DB_MINMAX(adav80x_digital_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new adav80x_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume", ADAV80X_DAC_L_VOL,
+ ADAV80X_DAC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
+ SOC_DOUBLE_R_TLV("Master Capture Volume", ADAV80X_ADC_L_VOL,
+ ADAV80X_ADC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAV80X_PGA_L_VOL,
+ ADAV80X_PGA_R_VOL, 0, 0x30, 0, adav80x_inpga_tlv),
+
+ SOC_DOUBLE("Master Playback Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 0),
+ SOC_DOUBLE("Master Capture Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1),
+
+ SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0),
+
+ SOC_SINGLE_BOOL_EXT("Playback De-emphasis Switch", 0,
+ adav80x_get_deemph, adav80x_put_deemph),
+};
+
+static unsigned int adav80x_port_ctrl_regs[2][2] = {
+ { ADAV80X_REC_CTRL, ADAV80X_PLAYBACK_CTRL, },
+ { ADAV80X_AUX_OUT_CTRL, ADAV80X_AUX_IN_CTRL },
+};
+
+static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int capture = 0x00;
+ unsigned int playback = 0x00;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ capture |= ADAV80X_CAPTURE_MODE_MASTER;
+ playback |= ADAV80X_PLAYBACK_MODE_MASTER;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ capture |= ADAV80X_CAPTURE_MODE_I2S;
+ playback |= ADAV80X_PLAYBACK_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ capture |= ADAV80X_CAPTURE_MODE_LEFT_J;
+ playback |= ADAV80X_PLAYBACK_MODE_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ capture |= ADAV80X_CAPTURE_MODE_RIGHT_J;
+ playback |= ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0],
+ ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER,
+ capture);
+ snd_soc_write(codec, adav80x_port_ctrl_regs[dai->id][1], playback);
+
+ adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ return 0;
+}
+
+static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
+ unsigned int sample_rate)
+{
+ unsigned int val;
+
+ if (sample_rate <= 48000)
+ val = ADAV80X_ADC_CTRL1_MODULATOR_128FS;
+ else
+ val = ADAV80X_ADC_CTRL1_MODULATOR_64FS;
+
+ snd_soc_update_bits(codec, ADAV80X_ADC_CTRL1,
+ ADAV80X_ADC_CTRL1_MODULATOR_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
+ unsigned int sample_rate)
+{
+ unsigned int val;
+
+ if (sample_rate <= 48000)
+ val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS;
+ else
+ val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS;
+
+ snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2,
+ ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK,
+ val);
+
+ return 0;
+}
+
+static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
+ struct snd_soc_dai *dai, snd_pcm_format_t format)
+{
+ unsigned int val;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = ADAV80X_CAPTURE_WORD_LEN16;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ val = ADAV80X_CAPTRUE_WORD_LEN18;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val = ADAV80X_CAPTURE_WORD_LEN20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = ADAV80X_CAPTURE_WORD_LEN24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0],
+ ADAV80X_CAPTURE_WORD_LEN_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
+ struct snd_soc_dai *dai, snd_pcm_format_t format)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][1],
+ ADAV80X_PLAYBACK_MODE_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_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 adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int rate = params_rate(params);
+
+ if (rate * 256 != adav80x->sysclk)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ adav80x_set_playback_pcm_format(codec, dai,
+ params_format(params));
+ adav80x_set_dac_clock(codec, rate);
+ } else {
+ adav80x_set_capture_pcm_format(codec, dai,
+ params_format(params));
+ adav80x_set_adc_clock(codec, rate);
+ }
+ adav80x->rate = rate;
+ adav80x_set_deemph(codec);
+
+ return 0;
+}
+
+static int adav80x_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ if (dir == SND_SOC_CLOCK_IN) {
+ switch (clk_id) {
+ case ADAV80X_CLK_XIN:
+ case ADAV80X_CLK_XTAL:
+ case ADAV80X_CLK_MCLKI:
+ case ADAV80X_CLK_PLL1:
+ case ADAV80X_CLK_PLL2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adav80x->sysclk = freq;
+
+ if (adav80x->clk_src != clk_id) {
+ unsigned int iclk_ctrl1, iclk_ctrl2;
+
+ adav80x->clk_src = clk_id;
+ if (clk_id == ADAV80X_CLK_XTAL)
+ clk_id = ADAV80X_CLK_XIN;
+
+ iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC(clk_id) |
+ ADAV80X_ICLK_CTRL1_ADC_SRC(clk_id) |
+ ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id);
+ iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id);
+
+ snd_soc_write(codec, ADAV80X_ICLK_CTRL1, iclk_ctrl1);
+ snd_soc_write(codec, ADAV80X_ICLK_CTRL2, iclk_ctrl2);
+
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+ } else {
+ unsigned int mask;
+
+ switch (clk_id) {
+ case ADAV80X_CLK_SYSCLK1:
+ case ADAV80X_CLK_SYSCLK2:
+ case ADAV80X_CLK_SYSCLK3:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ clk_id -= ADAV80X_CLK_SYSCLK1;
+ mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id);
+
+ if (freq == 0) {
+ snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, mask);
+ adav80x->sysclk_pd[clk_id] = true;
+ } else {
+ snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, 0);
+ adav80x->sysclk_pd[clk_id] = false;
+ }
+
+ if (adav80x->sysclk_pd[0])
+ snd_soc_dapm_disable_pin(&codec->dapm, "PLL1");
+ else
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
+
+ if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2])
+ snd_soc_dapm_disable_pin(&codec->dapm, "PLL2");
+ else
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
+
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ return 0;
+}
+
+static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int pll_ctrl1 = 0;
+ unsigned int pll_ctrl2 = 0;
+ unsigned int pll_src;
+
+ switch (source) {
+ case ADAV80X_PLL_SRC_XTAL:
+ case ADAV80X_PLL_SRC_XIN:
+ case ADAV80X_PLL_SRC_MCLKI:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!freq_out)
+ return 0;
+
+ switch (freq_in) {
+ case 27000000:
+ break;
+ case 54000000:
+ if (source == ADAV80X_PLL_SRC_XIN) {
+ pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (freq_out > 12288000) {
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_DOUB(pll_id);
+ freq_out /= 2;
+ }
+
+ /* freq_out = sample_rate * 256 */
+ switch (freq_out) {
+ case 8192000:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_32(pll_id);
+ break;
+ case 11289600:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_44(pll_id);
+ break;
+ case 12288000:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_48(pll_id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, ADAV80X_PLL_CTRL1, ADAV80X_PLL_CTRL1_PLLDIV,
+ pll_ctrl1);
+ snd_soc_update_bits(codec, ADAV80X_PLL_CTRL2,
+ ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2);
+
+ if (source != adav80x->pll_src) {
+ if (source == ADAV80X_PLL_SRC_MCLKI)
+ pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll_id);
+ else
+ pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id);
+
+ snd_soc_update_bits(codec, ADAV80X_PLL_CLK_SRC,
+ ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src);
+
+ adav80x->pll_src = source;
+
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ return 0;
+}
+
+static int adav80x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ unsigned int mask = ADAV80X_DAC_CTRL1_PD;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, 0x00);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, mask);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+/* Enforce the same sample rate on all audio interfaces */
+static int adav80x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ if (!codec->active || !adav80x->rate)
+ return 0;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
+}
+
+static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ if (!codec->active)
+ adav80x->rate = 0;
+}
+
+static const struct snd_soc_dai_ops adav80x_dai_ops = {
+ .set_fmt = adav80x_set_dai_fmt,
+ .hw_params = adav80x_hw_params,
+ .startup = adav80x_dai_startup,
+ .shutdown = adav80x_dai_shutdown,
+};
+
+#define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver adav80x_dais[] = {
+ {
+ .name = "adav80x-hifi",
+ .id = 0,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_PLAYBACK_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_CAPTURE_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .ops = &adav80x_dai_ops,
+ },
+ {
+ .name = "adav80x-aux",
+ .id = 1,
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_PLAYBACK_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Aux Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_CAPTURE_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .ops = &adav80x_dai_ops,
+ },
+};
+
+static int adav80x_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, adav80x->control_type);
+ if (ret) {
+ dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* Force PLLs on for SYSCLK output */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
+
+ /* Power down S/PDIF receiver, since it is currently not supported */
+ snd_soc_write(codec, ADAV80X_PLL_OUTE, 0x20);
+ /* Disable DAC zero flag */
+ snd_soc_write(codec, ADAV80X_DAC_CTRL3, 0x6);
+
+ return adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+}
+
+static int adav80x_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int adav80x_resume(struct snd_soc_codec *codec)
+{
+ adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ codec->cache_sync = 1;
+ snd_soc_cache_sync(codec);
+
+ return 0;
+}
+
+static int adav80x_remove(struct snd_soc_codec *codec)
+{
+ return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static struct snd_soc_codec_driver adav80x_codec_driver = {
+ .probe = adav80x_probe,
+ .remove = adav80x_remove,
+ .suspend = adav80x_suspend,
+ .resume = adav80x_resume,
+ .set_bias_level = adav80x_set_bias_level,
+
+ .set_pll = adav80x_set_pll,
+ .set_sysclk = adav80x_set_sysclk,
+
+ .reg_word_size = sizeof(u8),
+ .reg_cache_size = ARRAY_SIZE(adav80x_default_regs),
+ .reg_cache_default = adav80x_default_regs,
+
+ .controls = adav80x_controls,
+ .num_controls = ARRAY_SIZE(adav80x_controls),
+ .dapm_widgets = adav80x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets),
+ .dapm_routes = adav80x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
+};
+
+static int __devinit adav80x_bus_probe(struct device *dev,
+ enum snd_soc_control_type control_type)
+{
+ struct adav80x *adav80x;
+ int ret;
+
+ adav80x = kzalloc(sizeof(*adav80x), GFP_KERNEL);
+ if (!adav80x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, adav80x);
+ adav80x->control_type = control_type;
+
+ ret = snd_soc_register_codec(dev, &adav80x_codec_driver,
+ adav80x_dais, ARRAY_SIZE(adav80x_dais));
+ if (ret)
+ kfree(adav80x);
+
+ return ret;
+}
+
+static int __devexit adav80x_bus_remove(struct device *dev)
+{
+ snd_soc_unregister_codec(dev);
+ kfree(dev_get_drvdata(dev));
+ return 0;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit adav80x_spi_probe(struct spi_device *spi)
+{
+ return adav80x_bus_probe(&spi->dev, SND_SOC_SPI);
+}
+
+static int __devexit adav80x_spi_remove(struct spi_device *spi)
+{
+ return adav80x_bus_remove(&spi->dev);
+}
+
+static struct spi_driver adav80x_spi_driver = {
+ .driver = {
+ .name = "adav801",
+ .owner = THIS_MODULE,
+ },
+ .probe = adav80x_spi_probe,
+ .remove = __devexit_p(adav80x_spi_remove),
+};
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static const struct i2c_device_id adav80x_id[] = {
+ { "adav803", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adav80x_id);
+
+static int __devinit adav80x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return adav80x_bus_probe(&client->dev, SND_SOC_I2C);
+}
+
+static int __devexit adav80x_i2c_remove(struct i2c_client *client)
+{
+ return adav80x_bus_remove(&client->dev);
+}
+
+static struct i2c_driver adav80x_i2c_driver = {
+ .driver = {
+ .name = "adav803",
+ .owner = THIS_MODULE,
+ },
+ .probe = adav80x_i2c_probe,
+ .remove = __devexit_p(adav80x_i2c_remove),
+ .id_table = adav80x_id,
+};
+#endif
+
+static int __init adav80x_init(void)
+{
+ int ret = 0;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&adav80x_i2c_driver);
+ if (ret)
+ return ret;
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&adav80x_spi_driver);
+#endif
+
+ return ret;
+}
+module_init(adav80x_init);
+
+static void __exit adav80x_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&adav80x_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&adav80x_spi_driver);
+#endif
+}
+module_exit(adav80x_exit);
+
+MODULE_DESCRIPTION("ASoC ADAV80x driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h
new file mode 100644
index 0000000..adb0fc7
--- /dev/null
+++ b/sound/soc/codecs/adav80x.h
@@ -0,0 +1,35 @@
+/*
+ * header file for ADAV80X parts
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADAV80X_H
+#define _ADAV80X_H
+
+enum adav80x_pll_src {
+ ADAV80X_PLL_SRC_XIN,
+ ADAV80X_PLL_SRC_XTAL,
+ ADAV80X_PLL_SRC_MCLKI,
+};
+
+enum adav80x_pll {
+ ADAV80X_PLL1 = 0,
+ ADAV80X_PLL2 = 1,
+};
+
+enum adav80x_clk_src {
+ ADAV80X_CLK_XIN = 0,
+ ADAV80X_CLK_MCLKI = 1,
+ ADAV80X_CLK_PLL1 = 2,
+ ADAV80X_CLK_PLL2 = 3,
+ ADAV80X_CLK_XTAL = 6,
+
+ ADAV80X_CLK_SYSCLK1 = 6,
+ ADAV80X_CLK_SYSCLK2 = 7,
+ ADAV80X_CLK_SYSCLK3 = 8,
+};
+
+#endif
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index ed96f247c..7a64e58 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -457,7 +457,7 @@ static struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
.set_sysclk = ak4641_set_dai_sysclk,
};
-struct snd_soc_dai_driver ak4641_dai[] = {
+static struct snd_soc_dai_driver ak4641_dai[] = {
{
.name = "ak4641-hifi",
.id = 1,
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 4be0570..65f4604 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -357,7 +357,7 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PW_MGMT2, MS, data);
+ snd_soc_update_bits(codec, PW_MGMT2, MS | MCKO | PMPLL, data);
snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
/* format type */
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 0206a17..6cc8678 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -636,10 +636,7 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
#endif /* CONFIG_PM */
/*
- * ASoC codec device structure
- *
- * Assign this variable to the codec_dev field of the machine driver's
- * snd_soc_device structure.
+ * ASoC codec driver structure
*/
static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
.probe = cs4270_probe,
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 4173b67..ac65a2d 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1397,8 +1397,6 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
if (freq == max98088->sysclk)
return 0;
- max98088->sysclk = freq; /* remember current sysclk */
-
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 30MHz)..
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index e1d282d..668434d 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1517,8 +1517,6 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
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)..
@@ -2261,11 +2259,11 @@ static int max98095_probe(struct snd_soc_codec *codec)
ret = snd_soc_read(codec, M98095_0FF_REV_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
+ dev_err(codec->dev, "Failure reading hardware revision: %d\n",
ret);
goto err_access;
}
- dev_info(codec->dev, "revision %c\n", ret + 'A');
+ dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
@@ -2342,8 +2340,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
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);
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98095,
+ max98095_dai, ARRAY_SIZE(max98095_dai));
if (ret < 0)
kfree(max98095);
return ret;
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
new file mode 100644
index 0000000..409d89d
--- /dev/null
+++ b/sound/soc/codecs/sta32x.c
@@ -0,0 +1,917 @@
+/*
+ * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Johannes Stezenbach <js@sig21.net>
+ *
+ * based on code from:
+ * Wolfson Microelectronics PLC.
+ * Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Freescale Semiconductor, Inc.
+ * Timur Tabi <timur@freescale.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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
+
+#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/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "sta32x.h"
+
+#define STA32X_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+#define STA32X_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+
+/* Power-up register defaults */
+static const u8 sta32x_regs[STA32X_REGISTER_COUNT] = {
+ 0x63, 0x80, 0xc2, 0x40, 0xc2, 0x5c, 0x10, 0xff, 0x60, 0x60,
+ 0x60, 0x80, 0x00, 0x00, 0x00, 0x40, 0x80, 0x77, 0x6a, 0x69,
+ 0x6a, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
+ 0xc0, 0xf3, 0x33, 0x00, 0x0c,
+};
+
+/* regulator power supply names */
+static const char *sta32x_supply_names[] = {
+ "Vdda", /* analog supply, 3.3VV */
+ "Vdd3", /* digital supply, 3.3V */
+ "Vcc" /* power amp spply, 10V - 36V */
+};
+
+/* codec private data */
+struct sta32x_priv {
+ struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)];
+ struct snd_soc_codec *codec;
+
+ unsigned int mclk;
+ unsigned int format;
+};
+
+static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tone_tlv, -120, 200, 0);
+
+static const char *sta32x_drc_ac[] = {
+ "Anti-Clipping", "Dynamic Range Compression" };
+static const char *sta32x_auto_eq_mode[] = {
+ "User", "Preset", "Loudness" };
+static const char *sta32x_auto_gc_mode[] = {
+ "User", "AC no clipping", "AC limited clipping (10%)",
+ "DRC nighttime listening mode" };
+static const char *sta32x_auto_xo_mode[] = {
+ "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", "200Hz",
+ "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", "340Hz", "360Hz" };
+static const char *sta32x_preset_eq_mode[] = {
+ "Flat", "Rock", "Soft Rock", "Jazz", "Classical", "Dance", "Pop", "Soft",
+ "Hard", "Party", "Vocal", "Hip-Hop", "Dialog", "Bass-boost #1",
+ "Bass-boost #2", "Bass-boost #3", "Loudness 1", "Loudness 2",
+ "Loudness 3", "Loudness 4", "Loudness 5", "Loudness 6", "Loudness 7",
+ "Loudness 8", "Loudness 9", "Loudness 10", "Loudness 11", "Loudness 12",
+ "Loudness 13", "Loudness 14", "Loudness 15", "Loudness 16" };
+static const char *sta32x_limiter_select[] = {
+ "Limiter Disabled", "Limiter #1", "Limiter #2" };
+static const char *sta32x_limiter_attack_rate[] = {
+ "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024",
+ "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752",
+ "0.0645", "0.0564", "0.0501", "0.0451" };
+static const char *sta32x_limiter_release_rate[] = {
+ "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299",
+ "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137",
+ "0.0134", "0.0117", "0.0110", "0.0104" };
+
+static const unsigned int sta32x_limiter_ac_attack_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0),
+};
+
+static const unsigned int sta32x_limiter_ac_release_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0),
+ 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0),
+ 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const unsigned int sta32x_limiter_drc_attack_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0),
+ 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0),
+ 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0),
+};
+
+static const unsigned int sta32x_limiter_drc_release_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0),
+ 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0),
+ 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0),
+ 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+};
+
+static const struct soc_enum sta32x_drc_ac_enum =
+ SOC_ENUM_SINGLE(STA32X_CONFD, STA32X_CONFD_DRC_SHIFT,
+ 2, sta32x_drc_ac);
+static const struct soc_enum sta32x_auto_eq_enum =
+ SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT,
+ 3, sta32x_auto_eq_mode);
+static const struct soc_enum sta32x_auto_gc_enum =
+ SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT,
+ 4, sta32x_auto_gc_mode);
+static const struct soc_enum sta32x_auto_xo_enum =
+ SOC_ENUM_SINGLE(STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT,
+ 16, sta32x_auto_xo_mode);
+static const struct soc_enum sta32x_preset_eq_enum =
+ SOC_ENUM_SINGLE(STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT,
+ 32, sta32x_preset_eq_mode);
+static const struct soc_enum sta32x_limiter_ch1_enum =
+ SOC_ENUM_SINGLE(STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT,
+ 3, sta32x_limiter_select);
+static const struct soc_enum sta32x_limiter_ch2_enum =
+ SOC_ENUM_SINGLE(STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT,
+ 3, sta32x_limiter_select);
+static const struct soc_enum sta32x_limiter_ch3_enum =
+ SOC_ENUM_SINGLE(STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT,
+ 3, sta32x_limiter_select);
+static const struct soc_enum sta32x_limiter1_attack_rate_enum =
+ SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxA_SHIFT,
+ 16, sta32x_limiter_attack_rate);
+static const struct soc_enum sta32x_limiter2_attack_rate_enum =
+ SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxA_SHIFT,
+ 16, sta32x_limiter_attack_rate);
+static const struct soc_enum sta32x_limiter1_release_rate_enum =
+ SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxR_SHIFT,
+ 16, sta32x_limiter_release_rate);
+static const struct soc_enum sta32x_limiter2_release_rate_enum =
+ SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxR_SHIFT,
+ 16, sta32x_limiter_release_rate);
+
+/* byte array controls for setting biquad, mixer, scaling coefficients;
+ * for biquads all five coefficients need to be set in one go,
+ * mixer and pre/postscale coefs can be set individually;
+ * each coef is 24bit, the bytes are ordered in the same way
+ * as given in the STA32x data sheet (big endian; b1, b2, a1, a2, b0)
+ */
+
+static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int numcoef = kcontrol->private_value >> 16;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 3 * numcoef;
+ return 0;
+}
+
+static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+ unsigned int cfud;
+ int i;
+
+ /* preserve reserved bits in STA32X_CFUD */
+ cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0;
+ /* chip documentation does not say if the bits are self clearing,
+ * so do it explicitly */
+ snd_soc_write(codec, STA32X_CFUD, cfud);
+
+ snd_soc_write(codec, STA32X_CFADDR2, index);
+ if (numcoef == 1)
+ snd_soc_write(codec, STA32X_CFUD, cfud | 0x04);
+ else if (numcoef == 5)
+ snd_soc_write(codec, STA32X_CFUD, cfud | 0x08);
+ else
+ return -EINVAL;
+ for (i = 0; i < 3 * numcoef; i++)
+ ucontrol->value.bytes.data[i] =
+ snd_soc_read(codec, STA32X_B1CF1 + i);
+
+ return 0;
+}
+
+static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+ unsigned int cfud;
+ int i;
+
+ /* preserve reserved bits in STA32X_CFUD */
+ cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0;
+ /* chip documentation does not say if the bits are self clearing,
+ * so do it explicitly */
+ snd_soc_write(codec, STA32X_CFUD, cfud);
+
+ snd_soc_write(codec, STA32X_CFADDR2, index);
+ for (i = 0; i < 3 * numcoef; i++)
+ snd_soc_write(codec, STA32X_B1CF1 + i,
+ ucontrol->value.bytes.data[i]);
+ if (numcoef == 1)
+ snd_soc_write(codec, STA32X_CFUD, cfud | 0x01);
+ else if (numcoef == 5)
+ snd_soc_write(codec, STA32X_CFUD, cfud | 0x02);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+#define SINGLE_COEF(xname, index) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sta32x_coefficient_info, \
+ .get = sta32x_coefficient_get,\
+ .put = sta32x_coefficient_put, \
+ .private_value = index | (1 << 16) }
+
+#define BIQUAD_COEFS(xname, index) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sta32x_coefficient_info, \
+ .get = sta32x_coefficient_get,\
+ .put = sta32x_coefficient_put, \
+ .private_value = index | (5 << 16) }
+
+static const struct snd_kcontrol_new sta32x_snd_controls[] = {
+SOC_SINGLE_TLV("Master Volume", STA32X_MVOL, 0, 0xff, 1, mvol_tlv),
+SOC_SINGLE("Master Switch", STA32X_MMUTE, 0, 1, 1),
+SOC_SINGLE("Ch1 Switch", STA32X_MMUTE, 1, 1, 1),
+SOC_SINGLE("Ch2 Switch", STA32X_MMUTE, 2, 1, 1),
+SOC_SINGLE("Ch3 Switch", STA32X_MMUTE, 3, 1, 1),
+SOC_SINGLE_TLV("Ch1 Volume", STA32X_C1VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch2 Volume", STA32X_C2VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch3 Volume", STA32X_C3VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE("De-emphasis Filter Switch", STA32X_CONFD, STA32X_CONFD_DEMP_SHIFT, 1, 0),
+SOC_ENUM("Compressor/Limiter Switch", sta32x_drc_ac_enum),
+SOC_SINGLE("Miami Mode Switch", STA32X_CONFD, STA32X_CONFD_MME_SHIFT, 1, 0),
+SOC_SINGLE("Zero Cross Switch", STA32X_CONFE, STA32X_CONFE_ZCE_SHIFT, 1, 0),
+SOC_SINGLE("Soft Ramp Switch", STA32X_CONFE, STA32X_CONFE_SVE_SHIFT, 1, 0),
+SOC_SINGLE("Auto-Mute Switch", STA32X_CONFF, STA32X_CONFF_IDE_SHIFT, 1, 0),
+SOC_ENUM("Automode EQ", sta32x_auto_eq_enum),
+SOC_ENUM("Automode GC", sta32x_auto_gc_enum),
+SOC_ENUM("Automode XO", sta32x_auto_xo_enum),
+SOC_ENUM("Preset EQ", sta32x_preset_eq_enum),
+SOC_SINGLE("Ch1 Tone Control Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Tone Control Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 EQ Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 EQ Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch3 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
+SOC_ENUM("Ch1 Limiter Select", sta32x_limiter_ch1_enum),
+SOC_ENUM("Ch2 Limiter Select", sta32x_limiter_ch2_enum),
+SOC_ENUM("Ch3 Limiter Select", sta32x_limiter_ch3_enum),
+SOC_SINGLE_TLV("Bass Tone Control", STA32X_TONE, STA32X_TONE_BTC_SHIFT, 15, 0, tone_tlv),
+SOC_SINGLE_TLV("Treble Tone Control", STA32X_TONE, STA32X_TONE_TTC_SHIFT, 15, 0, tone_tlv),
+SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta32x_limiter1_attack_rate_enum),
+SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta32x_limiter2_attack_rate_enum),
+SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum),
+SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum),
+
+/* depending on mode, the attack/release thresholds have
+ * two different enum definitions; provide both
+ */
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_drc_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_drc_release_tlv),
+
+BIQUAD_COEFS("Ch1 - Biquad 1", 0),
+BIQUAD_COEFS("Ch1 - Biquad 2", 5),
+BIQUAD_COEFS("Ch1 - Biquad 3", 10),
+BIQUAD_COEFS("Ch1 - Biquad 4", 15),
+BIQUAD_COEFS("Ch2 - Biquad 1", 20),
+BIQUAD_COEFS("Ch2 - Biquad 2", 25),
+BIQUAD_COEFS("Ch2 - Biquad 3", 30),
+BIQUAD_COEFS("Ch2 - Biquad 4", 35),
+BIQUAD_COEFS("High-pass", 40),
+BIQUAD_COEFS("Low-pass", 45),
+SINGLE_COEF("Ch1 - Prescale", 50),
+SINGLE_COEF("Ch2 - Prescale", 51),
+SINGLE_COEF("Ch1 - Postscale", 52),
+SINGLE_COEF("Ch2 - Postscale", 53),
+SINGLE_COEF("Ch3 - Postscale", 54),
+SINGLE_COEF("Thermal warning - Postscale", 55),
+SINGLE_COEF("Ch1 - Mix 1", 56),
+SINGLE_COEF("Ch1 - Mix 2", 57),
+SINGLE_COEF("Ch2 - Mix 1", 58),
+SINGLE_COEF("Ch2 - Mix 2", 59),
+SINGLE_COEF("Ch3 - Mix 1", 60),
+SINGLE_COEF("Ch3 - Mix 2", 61),
+};
+
+static const struct snd_soc_dapm_widget sta32x_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LEFT"),
+SND_SOC_DAPM_OUTPUT("RIGHT"),
+SND_SOC_DAPM_OUTPUT("SUB"),
+};
+
+static const struct snd_soc_dapm_route sta32x_dapm_routes[] = {
+ { "LEFT", NULL, "DAC" },
+ { "RIGHT", NULL, "DAC" },
+ { "SUB", NULL, "DAC" },
+};
+
+/* MCLK interpolation ratio per fs */
+static struct {
+ int fs;
+ int ir;
+} interpolation_ratios[] = {
+ { 32000, 0 },
+ { 44100, 0 },
+ { 48000, 0 },
+ { 88200, 1 },
+ { 96000, 1 },
+ { 176400, 2 },
+ { 192000, 2 },
+};
+
+/* MCLK to fs clock ratios */
+static struct {
+ int ratio;
+ int mcs;
+} mclk_ratios[3][7] = {
+ { { 768, 0 }, { 512, 1 }, { 384, 2 }, { 256, 3 },
+ { 128, 4 }, { 576, 5 }, { 0, 0 } },
+ { { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } },
+ { { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } },
+};
+
+
+/**
+ * sta32x_set_dai_sysclk - configure MCLK
+ * @codec_dai: the codec DAI
+ * @clk_id: the clock ID (ignored)
+ * @freq: the MCLK input frequency
+ * @dir: the clock direction (ignored)
+ *
+ * The value of MCLK is used to determine which sample rates are supported
+ * by the STA32X, based on the mclk_ratios table.
+ *
+ * This function must be called by the machine driver's 'startup' function,
+ * otherwise the list of supported sample rates will not be available in
+ * time for ALSA.
+ *
+ * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
+ * theoretically possible sample rates to be enabled. Call it again with a
+ * proper value set one the external clock is set (most probably you would do
+ * that from a machine's driver 'hw_param' hook.
+ */
+static int sta32x_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 sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ int i, j, ir, fs;
+ unsigned int rates = 0;
+ unsigned int rate_min = -1;
+ unsigned int rate_max = 0;
+
+ pr_debug("mclk=%u\n", freq);
+ sta32x->mclk = freq;
+
+ if (sta32x->mclk) {
+ for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
+ ir = interpolation_ratios[i].ir;
+ fs = interpolation_ratios[i].fs;
+ for (j = 0; mclk_ratios[ir][j].ratio; j++) {
+ if (mclk_ratios[ir][j].ratio * fs == freq) {
+ rates |= snd_pcm_rate_to_rate_bit(fs);
+ if (fs < rate_min)
+ rate_min = fs;
+ if (fs > rate_max)
+ rate_max = fs;
+ }
+ }
+ }
+ /* FIXME: soc should support a rate list */
+ rates &= ~SNDRV_PCM_RATE_KNOT;
+
+ if (!rates) {
+ dev_err(codec->dev, "could not find a valid sample rate\n");
+ return -EINVAL;
+ }
+ } else {
+ /* enable all possible rates */
+ rates = STA32X_RATES;
+ rate_min = 32000;
+ rate_max = 192000;
+ }
+
+ codec_dai->driver->playback.rates = rates;
+ codec_dai->driver->playback.rate_min = rate_min;
+ codec_dai->driver->playback.rate_max = rate_max;
+ return 0;
+}
+
+/**
+ * sta32x_set_dai_fmt - configure the codec for the selected audio format
+ * @codec_dai: the codec DAI
+ * @fmt: a SND_SOC_DAIFMT_x value indicating the data format
+ *
+ * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
+ * codec accordingly.
+ */
+static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ u8 confb = snd_soc_read(codec, STA32X_CONFB);
+
+ pr_debug("\n");
+ confb &= ~(STA32X_CONFB_C1IM | STA32X_CONFB_C2IM);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ sta32x->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ confb |= STA32X_CONFB_C2IM;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ confb |= STA32X_CONFB_C1IM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, STA32X_CONFB, confb);
+ return 0;
+}
+
+/**
+ * sta32x_hw_params - program the STA32X with the given hardware parameters.
+ * @substream: the audio stream
+ * @params: the hardware parameters to set
+ * @dai: the SOC DAI (ignored)
+ *
+ * This function programs the hardware with the values provided.
+ * Specifically, the sample rate and the data format.
+ */
+static int sta32x_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 sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ unsigned int rate;
+ int i, mcs = -1, ir = -1;
+ u8 confa, confb;
+
+ rate = params_rate(params);
+ pr_debug("rate: %u\n", rate);
+ for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++)
+ if (interpolation_ratios[i].fs == rate)
+ ir = interpolation_ratios[i].ir;
+ if (ir < 0)
+ return -EINVAL;
+ for (i = 0; mclk_ratios[ir][i].ratio; i++)
+ if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk)
+ mcs = mclk_ratios[ir][i].mcs;
+ if (mcs < 0)
+ return -EINVAL;
+
+ confa = snd_soc_read(codec, STA32X_CONFA);
+ confa &= ~(STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK);
+ confa |= (ir << STA32X_CONFA_IR_SHIFT) | (mcs << STA32X_CONFA_MCS_SHIFT);
+
+ confb = snd_soc_read(codec, STA32X_CONFB);
+ confb &= ~(STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ pr_debug("24bit\n");
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ pr_debug("24bit or 32bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0x2;
+ break;
+ }
+
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S20_3BE:
+ pr_debug("20bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x4;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x5;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0x6;
+ break;
+ }
+
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ case SNDRV_PCM_FORMAT_S18_3BE:
+ pr_debug("18bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0xa;
+ break;
+ }
+
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
+ pr_debug("16bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0xd;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0xe;
+ break;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, STA32X_CONFA, confa);
+ snd_soc_write(codec, STA32X_CONFB, confb);
+ return 0;
+}
+
+/**
+ * sta32x_set_bias_level - DAPM callback
+ * @codec: the codec device
+ * @level: DAPM power level
+ *
+ * This is called by ALSA to put the codec into low power mode
+ * or to wake it up. If the codec is powered off completely
+ * all registers must be restored after power on.
+ */
+static int sta32x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("level = %d\n", level);
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* Full power on */
+ snd_soc_update_bits(codec, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_cache_sync(codec);
+ }
+
+ /* Power up to mute */
+ /* FIXME */
+ snd_soc_update_bits(codec, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* The chip runs through the power down sequence for us. */
+ snd_soc_update_bits(codec, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+ STA32X_CONFF_PWDN);
+ msleep(300);
+
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static struct snd_soc_dai_ops sta32x_dai_ops = {
+ .hw_params = sta32x_hw_params,
+ .set_sysclk = sta32x_set_dai_sysclk,
+ .set_fmt = sta32x_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sta32x_dai = {
+ .name = "STA32X",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STA32X_RATES,
+ .formats = STA32X_FORMATS,
+ },
+ .ops = &sta32x_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int sta32x_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int sta32x_resume(struct snd_soc_codec *codec)
+{
+ sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+#else
+#define sta32x_suspend NULL
+#define sta32x_resume NULL
+#endif
+
+static int sta32x_probe(struct snd_soc_codec *codec)
+{
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ int i, ret = 0;
+
+ sta32x->codec = codec;
+
+ /* regulators */
+ for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
+ sta32x->supplies[i].supply = sta32x_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ /* Tell ASoC what kind of I/O to use to read the registers. ASoC will
+ * then do the I2C transactions itself.
+ */
+ 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 (ret=%i)\n", ret);
+ return ret;
+ }
+
+ /* read reg reset values into cache */
+ for (i = 0; i < STA32X_REGISTER_COUNT; i++)
+ snd_soc_cache_write(codec, i, sta32x_regs[i]);
+
+ /* preserve reset values of reserved register bits */
+ snd_soc_cache_write(codec, STA32X_CONFC,
+ codec->hw_read(codec, STA32X_CONFC));
+ snd_soc_cache_write(codec, STA32X_CONFE,
+ codec->hw_read(codec, STA32X_CONFE));
+ snd_soc_cache_write(codec, STA32X_CONFF,
+ codec->hw_read(codec, STA32X_CONFF));
+ snd_soc_cache_write(codec, STA32X_MMUTE,
+ codec->hw_read(codec, STA32X_MMUTE));
+ snd_soc_cache_write(codec, STA32X_AUTO1,
+ codec->hw_read(codec, STA32X_AUTO1));
+ snd_soc_cache_write(codec, STA32X_AUTO3,
+ codec->hw_read(codec, STA32X_AUTO3));
+ snd_soc_cache_write(codec, STA32X_C3CFG,
+ codec->hw_read(codec, STA32X_C3CFG));
+
+ /* FIXME enable thermal warning adjustment and recovery */
+ snd_soc_update_bits(codec, STA32X_CONFA,
+ STA32X_CONFA_TWAB | STA32X_CONFA_TWRB, 0);
+
+ /* FIXME select 2.1 mode */
+ snd_soc_update_bits(codec, STA32X_CONFF,
+ STA32X_CONFF_OCFG_MASK,
+ 1 << STA32X_CONFF_OCFG_SHIFT);
+
+ /* FIXME channel to output mapping */
+ snd_soc_update_bits(codec, STA32X_C1CFG,
+ STA32X_CxCFG_OM_MASK,
+ 0 << STA32X_CxCFG_OM_SHIFT);
+ snd_soc_update_bits(codec, STA32X_C2CFG,
+ STA32X_CxCFG_OM_MASK,
+ 1 << STA32X_CxCFG_OM_SHIFT);
+ snd_soc_update_bits(codec, STA32X_C3CFG,
+ STA32X_CxCFG_OM_MASK,
+ 2 << STA32X_CxCFG_OM_SHIFT);
+
+ sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* Bias level configuration will have done an extra enable */
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+
+ return 0;
+
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+err:
+ return ret;
+}
+
+static int sta32x_remove(struct snd_soc_codec *codec)
+{
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+ regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+
+ return 0;
+}
+
+static int sta32x_reg_is_volatile(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ case STA32X_CONFA ... STA32X_L2ATRT:
+ case STA32X_MPCC1 ... STA32X_FDRC2:
+ return 0;
+ }
+ return 1;
+}
+
+static const struct snd_soc_codec_driver sta32x_codec = {
+ .probe = sta32x_probe,
+ .remove = sta32x_remove,
+ .suspend = sta32x_suspend,
+ .resume = sta32x_resume,
+ .reg_cache_size = STA32X_REGISTER_COUNT,
+ .reg_word_size = sizeof(u8),
+ .volatile_register = sta32x_reg_is_volatile,
+ .set_bias_level = sta32x_set_bias_level,
+ .controls = sta32x_snd_controls,
+ .num_controls = ARRAY_SIZE(sta32x_snd_controls),
+ .dapm_widgets = sta32x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets),
+ .dapm_routes = sta32x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes),
+};
+
+static __devinit int sta32x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct sta32x_priv *sta32x;
+ int ret;
+
+ sta32x = kzalloc(sizeof(struct sta32x_priv), GFP_KERNEL);
+ if (!sta32x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, sta32x);
+
+ ret = snd_soc_register_codec(&i2c->dev, &sta32x_codec, &sta32x_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static __devexit int sta32x_i2c_remove(struct i2c_client *client)
+{
+ struct sta32x_priv *sta32x = i2c_get_clientdata(client);
+ struct snd_soc_codec *codec = sta32x->codec;
+
+ if (codec)
+ sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+
+ if (codec) {
+ snd_soc_unregister_codec(&client->dev);
+ snd_soc_codec_set_drvdata(codec, NULL);
+ }
+
+ kfree(sta32x);
+ return 0;
+}
+
+static const struct i2c_device_id sta32x_i2c_id[] = {
+ { "sta326", 0 },
+ { "sta328", 0 },
+ { "sta329", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
+
+static struct i2c_driver sta32x_i2c_driver = {
+ .driver = {
+ .name = "sta32x",
+ .owner = THIS_MODULE,
+ },
+ .probe = sta32x_i2c_probe,
+ .remove = __devexit_p(sta32x_i2c_remove),
+ .id_table = sta32x_i2c_id,
+};
+
+static int __init sta32x_init(void)
+{
+ return i2c_add_driver(&sta32x_i2c_driver);
+}
+module_init(sta32x_init);
+
+static void __exit sta32x_exit(void)
+{
+ i2c_del_driver(&sta32x_i2c_driver);
+}
+module_exit(sta32x_exit);
+
+MODULE_DESCRIPTION("ASoC STA32X driver");
+MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sta32x.h b/sound/soc/codecs/sta32x.h
new file mode 100644
index 0000000..b97ee5a
--- /dev/null
+++ b/sound/soc/codecs/sta32x.h
@@ -0,0 +1,210 @@
+/*
+ * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Johannes Stezenbach <js@sig21.net>
+ *
+ * based on code from:
+ * Wolfson Microelectronics PLC.
+ * 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 _ASOC_STA_32X_H
+#define _ASOC_STA_32X_H
+
+/* STA326 register addresses */
+
+#define STA32X_REGISTER_COUNT 0x2d
+
+#define STA32X_CONFA 0x00
+#define STA32X_CONFB 0x01
+#define STA32X_CONFC 0x02
+#define STA32X_CONFD 0x03
+#define STA32X_CONFE 0x04
+#define STA32X_CONFF 0x05
+#define STA32X_MMUTE 0x06
+#define STA32X_MVOL 0x07
+#define STA32X_C1VOL 0x08
+#define STA32X_C2VOL 0x09
+#define STA32X_C3VOL 0x0a
+#define STA32X_AUTO1 0x0b
+#define STA32X_AUTO2 0x0c
+#define STA32X_AUTO3 0x0d
+#define STA32X_C1CFG 0x0e
+#define STA32X_C2CFG 0x0f
+#define STA32X_C3CFG 0x10
+#define STA32X_TONE 0x11
+#define STA32X_L1AR 0x12
+#define STA32X_L1ATRT 0x13
+#define STA32X_L2AR 0x14
+#define STA32X_L2ATRT 0x15
+#define STA32X_CFADDR2 0x16
+#define STA32X_B1CF1 0x17
+#define STA32X_B1CF2 0x18
+#define STA32X_B1CF3 0x19
+#define STA32X_B2CF1 0x1a
+#define STA32X_B2CF2 0x1b
+#define STA32X_B2CF3 0x1c
+#define STA32X_A1CF1 0x1d
+#define STA32X_A1CF2 0x1e
+#define STA32X_A1CF3 0x1f
+#define STA32X_A2CF1 0x20
+#define STA32X_A2CF2 0x21
+#define STA32X_A2CF3 0x22
+#define STA32X_B0CF1 0x23
+#define STA32X_B0CF2 0x24
+#define STA32X_B0CF3 0x25
+#define STA32X_CFUD 0x26
+#define STA32X_MPCC1 0x27
+#define STA32X_MPCC2 0x28
+/* Reserved 0x29 */
+/* Reserved 0x2a */
+#define STA32X_Reserved 0x2a
+#define STA32X_FDRC1 0x2b
+#define STA32X_FDRC2 0x2c
+/* Reserved 0x2d */
+
+
+/* STA326 register field definitions */
+
+/* 0x00 CONFA */
+#define STA32X_CONFA_MCS_MASK 0x03
+#define STA32X_CONFA_MCS_SHIFT 0
+#define STA32X_CONFA_IR_MASK 0x18
+#define STA32X_CONFA_IR_SHIFT 3
+#define STA32X_CONFA_TWRB 0x20
+#define STA32X_CONFA_TWAB 0x40
+#define STA32X_CONFA_FDRB 0x80
+
+/* 0x01 CONFB */
+#define STA32X_CONFB_SAI_MASK 0x0f
+#define STA32X_CONFB_SAI_SHIFT 0
+#define STA32X_CONFB_SAIFB 0x10
+#define STA32X_CONFB_DSCKE 0x20
+#define STA32X_CONFB_C1IM 0x40
+#define STA32X_CONFB_C2IM 0x80
+
+/* 0x02 CONFC */
+#define STA32X_CONFC_OM_MASK 0x03
+#define STA32X_CONFC_OM_SHIFT 0
+#define STA32X_CONFC_CSZ_MASK 0x7c
+#define STA32X_CONFC_CSZ_SHIFT 2
+
+/* 0x03 CONFD */
+#define STA32X_CONFD_HPB 0x01
+#define STA32X_CONFD_HPB_SHIFT 0
+#define STA32X_CONFD_DEMP 0x02
+#define STA32X_CONFD_DEMP_SHIFT 1
+#define STA32X_CONFD_DSPB 0x04
+#define STA32X_CONFD_DSPB_SHIFT 2
+#define STA32X_CONFD_PSL 0x08
+#define STA32X_CONFD_PSL_SHIFT 3
+#define STA32X_CONFD_BQL 0x10
+#define STA32X_CONFD_BQL_SHIFT 4
+#define STA32X_CONFD_DRC 0x20
+#define STA32X_CONFD_DRC_SHIFT 5
+#define STA32X_CONFD_ZDE 0x40
+#define STA32X_CONFD_ZDE_SHIFT 6
+#define STA32X_CONFD_MME 0x80
+#define STA32X_CONFD_MME_SHIFT 7
+
+/* 0x04 CONFE */
+#define STA32X_CONFE_MPCV 0x01
+#define STA32X_CONFE_MPCV_SHIFT 0
+#define STA32X_CONFE_MPC 0x02
+#define STA32X_CONFE_MPC_SHIFT 1
+#define STA32X_CONFE_AME 0x08
+#define STA32X_CONFE_AME_SHIFT 3
+#define STA32X_CONFE_PWMS 0x10
+#define STA32X_CONFE_PWMS_SHIFT 4
+#define STA32X_CONFE_ZCE 0x40
+#define STA32X_CONFE_ZCE_SHIFT 6
+#define STA32X_CONFE_SVE 0x80
+#define STA32X_CONFE_SVE_SHIFT 7
+
+/* 0x05 CONFF */
+#define STA32X_CONFF_OCFG_MASK 0x03
+#define STA32X_CONFF_OCFG_SHIFT 0
+#define STA32X_CONFF_IDE 0x04
+#define STA32X_CONFF_IDE_SHIFT 3
+#define STA32X_CONFF_BCLE 0x08
+#define STA32X_CONFF_ECLE 0x20
+#define STA32X_CONFF_PWDN 0x40
+#define STA32X_CONFF_EAPD 0x80
+
+/* 0x06 MMUTE */
+#define STA32X_MMUTE_MMUTE 0x01
+
+/* 0x0b AUTO1 */
+#define STA32X_AUTO1_AMEQ_MASK 0x03
+#define STA32X_AUTO1_AMEQ_SHIFT 0
+#define STA32X_AUTO1_AMV_MASK 0xc0
+#define STA32X_AUTO1_AMV_SHIFT 2
+#define STA32X_AUTO1_AMGC_MASK 0x30
+#define STA32X_AUTO1_AMGC_SHIFT 4
+#define STA32X_AUTO1_AMPS 0x80
+
+/* 0x0c AUTO2 */
+#define STA32X_AUTO2_AMAME 0x01
+#define STA32X_AUTO2_AMAM_MASK 0x0e
+#define STA32X_AUTO2_AMAM_SHIFT 1
+#define STA32X_AUTO2_XO_MASK 0xf0
+#define STA32X_AUTO2_XO_SHIFT 4
+
+/* 0x0d AUTO3 */
+#define STA32X_AUTO3_PEQ_MASK 0x1f
+#define STA32X_AUTO3_PEQ_SHIFT 0
+
+/* 0x0e 0x0f 0x10 CxCFG */
+#define STA32X_CxCFG_TCB 0x01 /* only C1 and C2 */
+#define STA32X_CxCFG_TCB_SHIFT 0
+#define STA32X_CxCFG_EQBP 0x02 /* only C1 and C2 */
+#define STA32X_CxCFG_EQBP_SHIFT 1
+#define STA32X_CxCFG_VBP 0x03
+#define STA32X_CxCFG_VBP_SHIFT 2
+#define STA32X_CxCFG_BO 0x04
+#define STA32X_CxCFG_LS_MASK 0x30
+#define STA32X_CxCFG_LS_SHIFT 4
+#define STA32X_CxCFG_OM_MASK 0xc0
+#define STA32X_CxCFG_OM_SHIFT 6
+
+/* 0x11 TONE */
+#define STA32X_TONE_BTC_SHIFT 0
+#define STA32X_TONE_TTC_SHIFT 4
+
+/* 0x12 0x13 0x14 0x15 limiter attack/release */
+#define STA32X_LxA_SHIFT 0
+#define STA32X_LxR_SHIFT 4
+
+/* 0x26 CFUD */
+#define STA32X_CFUD_W1 0x01
+#define STA32X_CFUD_WA 0x02
+#define STA32X_CFUD_R1 0x04
+#define STA32X_CFUD_RA 0x08
+
+
+/* biquad filter coefficient table offsets */
+#define STA32X_C1_BQ_BASE 0
+#define STA32X_C2_BQ_BASE 20
+#define STA32X_CH_BQ_NUM 4
+#define STA32X_BQ_NUM_COEF 5
+#define STA32X_XO_HP_BQ_BASE 40
+#define STA32X_XO_LP_BQ_BASE 45
+#define STA32X_C1_PRESCALE 50
+#define STA32X_C2_PRESCALE 51
+#define STA32X_C1_POSTSCALE 52
+#define STA32X_C2_POSTSCALE 53
+#define STA32X_C3_POSTSCALE 54
+#define STA32X_TW_POSTSCALE 55
+#define STA32X_C1_MIX1 56
+#define STA32X_C1_MIX2 57
+#define STA32X_C2_MIX1 58
+#define STA32X_C2_MIX2 59
+#define STA32X_C3_MIX1 60
+#define STA32X_C3_MIX2 61
+
+#endif /* _ASOC_STA_32X_H */
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index e2a7608..7859bdc 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -161,10 +161,18 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
}
- /* Configure PLL */
+ /**
+ * Configure PLL
+ * fsref = (mclk * PLLM) / 2048
+ * where PLLM = J.DDDD (DDDD register ranges from 0 to 9999, decimal)
+ */
pval = 1;
- jval = (fsref == 44100) ? 7 : 8;
- dval = (fsref == 44100) ? 5264 : 1920;
+ /* compute J portion of multiplier */
+ jval = fsref / (aic26->mclk / 2048);
+ /* compute fractional DDDD component of multiplier */
+ dval = fsref - (jval * (aic26->mclk / 2048));
+ dval = (10000 * dval) / (aic26->mclk / 2048);
+ dev_dbg(&aic26->spi->dev, "Setting PLLM to %d.%04d\n", jval, dval);
qval = 0;
reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg);
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index c3d96fc..0963c4c 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -226,11 +226,13 @@ static const char *aic3x_adc_hpf[] =
#define RDAC_ENUM 1
#define LHPCOM_ENUM 2
#define RHPCOM_ENUM 3
-#define LINE1L_ENUM 4
-#define LINE1R_ENUM 5
-#define LINE2L_ENUM 6
-#define LINE2R_ENUM 7
-#define ADC_HPF_ENUM 8
+#define LINE1L_2_L_ENUM 4
+#define LINE1L_2_R_ENUM 5
+#define LINE1R_2_L_ENUM 6
+#define LINE1R_2_R_ENUM 7
+#define LINE2L_ENUM 8
+#define LINE2R_ENUM 9
+#define ADC_HPF_ENUM 10
static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
@@ -238,6 +240,8 @@ static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux),
SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux),
SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+ SOC_ENUM_SINGLE(LINE1L_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+ SOC_ENUM_SINGLE(LINE1R_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
@@ -490,12 +494,16 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
};
/* Left Line1 Mux */
-static const struct snd_kcontrol_new aic3x_left_line1_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]);
+static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_L_ENUM]);
+static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_R_ENUM]);
/* Right Line1 Mux */
-static const struct snd_kcontrol_new aic3x_right_line1_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]);
+static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_R_ENUM]);
+static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_L_ENUM]);
/* Left Line2 Mux */
static const struct snd_kcontrol_new aic3x_left_line2_mux_controls =
@@ -535,9 +543,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
&aic3x_left_pga_mixer_controls[0],
ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
- &aic3x_left_line1_mux_controls),
+ &aic3x_left_line1l_mux_controls),
SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0,
- &aic3x_left_line1_mux_controls),
+ &aic3x_left_line1r_mux_controls),
SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
&aic3x_left_line2_mux_controls),
@@ -548,9 +556,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
&aic3x_right_pga_mixer_controls[0],
ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0,
- &aic3x_right_line1_mux_controls),
+ &aic3x_right_line1l_mux_controls),
SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
- &aic3x_right_line1_mux_controls),
+ &aic3x_right_line1r_mux_controls),
SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
&aic3x_right_line2_mux_controls),
@@ -1114,12 +1122,19 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
/* Sync reg_cache with the hardware */
codec->cache_only = 0;
- for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++)
+ for (i = AIC3X_SAMPLE_RATE_SEL_REG; i < ARRAY_SIZE(aic3x_reg); i++)
snd_soc_write(codec, i, cache[i]);
if (aic3x->model == AIC3X_MODEL_3007)
aic3x_init_3007(codec);
codec->cache_sync = 0;
} else {
+ /*
+ * Do soft reset to this codec instance in order to clear
+ * possible VDD leakage currents in case the supply regulators
+ * remain on
+ */
+ snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
+ codec->cache_sync = 1;
aic3x->power = 0;
/* HW writes are needless when bias is off */
codec->cache_only = 1;
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 4c33663..cd63bba 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -954,9 +954,9 @@ static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0);
/*
* MICGAIN volume control:
- * from -6 to 30 dB in 6 dB steps
+ * from 6 to 30 dB in 6 dB steps
*/
-static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0);
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
/*
* AFMGAIN volume control:
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 2dc964b..76b4361 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -175,6 +175,7 @@ static const struct snd_kcontrol_new wm8731_input_mux_controls =
SOC_DAPM_ENUM("Input Select", wm8731_insel_enum);
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("ACTIVE",WM8731_ACTIVE, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0),
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
&wm8731_output_mixer_controls[0],
@@ -204,6 +205,8 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
static const struct snd_soc_dapm_route wm8731_intercon[] = {
{"DAC", NULL, "OSC", wm8731_check_osc},
{"ADC", NULL, "OSC", wm8731_check_osc},
+ {"DAC", NULL, "ACTIVE"},
+ {"ADC", NULL, "ACTIVE"},
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
@@ -315,29 +318,6 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
-
- /* set active */
- snd_soc_write(codec, WM8731_ACTIVE, 0x0001);
-
- return 0;
-}
-
-static void wm8731_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
-
- /* deactivate */
- if (!codec->active) {
- udelay(50);
- snd_soc_write(codec, WM8731_ACTIVE, 0x0);
- }
-}
-
static int wm8731_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
@@ -480,7 +460,6 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, WM8731_ACTIVE, 0x0);
snd_soc_write(codec, WM8731_PWR, 0xffff);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
@@ -496,9 +475,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8731_dai_ops = {
- .prepare = wm8731_pcm_prepare,
.hw_params = wm8731_hw_params,
- .shutdown = wm8731_shutdown,
.digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c
new file mode 100644
index 0000000..a2a09f8
--- /dev/null
+++ b/sound/soc/codecs/wm8782.c
@@ -0,0 +1,80 @@
+/*
+ * sound/soc/codecs/wm8782.c
+ * simple, strap-pin configured 24bit 2ch ADC
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Johannes Stezenbach <js@sig21.net>
+ *
+ * based on ad73311.c
+ * Copyright: Analog Device Inc.
+ * Author: Cliff Cai <cliff.cai@analog.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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver wm8782_dai = {
+ .name = "wm8782",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ /* For configurations with FSAMPEN=0 */
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8782;
+
+static __devinit int wm8782_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm8782, &wm8782_dai, 1);
+}
+
+static int __devexit wm8782_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm8782_codec_driver = {
+ .driver = {
+ .name = "wm8782",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8782_probe,
+ .remove = wm8782_remove,
+};
+
+static int __init wm8782_init(void)
+{
+ return platform_driver_register(&wm8782_codec_driver);
+}
+module_init(wm8782_init);
+
+static void __exit wm8782_exit(void)
+{
+ platform_driver_unregister(&wm8782_codec_driver);
+}
+module_exit(wm8782_exit);
+
+MODULE_DESCRIPTION("ASoC WM8782 driver");
+MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 449ea09..082040e 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1167,6 +1167,7 @@ static int wm8900_resume(struct snd_soc_codec *codec)
ret = wm8900_set_fll(codec, 0, fll_in, fll_out);
if (ret != 0) {
dev_err(codec->dev, "Failed to restart FLL\n");
+ kfree(cache);
return ret;
}
}
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 9b3bba4..b085575 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2560,6 +2560,7 @@ static __devexit int wm8904_i2c_remove(struct i2c_client *client)
static const struct i2c_device_id wm8904_i2c_id[] = {
{ "wm8904", WM8904 },
{ "wm8912", WM8912 },
+ { "wm8918", WM8904 }, /* Actually a subset, updates to follow */
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id);
diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c
index e2ab4fa..423baa9 100644
--- a/sound/soc/codecs/wm8915.c
+++ b/sound/soc/codecs/wm8915.c
@@ -41,14 +41,12 @@
#define HPOUT2L 4
#define HPOUT2R 8
-#define WM8915_NUM_SUPPLIES 6
+#define WM8915_NUM_SUPPLIES 4
static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = {
- "DCVDD",
"DBVDD",
"AVDD1",
"AVDD2",
"CPVDD",
- "MICVDD",
};
struct wm8915_priv {
@@ -57,6 +55,7 @@ struct wm8915_priv {
int ldo1ena;
int sysclk;
+ int sysclk_src;
int fll_src;
int fll_fref;
@@ -76,6 +75,7 @@ struct wm8915_priv {
struct wm8915_pdata pdata;
int rx_rate[WM8915_AIFS];
+ int bclk_rate[WM8915_AIFS];
/* Platform dependant ReTune mobile configuration */
int num_retune_mobile_texts;
@@ -113,8 +113,6 @@ 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,
@@ -1565,6 +1563,50 @@ static int wm8915_reset(struct snd_soc_codec *codec)
return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915);
}
+static const int bclk_divs[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static void wm8915_update_bclk(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int aif, best, cur_val, bclk_rate, bclk_reg, i;
+
+ /* Don't bother if we're in a low frequency idle mode that
+ * can't support audio.
+ */
+ if (wm8915->sysclk < 64000)
+ return;
+
+ for (aif = 0; aif < WM8915_AIFS; aif++) {
+ switch (aif) {
+ case 0:
+ bclk_reg = WM8915_AIF1_BCLK;
+ break;
+ case 1:
+ bclk_reg = WM8915_AIF2_BCLK;
+ break;
+ }
+
+ bclk_rate = wm8915->bclk_rate[aif];
+
+ /* 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(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8915_AIF1_BCLK_DIV_MASK, best);
+ }
+}
+
static int wm8915_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -1717,10 +1759,6 @@ static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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
};
@@ -1731,17 +1769,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
{
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 bits, i, bclk_rate;
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;
- }
+ int aifdata_reg, lrclk_reg, dsp_shift;
switch (dai->id) {
case 0:
@@ -1753,7 +1785,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
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:
@@ -1765,7 +1796,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
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:
@@ -1779,6 +1809,9 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
return bclk_rate;
}
+ wm8915->bclk_rate[dai->id] = bclk_rate;
+ wm8915->rx_rate[dai->id] = params_rate(params);
+
/* Needs looking at for TDM */
bits = snd_pcm_format_width(params_format(params));
if (bits < 0)
@@ -1796,18 +1829,7 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
}
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;
+ wm8915_update_bclk(codec);
lrclk = bclk_rate / params_rate(params);
dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
@@ -1817,14 +1839,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
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;
}
@@ -1838,6 +1857,9 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
int src;
int old;
+ if (freq == wm8915->sysclk && clk_id == wm8915->sysclk_src)
+ return 0;
+
/* Disable SYSCLK while we reconfigure */
old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1) & WM8915_SYSCLK_ENA;
snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
@@ -1882,6 +1904,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
return -EINVAL;
}
+ wm8915_update_bclk(codec);
+
snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK,
src << WM8915_SYSCLK_SRC_SHIFT | ratediv);
@@ -1889,6 +1913,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
WM8915_SYSCLK_ENA, old);
+ wm8915->sysclk_src = clk_id;
+
return 0;
}
@@ -2007,6 +2033,7 @@ 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 i2c_client *i2c = to_i2c_client(codec->dev);
struct _fll_div fll_div;
unsigned long timeout;
int ret, reg;
@@ -2093,7 +2120,18 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
else
timeout = msecs_to_jiffies(2);
- wait_for_completion_timeout(&wm8915->fll_lock, timeout);
+ /* Allow substantially longer if we've actually got the IRQ */
+ if (i2c->irq)
+ timeout *= 1000;
+
+ ret = wait_for_completion_timeout(&wm8915->fll_lock, timeout);
+
+ if (ret == 0 && i2c->irq) {
+ dev_err(codec->dev, "Timed out waiting for FLL\n");
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
@@ -2101,7 +2139,7 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
wm8915->fll_fout = Fout;
wm8915->fll_src = source;
- return 0;
+ return ret;
}
#ifdef CONFIG_GPIOLIB
@@ -2293,6 +2331,12 @@ static void wm8915_micd(struct snd_soc_codec *codec)
SND_JACK_HEADSET | SND_JACK_BTN_0);
wm8915->jack_mic = true;
wm8915->detecting = false;
+
+ /* Increase poll rate to give better responsiveness
+ * for buttons */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ 5 << WM8915_MICD_RATE_SHIFT);
}
/* If we detected a lower impedence during initial startup
@@ -2333,15 +2377,17 @@ static void wm8915_micd(struct snd_soc_codec *codec)
SND_JACK_HEADPHONE,
SND_JACK_HEADSET |
SND_JACK_BTN_0);
+
+ /* Increase the detection rate a bit for
+ * responsiveness.
+ */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ 7 << WM8915_MICD_RATE_SHIFT);
+
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)
@@ -2383,6 +2429,20 @@ static irqreturn_t wm8915_irq(int irq, void *data)
}
}
+static irqreturn_t wm8915_edge_irq(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+ irqreturn_t val;
+
+ do {
+ val = wm8915_irq(irq, data);
+ if (val != IRQ_NONE)
+ ret = val;
+ } while (val != IRQ_NONE);
+
+ return ret;
+}
+
static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec)
{
struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
@@ -2482,8 +2542,6 @@ static int wm8915_probe(struct snd_soc_codec *codec)
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++) {
@@ -2709,8 +2767,14 @@ static int wm8915_probe(struct snd_soc_codec *codec)
irq_flags |= IRQF_ONESHOT;
- ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
- irq_flags, "wm8915", codec);
+ if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
+ ret = request_threaded_irq(i2c->irq, NULL,
+ wm8915_edge_irq,
+ irq_flags, "wm8915", codec);
+ else
+ 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,
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 25580e3..056daa0 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -297,8 +297,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec)
if (ret)
goto error_ret;
ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
- if (ret)
- goto error_ret;
error_ret:
return ret;
@@ -683,8 +681,6 @@ static int wm8940_resume(struct snd_soc_codec *codec)
}
}
ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- if (ret)
- goto error_ret;
error_ret:
return ret;
@@ -730,9 +726,6 @@ static int wm8940_probe(struct snd_soc_codec *codec)
if (ret)
return ret;
ret = wm8940_add_widgets(codec);
- if (ret)
- return ret;
-
return ret;
}
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 5e05eed..8499c56 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -78,6 +78,8 @@ struct wm8962_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
+
+ int irq;
};
/* We can't use the same notifier block for more than one supply and
@@ -1982,6 +1984,7 @@ static const unsigned int classd_tlv[] = {
0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
};
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
/* The VU bits for the headphones are in a different register to the mute
* bits and only take effect on the PGA if it is actually powered.
@@ -2119,6 +2122,18 @@ SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4,
SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0,
classd_tlv),
+
+SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0),
+SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
+ WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
+ WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
};
static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2184,6 +2199,8 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ unsigned long timeout;
int src;
int fll;
@@ -2203,9 +2220,19 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (fll)
+ if (fll) {
snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
WM8962_FLL_ENA, WM8962_FLL_ENA);
+ if (wm8962->irq) {
+ timeout = msecs_to_jiffies(5);
+ timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+ timeout);
+
+ if (timeout == 0)
+ dev_err(codec->dev,
+ "Timed out starting FLL\n");
+ }
+ }
break;
case SND_SOC_DAPM_POST_PMD:
@@ -2763,18 +2790,44 @@ static const int bclk_divs[] = {
1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32
};
+static const int sysclk_rates[] = {
+ 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
+};
+
static void wm8962_configure_bclk(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int dspclk, i;
int clocking2 = 0;
+ int clocking4 = 0;
int aif2 = 0;
- if (!wm8962->bclk) {
- dev_dbg(codec->dev, "No BCLK rate configured\n");
+ if (!wm8962->sysclk_rate) {
+ dev_dbg(codec->dev, "No SYSCLK configured\n");
+ return;
+ }
+
+ if (!wm8962->bclk || !wm8962->lrclk) {
+ dev_dbg(codec->dev, "No audio clocks configured\n");
return;
}
+ for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
+ if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) {
+ clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(sysclk_rates)) {
+ dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
+ wm8962->sysclk_rate / wm8962->lrclk);
+ return;
+ }
+
+ snd_soc_update_bits(codec, WM8962_CLOCKING_4,
+ WM8962_SYSCLK_RATE_MASK, clocking4);
+
dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
if (dspclk < 0) {
dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
@@ -2844,6 +2897,8 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
/* VMID 2*50k */
snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x80);
+
+ wm8962_configure_bclk(codec);
break;
case SND_SOC_BIAS_STANDBY:
@@ -2876,8 +2931,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, WM8962_CLOCKING2,
WM8962_CLKREG_OVD,
WM8962_CLKREG_OVD);
-
- wm8962_configure_bclk(codec);
}
/* VMID 2*250k */
@@ -2918,10 +2971,6 @@ static const struct {
{ 96000, 6 },
};
-static const int sysclk_rates[] = {
- 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
-};
-
static int wm8962_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -2929,41 +2978,27 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- int rate = params_rate(params);
int i;
int aif0 = 0;
int adctl3 = 0;
- int clocking4 = 0;
wm8962->bclk = snd_soc_params_to_bclk(params);
wm8962->lrclk = params_rate(params);
for (i = 0; i < ARRAY_SIZE(sr_vals); i++) {
- if (sr_vals[i].rate == rate) {
+ if (sr_vals[i].rate == wm8962->lrclk) {
adctl3 |= sr_vals[i].reg;
break;
}
}
if (i == ARRAY_SIZE(sr_vals)) {
- dev_err(codec->dev, "Unsupported rate %dHz\n", rate);
+ dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
return -EINVAL;
}
- if (rate % 8000 == 0)
+ if (wm8962->lrclk % 8000 == 0)
adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
- for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
- if (sysclk_rates[i] == wm8962->sysclk_rate / rate) {
- clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
- break;
- }
- }
- if (i == ARRAY_SIZE(sysclk_rates)) {
- dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
- wm8962->sysclk_rate / rate);
- return -EINVAL;
- }
-
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
@@ -2985,8 +3020,6 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3,
WM8962_SAMPLE_RATE_INT_MODE |
WM8962_SAMPLE_RATE_MASK, adctl3);
- snd_soc_update_bits(codec, WM8962_CLOCKING_4,
- WM8962_SYSCLK_RATE_MASK, clocking4);
wm8962_configure_bclk(codec);
@@ -3261,16 +3294,31 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, 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);
+ ret = 0;
+
+ if (fll1 & WM8962_FLL_ENA) {
+ /* This should be a massive overestimate but go even
+ * higher if we'll error out
+ */
+ if (wm8962->irq)
+ timeout = msecs_to_jiffies(5);
+ else
+ timeout = msecs_to_jiffies(1);
+
+ timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+ timeout);
- wait_for_completion_timeout(&wm8962->fll_lock, timeout);
+ if (timeout == 0 && wm8962->irq) {
+ dev_err(codec->dev, "FLL lock timed out");
+ ret = -ETIMEDOUT;
+ }
+ }
wm8962->fll_fref = Fref;
wm8962->fll_fout = Fout;
wm8962->fll_src = source;
- return 0;
+ return ret;
}
static int wm8962_mute(struct snd_soc_dai *dai, int mute)
@@ -3731,8 +3779,6 @@ static int wm8962_probe(struct snd_soc_codec *codec)
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
- struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
- dev);
u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
bool dmicclk, dmicdat;
@@ -3871,6 +3917,9 @@ static int wm8962_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+ /* Stereo control for EQ */
+ snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0);
+
wm8962_add_widgets(codec);
/* Save boards having to disable DMIC when not in use */
@@ -3899,7 +3948,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_init_beep(codec);
wm8962_init_gpio(codec);
- if (i2c->irq) {
+ if (wm8962->irq) {
if (pdata && pdata->irq_active_low) {
trigger = IRQF_TRIGGER_LOW;
irq_pol = WM8962_IRQ_POL;
@@ -3911,12 +3960,13 @@ static int wm8962_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL,
WM8962_IRQ_POL, irq_pol);
- ret = request_threaded_irq(i2c->irq, NULL, wm8962_irq,
+ ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq,
trigger | IRQF_ONESHOT,
"wm8962", codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
- i2c->irq, ret);
+ wm8962->irq, ret);
+ wm8962->irq = 0;
/* Non-fatal */
} else {
/* Enable some IRQs by default */
@@ -3941,12 +3991,10 @@ err:
static int wm8962_remove(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
- dev);
int i;
- if (i2c->irq)
- free_irq(i2c->irq, codec);
+ if (wm8962->irq)
+ free_irq(wm8962->irq, codec);
cancel_delayed_work_sync(&wm8962->mic_work);
@@ -3986,6 +4034,8 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8962);
+ wm8962->irq = i2c->irq;
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8962, &wm8962_dai, 1);
if (ret < 0)
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
new file mode 100644
index 0000000..17f04ec
--- /dev/null
+++ b/sound/soc/codecs/wm8983.c
@@ -0,0 +1,1203 @@
+/*
+ * wm8983.c -- WM8983 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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/spi/spi.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 "wm8983.h"
+
+static const u16 wm8983_reg_defs[WM8983_MAX_REGISTER + 1] = {
+ [0x00] = 0x0000, /* R0 - Software Reset */
+ [0x01] = 0x0000, /* R1 - Power management 1 */
+ [0x02] = 0x0000, /* R2 - Power management 2 */
+ [0x03] = 0x0000, /* R3 - Power management 3 */
+ [0x04] = 0x0050, /* R4 - Audio Interface */
+ [0x05] = 0x0000, /* R5 - Companding control */
+ [0x06] = 0x0140, /* R6 - Clock Gen control */
+ [0x07] = 0x0000, /* R7 - Additional control */
+ [0x08] = 0x0000, /* R8 - GPIO Control */
+ [0x09] = 0x0000, /* R9 - Jack Detect Control 1 */
+ [0x0A] = 0x0000, /* R10 - DAC Control */
+ [0x0B] = 0x00FF, /* R11 - Left DAC digital Vol */
+ [0x0C] = 0x00FF, /* R12 - Right DAC digital vol */
+ [0x0D] = 0x0000, /* R13 - Jack Detect Control 2 */
+ [0x0E] = 0x0100, /* R14 - ADC Control */
+ [0x0F] = 0x00FF, /* R15 - Left ADC Digital Vol */
+ [0x10] = 0x00FF, /* R16 - Right ADC Digital Vol */
+ [0x12] = 0x012C, /* R18 - EQ1 - low shelf */
+ [0x13] = 0x002C, /* R19 - EQ2 - peak 1 */
+ [0x14] = 0x002C, /* R20 - EQ3 - peak 2 */
+ [0x15] = 0x002C, /* R21 - EQ4 - peak 3 */
+ [0x16] = 0x002C, /* R22 - EQ5 - high shelf */
+ [0x18] = 0x0032, /* R24 - DAC Limiter 1 */
+ [0x19] = 0x0000, /* R25 - DAC Limiter 2 */
+ [0x1B] = 0x0000, /* R27 - Notch Filter 1 */
+ [0x1C] = 0x0000, /* R28 - Notch Filter 2 */
+ [0x1D] = 0x0000, /* R29 - Notch Filter 3 */
+ [0x1E] = 0x0000, /* R30 - Notch Filter 4 */
+ [0x20] = 0x0038, /* R32 - ALC control 1 */
+ [0x21] = 0x000B, /* R33 - ALC control 2 */
+ [0x22] = 0x0032, /* R34 - ALC control 3 */
+ [0x23] = 0x0000, /* R35 - Noise Gate */
+ [0x24] = 0x0008, /* R36 - PLL N */
+ [0x25] = 0x000C, /* R37 - PLL K 1 */
+ [0x26] = 0x0093, /* R38 - PLL K 2 */
+ [0x27] = 0x00E9, /* R39 - PLL K 3 */
+ [0x29] = 0x0000, /* R41 - 3D control */
+ [0x2A] = 0x0000, /* R42 - OUT4 to ADC */
+ [0x2B] = 0x0000, /* R43 - Beep control */
+ [0x2C] = 0x0033, /* R44 - Input ctrl */
+ [0x2D] = 0x0010, /* R45 - Left INP PGA gain ctrl */
+ [0x2E] = 0x0010, /* R46 - Right INP PGA gain ctrl */
+ [0x2F] = 0x0100, /* R47 - Left ADC BOOST ctrl */
+ [0x30] = 0x0100, /* R48 - Right ADC BOOST ctrl */
+ [0x31] = 0x0002, /* R49 - Output ctrl */
+ [0x32] = 0x0001, /* R50 - Left mixer ctrl */
+ [0x33] = 0x0001, /* R51 - Right mixer ctrl */
+ [0x34] = 0x0039, /* R52 - LOUT1 (HP) volume ctrl */
+ [0x35] = 0x0039, /* R53 - ROUT1 (HP) volume ctrl */
+ [0x36] = 0x0039, /* R54 - LOUT2 (SPK) volume ctrl */
+ [0x37] = 0x0039, /* R55 - ROUT2 (SPK) volume ctrl */
+ [0x38] = 0x0001, /* R56 - OUT3 mixer ctrl */
+ [0x39] = 0x0001, /* R57 - OUT4 (MONO) mix ctrl */
+ [0x3D] = 0x0000 /* R61 - BIAS CTRL */
+};
+
+static const struct wm8983_reg_access {
+ u16 read; /* Mask of readable bits */
+ u16 write; /* Mask of writable bits */
+} wm8983_access_masks[WM8983_MAX_REGISTER + 1] = {
+ [0x00] = { 0x0000, 0x01FF }, /* R0 - Software Reset */
+ [0x01] = { 0x0000, 0x01FF }, /* R1 - Power management 1 */
+ [0x02] = { 0x0000, 0x01FF }, /* R2 - Power management 2 */
+ [0x03] = { 0x0000, 0x01EF }, /* R3 - Power management 3 */
+ [0x04] = { 0x0000, 0x01FF }, /* R4 - Audio Interface */
+ [0x05] = { 0x0000, 0x003F }, /* R5 - Companding control */
+ [0x06] = { 0x0000, 0x01FD }, /* R6 - Clock Gen control */
+ [0x07] = { 0x0000, 0x000F }, /* R7 - Additional control */
+ [0x08] = { 0x0000, 0x003F }, /* R8 - GPIO Control */
+ [0x09] = { 0x0000, 0x0070 }, /* R9 - Jack Detect Control 1 */
+ [0x0A] = { 0x0000, 0x004F }, /* R10 - DAC Control */
+ [0x0B] = { 0x0000, 0x01FF }, /* R11 - Left DAC digital Vol */
+ [0x0C] = { 0x0000, 0x01FF }, /* R12 - Right DAC digital vol */
+ [0x0D] = { 0x0000, 0x00FF }, /* R13 - Jack Detect Control 2 */
+ [0x0E] = { 0x0000, 0x01FB }, /* R14 - ADC Control */
+ [0x0F] = { 0x0000, 0x01FF }, /* R15 - Left ADC Digital Vol */
+ [0x10] = { 0x0000, 0x01FF }, /* R16 - Right ADC Digital Vol */
+ [0x12] = { 0x0000, 0x017F }, /* R18 - EQ1 - low shelf */
+ [0x13] = { 0x0000, 0x017F }, /* R19 - EQ2 - peak 1 */
+ [0x14] = { 0x0000, 0x017F }, /* R20 - EQ3 - peak 2 */
+ [0x15] = { 0x0000, 0x017F }, /* R21 - EQ4 - peak 3 */
+ [0x16] = { 0x0000, 0x007F }, /* R22 - EQ5 - high shelf */
+ [0x18] = { 0x0000, 0x01FF }, /* R24 - DAC Limiter 1 */
+ [0x19] = { 0x0000, 0x007F }, /* R25 - DAC Limiter 2 */
+ [0x1B] = { 0x0000, 0x01FF }, /* R27 - Notch Filter 1 */
+ [0x1C] = { 0x0000, 0x017F }, /* R28 - Notch Filter 2 */
+ [0x1D] = { 0x0000, 0x017F }, /* R29 - Notch Filter 3 */
+ [0x1E] = { 0x0000, 0x017F }, /* R30 - Notch Filter 4 */
+ [0x20] = { 0x0000, 0x01BF }, /* R32 - ALC control 1 */
+ [0x21] = { 0x0000, 0x00FF }, /* R33 - ALC control 2 */
+ [0x22] = { 0x0000, 0x01FF }, /* R34 - ALC control 3 */
+ [0x23] = { 0x0000, 0x000F }, /* R35 - Noise Gate */
+ [0x24] = { 0x0000, 0x001F }, /* R36 - PLL N */
+ [0x25] = { 0x0000, 0x003F }, /* R37 - PLL K 1 */
+ [0x26] = { 0x0000, 0x01FF }, /* R38 - PLL K 2 */
+ [0x27] = { 0x0000, 0x01FF }, /* R39 - PLL K 3 */
+ [0x29] = { 0x0000, 0x000F }, /* R41 - 3D control */
+ [0x2A] = { 0x0000, 0x01E7 }, /* R42 - OUT4 to ADC */
+ [0x2B] = { 0x0000, 0x01BF }, /* R43 - Beep control */
+ [0x2C] = { 0x0000, 0x0177 }, /* R44 - Input ctrl */
+ [0x2D] = { 0x0000, 0x01FF }, /* R45 - Left INP PGA gain ctrl */
+ [0x2E] = { 0x0000, 0x01FF }, /* R46 - Right INP PGA gain ctrl */
+ [0x2F] = { 0x0000, 0x0177 }, /* R47 - Left ADC BOOST ctrl */
+ [0x30] = { 0x0000, 0x0177 }, /* R48 - Right ADC BOOST ctrl */
+ [0x31] = { 0x0000, 0x007F }, /* R49 - Output ctrl */
+ [0x32] = { 0x0000, 0x01FF }, /* R50 - Left mixer ctrl */
+ [0x33] = { 0x0000, 0x01FF }, /* R51 - Right mixer ctrl */
+ [0x34] = { 0x0000, 0x01FF }, /* R52 - LOUT1 (HP) volume ctrl */
+ [0x35] = { 0x0000, 0x01FF }, /* R53 - ROUT1 (HP) volume ctrl */
+ [0x36] = { 0x0000, 0x01FF }, /* R54 - LOUT2 (SPK) volume ctrl */
+ [0x37] = { 0x0000, 0x01FF }, /* R55 - ROUT2 (SPK) volume ctrl */
+ [0x38] = { 0x0000, 0x004F }, /* R56 - OUT3 mixer ctrl */
+ [0x39] = { 0x0000, 0x00FF }, /* R57 - OUT4 (MONO) mix ctrl */
+ [0x3D] = { 0x0000, 0x0100 } /* R61 - BIAS CTRL */
+};
+
+/* vol/gain update regs */
+static const int vol_update_regs[] = {
+ WM8983_LEFT_DAC_DIGITAL_VOL,
+ WM8983_RIGHT_DAC_DIGITAL_VOL,
+ WM8983_LEFT_ADC_DIGITAL_VOL,
+ WM8983_RIGHT_ADC_DIGITAL_VOL,
+ WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL,
+ WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL,
+ WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ WM8983_RIGHT_INP_PGA_GAIN_CTRL
+};
+
+struct wm8983_priv {
+ enum snd_soc_control_type control_type;
+ u32 sysclk;
+ u32 bclk;
+};
+
+static const struct {
+ int div;
+ int ratio;
+} fs_ratios[] = {
+ { 10, 128 },
+ { 15, 192 },
+ { 20, 256 },
+ { 30, 384 },
+ { 40, 512 },
+ { 60, 768 },
+ { 80, 1024 },
+ { 120, 1536 }
+};
+
+static const int srates[] = { 48000, 32000, 24000, 16000, 12000, 8000 };
+
+static const int bclk_divs[] = {
+ 1, 2, 4, 8, 16, 32
+};
+
+static int eqmode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int eqmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lim_thresh_tlv, -600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lim_boost_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_min_tlv, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tar_tlv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(aux_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
+
+static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" };
+static const SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7,
+ alc_sel_text);
+
+static const char *alc_mode_text[] = { "ALC", "Limiter" };
+static const SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8,
+ alc_mode_text);
+
+static const char *filter_mode_text[] = { "Audio", "Application" };
+static const SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7,
+ filter_mode_text);
+
+static const char *eq_bw_text[] = { "Narrow", "Wide" };
+static const char *eqmode_text[] = { "Capture", "Playback" };
+static const SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text);
+
+static const char *eq1_cutoff_text[] = {
+ "80Hz", "105Hz", "135Hz", "175Hz"
+};
+static const SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5,
+ eq1_cutoff_text);
+static const char *eq2_cutoff_text[] = {
+ "230Hz", "300Hz", "385Hz", "500Hz"
+};
+static const SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text);
+static const SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5,
+ eq2_cutoff_text);
+static const char *eq3_cutoff_text[] = {
+ "650Hz", "850Hz", "1.1kHz", "1.4kHz"
+};
+static const SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text);
+static const SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5,
+ eq3_cutoff_text);
+static const char *eq4_cutoff_text[] = {
+ "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"
+};
+static const SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text);
+static const SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5,
+ eq4_cutoff_text);
+static const char *eq5_cutoff_text[] = {
+ "5.3kHz", "6.9kHz", "9kHz", "11.7kHz"
+};
+static const SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5,
+ eq5_cutoff_text);
+
+static const char *speaker_mode_text[] = { "Class A/B", "Class D" };
+static const SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text);
+
+static const char *depth_3d_text[] = {
+ "Off",
+ "6.67%",
+ "13.3%",
+ "20%",
+ "26.7%",
+ "33.3%",
+ "40%",
+ "46.6%",
+ "53.3%",
+ "60%",
+ "66.7%",
+ "73.3%",
+ "80%",
+ "86.7%",
+ "93.3%",
+ "100%"
+};
+static const SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0,
+ depth_3d_text);
+
+static const struct snd_kcontrol_new wm8983_snd_controls[] = {
+ SOC_SINGLE("Digital Loopback Switch", WM8983_COMPANDING_CONTROL,
+ 0, 1, 0),
+
+ SOC_ENUM("ALC Capture Function", alc_sel),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", WM8983_ALC_CONTROL_1,
+ 3, 7, 0, alc_max_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", WM8983_ALC_CONTROL_1,
+ 0, 7, 0, alc_min_tlv),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", WM8983_ALC_CONTROL_2,
+ 0, 15, 0, alc_tar_tlv),
+ SOC_SINGLE("ALC Capture Attack", WM8983_ALC_CONTROL_3, 0, 10, 0),
+ SOC_SINGLE("ALC Capture Hold", WM8983_ALC_CONTROL_2, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Decay", WM8983_ALC_CONTROL_3, 4, 10, 0),
+ SOC_ENUM("ALC Mode", alc_mode),
+ SOC_SINGLE("ALC Capture NG Switch", WM8983_NOISE_GATE,
+ 3, 1, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", WM8983_NOISE_GATE,
+ 0, 7, 1),
+
+ SOC_DOUBLE_R_TLV("Capture Volume", WM8983_LEFT_ADC_DIGITAL_VOL,
+ WM8983_RIGHT_ADC_DIGITAL_VOL, 0, 255, 0, adc_tlv),
+ SOC_DOUBLE_R("Capture PGA ZC Switch", WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ WM8983_RIGHT_INP_PGA_GAIN_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("Capture PGA Volume", WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ WM8983_RIGHT_INP_PGA_GAIN_CTRL, 0, 63, 0, pga_vol_tlv),
+
+ SOC_DOUBLE_R_TLV("Capture PGA Boost Volume",
+ WM8983_LEFT_ADC_BOOST_CTRL, WM8983_RIGHT_ADC_BOOST_CTRL,
+ 8, 1, 0, pga_boost_tlv),
+
+ SOC_DOUBLE("ADC Inversion Switch", WM8983_ADC_CONTROL, 0, 1, 1, 0),
+ SOC_SINGLE("ADC 128x Oversampling Switch", WM8983_ADC_CONTROL, 8, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Playback Volume", WM8983_LEFT_DAC_DIGITAL_VOL,
+ WM8983_RIGHT_DAC_DIGITAL_VOL, 0, 255, 0, dac_tlv),
+
+ SOC_SINGLE("DAC Playback Limiter Switch", WM8983_DAC_LIMITER_1, 8, 1, 0),
+ SOC_SINGLE("DAC Playback Limiter Decay", WM8983_DAC_LIMITER_1, 4, 10, 0),
+ SOC_SINGLE("DAC Playback Limiter Attack", WM8983_DAC_LIMITER_1, 0, 11, 0),
+ SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8983_DAC_LIMITER_2,
+ 4, 7, 1, lim_thresh_tlv),
+ SOC_SINGLE_TLV("DAC Playback Limiter Boost Volume", WM8983_DAC_LIMITER_2,
+ 0, 12, 0, lim_boost_tlv),
+ SOC_DOUBLE("DAC Inversion Switch", WM8983_DAC_CONTROL, 0, 1, 1, 0),
+ SOC_SINGLE("DAC Auto Mute Switch", WM8983_DAC_CONTROL, 2, 1, 0),
+ SOC_SINGLE("DAC 128x Oversampling Switch", WM8983_DAC_CONTROL, 3, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL, 0, 63, 0, out_tlv),
+ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R("Headphone Switch", WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL, 6, 1, 1),
+
+ SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL, 0, 63, 0, out_tlv),
+ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R("Speaker Switch", WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL, 6, 1, 1),
+
+ SOC_SINGLE("OUT3 Switch", WM8983_OUT3_MIXER_CTRL,
+ 6, 1, 1),
+
+ SOC_SINGLE("OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 6, 1, 1),
+
+ SOC_SINGLE("High Pass Filter Switch", WM8983_ADC_CONTROL, 8, 1, 0),
+ SOC_ENUM("High Pass Filter Mode", filter_mode),
+ SOC_SINGLE("High Pass Filter Cutoff", WM8983_ADC_CONTROL, 4, 7, 0),
+
+ SOC_DOUBLE_R_TLV("Aux Bypass Volume",
+ WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 6, 7, 0,
+ aux_tlv),
+
+ SOC_DOUBLE_R_TLV("Input PGA Bypass Volume",
+ WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 2, 7, 0,
+ bypass_tlv),
+
+ SOC_ENUM_EXT("Equalizer Function", eqmode, eqmode_get, eqmode_put),
+ SOC_ENUM("EQ1 Cutoff", eq1_cutoff),
+ SOC_SINGLE_TLV("EQ1 Volume", WM8983_EQ1_LOW_SHELF, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ2 Bandwith", eq2_bw),
+ SOC_ENUM("EQ2 Cutoff", eq2_cutoff),
+ SOC_SINGLE_TLV("EQ2 Volume", WM8983_EQ2_PEAK_1, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ3 Bandwith", eq3_bw),
+ SOC_ENUM("EQ3 Cutoff", eq3_cutoff),
+ SOC_SINGLE_TLV("EQ3 Volume", WM8983_EQ3_PEAK_2, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ4 Bandwith", eq4_bw),
+ SOC_ENUM("EQ4 Cutoff", eq4_cutoff),
+ SOC_SINGLE_TLV("EQ4 Volume", WM8983_EQ4_PEAK_3, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ5 Cutoff", eq5_cutoff),
+ SOC_SINGLE_TLV("EQ5 Volume", WM8983_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("3D Depth", depth_3d),
+
+ SOC_ENUM("Speaker Mode", speaker_mode)
+};
+
+static const struct snd_kcontrol_new left_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Switch", WM8983_LEFT_MIXER_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Switch", WM8983_LEFT_MIXER_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Switch", WM8983_LEFT_MIXER_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Switch", WM8983_RIGHT_MIXER_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Switch", WM8983_RIGHT_MIXER_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Switch", WM8983_RIGHT_MIXER_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_input_mixer[] = {
+ SOC_DAPM_SINGLE("L2 Switch", WM8983_INPUT_CTRL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_input_mixer[] = {
+ SOC_DAPM_SINGLE("R2 Switch", WM8983_INPUT_CTRL, 6, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_boost_mixer[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Volume", WM8983_LEFT_ADC_BOOST_CTRL,
+ 4, 7, 0, boost_tlv),
+ SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8983_LEFT_ADC_BOOST_CTRL,
+ 0, 7, 0, boost_tlv)
+};
+
+static const struct snd_kcontrol_new out3_mixer[] = {
+ SOC_DAPM_SINGLE("LMIX2OUT3 Switch", WM8983_OUT3_MIXER_CTRL,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("LDAC2OUT3 Switch", WM8983_OUT3_MIXER_CTRL,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new out4_mixer[] = {
+ SOC_DAPM_SINGLE("LMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("RMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("LDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 3, 1, 0),
+ SOC_DAPM_SINGLE("RDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_boost_mixer[] = {
+ SOC_DAPM_SINGLE_TLV("R2 Volume", WM8983_RIGHT_ADC_BOOST_CTRL,
+ 4, 7, 0, boost_tlv),
+ SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8983_RIGHT_ADC_BOOST_CTRL,
+ 0, 7, 0, boost_tlv)
+};
+
+static const struct snd_soc_dapm_widget wm8983_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8983_POWER_MANAGEMENT_3,
+ 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8983_POWER_MANAGEMENT_3,
+ 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8983_POWER_MANAGEMENT_2,
+ 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8983_POWER_MANAGEMENT_2,
+ 1, 0),
+
+ SND_SOC_DAPM_MIXER("Left Output Mixer", WM8983_POWER_MANAGEMENT_3,
+ 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8983_POWER_MANAGEMENT_3,
+ 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Input Mixer", WM8983_POWER_MANAGEMENT_2,
+ 2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", WM8983_POWER_MANAGEMENT_2,
+ 3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8983_POWER_MANAGEMENT_2,
+ 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
+ SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8983_POWER_MANAGEMENT_2,
+ 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
+
+ SND_SOC_DAPM_MIXER("OUT3 Mixer", WM8983_POWER_MANAGEMENT_1,
+ 6, 0, out3_mixer, ARRAY_SIZE(out3_mixer)),
+
+ SND_SOC_DAPM_MIXER("OUT4 Mixer", WM8983_POWER_MANAGEMENT_1,
+ 7, 0, out4_mixer, ARRAY_SIZE(out4_mixer)),
+
+ SND_SOC_DAPM_PGA("Left Capture PGA", WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Capture PGA", WM8983_RIGHT_INP_PGA_GAIN_CTRL,
+ 6, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Headphone Out", WM8983_POWER_MANAGEMENT_2,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Out", WM8983_POWER_MANAGEMENT_2,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Speaker Out", WM8983_POWER_MANAGEMENT_3,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Speaker Out", WM8983_POWER_MANAGEMENT_3,
+ 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("OUT3 Out", WM8983_POWER_MANAGEMENT_3,
+ 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("OUT4 Out", WM8983_POWER_MANAGEMENT_3,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8983_POWER_MANAGEMENT_1, 4, 0),
+
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_INPUT("LIP"),
+ SND_SOC_DAPM_INPUT("RIN"),
+ SND_SOC_DAPM_INPUT("RIP"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+ SND_SOC_DAPM_INPUT("L2"),
+ SND_SOC_DAPM_INPUT("R2"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4")
+};
+
+static const struct snd_soc_dapm_route wm8983_audio_map[] = {
+ { "OUT3 Mixer", "LMIX2OUT3 Switch", "Left Output Mixer" },
+ { "OUT3 Mixer", "LDAC2OUT3 Switch", "Left DAC" },
+
+ { "OUT3 Out", NULL, "OUT3 Mixer" },
+ { "OUT3", NULL, "OUT3 Out" },
+
+ { "OUT4 Mixer", "LMIX2OUT4 Switch", "Left Output Mixer" },
+ { "OUT4 Mixer", "RMIX2OUT4 Switch", "Right Output Mixer" },
+ { "OUT4 Mixer", "LDAC2OUT4 Switch", "Left DAC" },
+ { "OUT4 Mixer", "RDAC2OUT4 Switch", "Right DAC" },
+
+ { "OUT4 Out", NULL, "OUT4 Mixer" },
+ { "OUT4", NULL, "OUT4 Out" },
+
+ { "Right Output Mixer", "PCM Switch", "Right DAC" },
+ { "Right Output Mixer", "Aux Switch", "AUXR" },
+ { "Right Output Mixer", "Line Switch", "Right Boost Mixer" },
+
+ { "Left Output Mixer", "PCM Switch", "Left DAC" },
+ { "Left Output Mixer", "Aux Switch", "AUXL" },
+ { "Left Output Mixer", "Line Switch", "Left Boost Mixer" },
+
+ { "Right Headphone Out", NULL, "Right Output Mixer" },
+ { "HPR", NULL, "Right Headphone Out" },
+
+ { "Left Headphone Out", NULL, "Left Output Mixer" },
+ { "HPL", NULL, "Left Headphone Out" },
+
+ { "Right Speaker Out", NULL, "Right Output Mixer" },
+ { "SPKR", NULL, "Right Speaker Out" },
+
+ { "Left Speaker Out", NULL, "Left Output Mixer" },
+ { "SPKL", NULL, "Left Speaker Out" },
+
+ { "Right ADC", NULL, "Right Boost Mixer" },
+
+ { "Right Boost Mixer", "AUXR Volume", "AUXR" },
+ { "Right Boost Mixer", NULL, "Right Capture PGA" },
+ { "Right Boost Mixer", "R2 Volume", "R2" },
+
+ { "Left ADC", NULL, "Left Boost Mixer" },
+
+ { "Left Boost Mixer", "AUXL Volume", "AUXL" },
+ { "Left Boost Mixer", NULL, "Left Capture PGA" },
+ { "Left Boost Mixer", "L2 Volume", "L2" },
+
+ { "Right Capture PGA", NULL, "Right Input Mixer" },
+ { "Left Capture PGA", NULL, "Left Input Mixer" },
+
+ { "Right Input Mixer", "R2 Switch", "R2" },
+ { "Right Input Mixer", "MicN Switch", "RIN" },
+ { "Right Input Mixer", "MicP Switch", "RIP" },
+
+ { "Left Input Mixer", "L2 Switch", "L2" },
+ { "Left Input Mixer", "MicN Switch", "LIN" },
+ { "Left Input Mixer", "MicP Switch", "LIP" },
+};
+
+static int eqmode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF);
+ if (reg & WM8983_EQ3DMODE)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int eqmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int regpwr2, regpwr3;
+ unsigned int reg_eq;
+
+ if (ucontrol->value.integer.value[0] != 0
+ && ucontrol->value.integer.value[0] != 1)
+ return -EINVAL;
+
+ reg_eq = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF);
+ switch ((reg_eq & WM8983_EQ3DMODE) >> WM8983_EQ3DMODE_SHIFT) {
+ case 0:
+ if (!ucontrol->value.integer.value[0])
+ return 0;
+ break;
+ case 1:
+ if (ucontrol->value.integer.value[0])
+ return 0;
+ break;
+ }
+
+ regpwr2 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_2);
+ regpwr3 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_3);
+ /* disable the DACs and ADCs */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_2,
+ WM8983_ADCENR_MASK | WM8983_ADCENL_MASK, 0);
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_3,
+ WM8983_DACENR_MASK | WM8983_DACENL_MASK, 0);
+ /* set the desired eqmode */
+ snd_soc_update_bits(codec, WM8983_EQ1_LOW_SHELF,
+ WM8983_EQ3DMODE_MASK,
+ ucontrol->value.integer.value[0]
+ << WM8983_EQ3DMODE_SHIFT);
+ /* restore DAC/ADC configuration */
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_2, regpwr2);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_3, regpwr3);
+ return 0;
+}
+
+static int wm8983_readable(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if (reg > WM8983_MAX_REGISTER)
+ return 0;
+
+ return wm8983_access_masks[reg].read != 0;
+}
+
+static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, WM8983_DAC_CONTROL,
+ WM8983_SOFTMUTE_MASK,
+ !!mute << WM8983_SOFTMUTE_SHIFT);
+}
+
+static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 format, master, bcp, lrp;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = 0x1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ format = 0x3;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_FMT_MASK, format << WM8983_FMT_SHIFT);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_MS_MASK, master << WM8983_MS_SHIFT);
+
+ /* FIXME: We don't currently support DSP A/B modes */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ dev_err(dai->dev, "DSP A/B modes are not supported\n");
+ return -EINVAL;
+ default:
+ break;
+ }
+
+ bcp = lrp = 0;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bcp = lrp = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bcp = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrp = 1;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown polarity configuration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_LRCP_MASK, lrp << WM8983_LRCP_SHIFT);
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_BCP_MASK, bcp << WM8983_BCP_SHIFT);
+ return 0;
+}
+
+static int wm8983_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int i;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+ u16 blen, srate_idx;
+ u32 tmp;
+ int srate_best;
+ int ret;
+
+ ret = snd_soc_params_to_bclk(params);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to convert params to bclk: %d\n", ret);
+ return ret;
+ }
+
+ wm8983->bclk = ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ blen = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ blen = 0x1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ blen = 0x2;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ blen = 0x3;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported word length %u\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_WL_MASK, blen << WM8983_WL_SHIFT);
+
+ /*
+ * match to the nearest possible sample rate and rely
+ * on the array index to configure the SR register
+ */
+ srate_idx = 0;
+ srate_best = abs(srates[0] - params_rate(params));
+ for (i = 1; i < ARRAY_SIZE(srates); ++i) {
+ if (abs(srates[i] - params_rate(params)) >= srate_best)
+ continue;
+ srate_idx = i;
+ srate_best = abs(srates[i] - params_rate(params));
+ }
+
+ dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]);
+ snd_soc_update_bits(codec, WM8983_ADDITIONAL_CONTROL,
+ WM8983_SR_MASK, srate_idx << WM8983_SR_SHIFT);
+
+ dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8983->bclk);
+ dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8983->sysclk);
+
+ for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) {
+ if (wm8983->sysclk / params_rate(params)
+ == fs_ratios[i].ratio)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(fs_ratios)) {
+ dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n",
+ wm8983->sysclk, params_rate(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio);
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_MCLKDIV_MASK, i << WM8983_MCLKDIV_SHIFT);
+
+ /* select the appropriate bclk divider */
+ tmp = (wm8983->sysclk / fs_ratios[i].div) * 10;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) {
+ if (wm8983->bclk == tmp / bclk_divs[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(bclk_divs)) {
+ dev_err(dai->dev, "No matching BCLK divider found\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "BCLK div = %d\n", i);
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_BCLKDIV_MASK, i << WM8983_BCLKDIV_SHIFT);
+
+ return 0;
+}
+
+struct pll_div {
+ u32 div2:1;
+ u32 n:4;
+ u32 k:24;
+};
+
+#define FIXED_PLL_SIZE ((1ULL << 24) * 10)
+static int pll_factors(struct pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned long int K, Ndiv, Nmod;
+
+ pll_div->div2 = 0;
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ }
+
+ if (Ndiv < 6 || Ndiv > 12) {
+ printk(KERN_ERR "%s: WM8983 N value is not within"
+ " the recommended range: %lu\n", __func__, Ndiv);
+ return -EINVAL;
+ }
+ pll_div->n = Ndiv;
+
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (u64)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xffffffff;
+ if ((K % 10) >= 5)
+ K += 5;
+ K /= 10;
+ pll_div->k = K;
+ return 0;
+}
+
+static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ int ret;
+ struct snd_soc_codec *codec;
+ struct pll_div pll_div;
+
+ codec = dai->codec;
+ if (freq_in && freq_out) {
+ ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
+ if (ret)
+ return ret;
+ }
+
+ /* disable the PLL before re-programming it */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_PLLEN_MASK, 0);
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ /* set PLLN and PRESCALE */
+ snd_soc_write(codec, WM8983_PLL_N,
+ (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT)
+ | pll_div.n);
+ /* set PLLK */
+ snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18));
+ /* enable the PLL */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_PLLEN_MASK, WM8983_PLLEN);
+ return 0;
+}
+
+static int wm8983_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM8983_CLKSRC_MCLK:
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_CLKSEL_MASK, 0);
+ break;
+ case WM8983_CLKSRC_PLL:
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_CLKSEL_MASK, WM8983_CLKSEL);
+ break;
+ default:
+ dev_err(dai->dev, "Unknown clock source: %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ wm8983->sysclk = freq;
+ return 0;
+}
+
+static int wm8983_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID at 100k */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK,
+ 1 << WM8983_VMIDSEL_SHIFT);
+ 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;
+ }
+ /* enable anti-pop features */
+ snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC,
+ WM8983_POBCTRL_MASK | WM8983_DELEN_MASK,
+ WM8983_POBCTRL | WM8983_DELEN);
+ /* enable thermal shutdown */
+ snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL,
+ WM8983_TSDEN_MASK, WM8983_TSDEN);
+ /* enable BIASEN */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_BIASEN_MASK, WM8983_BIASEN);
+ /* VMID at 100k */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK,
+ 1 << WM8983_VMIDSEL_SHIFT);
+ msleep(250);
+ /* disable anti-pop features */
+ snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC,
+ WM8983_POBCTRL_MASK |
+ WM8983_DELEN_MASK, 0);
+ }
+
+ /* VMID at 500k */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK,
+ 2 << WM8983_VMIDSEL_SHIFT);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* disable thermal shutdown */
+ snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL,
+ WM8983_TSDEN_MASK, 0);
+ /* disable VMIDSEL and BIASEN */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK | WM8983_BIASEN_MASK,
+ 0);
+ /* wait for VMID to discharge */
+ msleep(100);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_1, 0);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_2, 0);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_3, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8983_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8983_resume(struct snd_soc_codec *codec)
+{
+ wm8983_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+#else
+#define wm8983_suspend NULL
+#define wm8983_resume NULL
+#endif
+
+static int wm8983_remove(struct snd_soc_codec *codec)
+{
+ wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8983_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8983->control_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_write(codec, WM8983_SOFTWARE_RESET, 0x8983);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ /* set the vol/gain update bits */
+ for (i = 0; i < ARRAY_SIZE(vol_update_regs); ++i)
+ snd_soc_update_bits(codec, vol_update_regs[i],
+ 0x100, 0x100);
+
+ /* mute all outputs and set PGAs to minimum gain */
+ for (i = WM8983_LOUT1_HP_VOLUME_CTRL;
+ i <= WM8983_OUT4_MONO_MIX_CTRL; ++i)
+ snd_soc_update_bits(codec, i, 0x40, 0x40);
+
+ /* enable soft mute */
+ snd_soc_update_bits(codec, WM8983_DAC_CONTROL,
+ WM8983_SOFTMUTE_MASK,
+ WM8983_SOFTMUTE);
+
+ /* enable BIASCUT */
+ snd_soc_update_bits(codec, WM8983_BIAS_CTRL,
+ WM8983_BIASCUT, WM8983_BIASCUT);
+ return 0;
+}
+
+static struct snd_soc_dai_ops wm8983_dai_ops = {
+ .digital_mute = wm8983_dac_mute,
+ .hw_params = wm8983_hw_params,
+ .set_fmt = wm8983_set_fmt,
+ .set_sysclk = wm8983_set_sysclk,
+ .set_pll = wm8983_set_pll
+};
+
+#define WM8983_FORMATS (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_driver wm8983_dai = {
+ .name = "wm8983-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8983_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8983_FORMATS,
+ },
+ .ops = &wm8983_dai_ops,
+ .symmetric_rates = 1
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8983 = {
+ .probe = wm8983_probe,
+ .remove = wm8983_remove,
+ .suspend = wm8983_suspend,
+ .resume = wm8983_resume,
+ .set_bias_level = wm8983_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(wm8983_reg_defs),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = wm8983_reg_defs,
+ .controls = wm8983_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8983_snd_controls),
+ .dapm_widgets = wm8983_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets),
+ .dapm_routes = wm8983_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map),
+ .readable_register = wm8983_readable
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8983_spi_probe(struct spi_device *spi)
+{
+ struct wm8983_priv *wm8983;
+ int ret;
+
+ wm8983 = kzalloc(sizeof *wm8983, GFP_KERNEL);
+ if (!wm8983)
+ return -ENOMEM;
+
+ wm8983->control_type = SND_SOC_SPI;
+ spi_set_drvdata(spi, wm8983);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8983, &wm8983_dai, 1);
+ if (ret < 0)
+ kfree(wm8983);
+ return ret;
+}
+
+static int __devexit wm8983_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ kfree(spi_get_drvdata(spi));
+ return 0;
+}
+
+static struct spi_driver wm8983_spi_driver = {
+ .driver = {
+ .name = "wm8983",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8983_spi_probe,
+ .remove = __devexit_p(wm8983_spi_remove)
+};
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8983_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8983_priv *wm8983;
+ int ret;
+
+ wm8983 = kzalloc(sizeof *wm8983, GFP_KERNEL);
+ if (!wm8983)
+ return -ENOMEM;
+
+ wm8983->control_type = SND_SOC_I2C;
+ i2c_set_clientdata(i2c, wm8983);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8983, &wm8983_dai, 1);
+ if (ret < 0)
+ kfree(wm8983);
+ return ret;
+}
+
+static __devexit int wm8983_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 wm8983_i2c_id[] = {
+ { "wm8983", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id);
+
+static struct i2c_driver wm8983_i2c_driver = {
+ .driver = {
+ .name = "wm8983",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8983_i2c_probe,
+ .remove = __devexit_p(wm8983_i2c_remove),
+ .id_table = wm8983_i2c_id
+};
+#endif
+
+static int __init wm8983_modinit(void)
+{
+ int ret = 0;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8983_i2c_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register wm8983 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8983_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8983 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8983_modinit);
+
+static void __exit wm8983_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8983_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8983_spi_driver);
+#endif
+}
+module_exit(wm8983_exit);
+
+MODULE_DESCRIPTION("ASoC WM8983 driver");
+MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8983.h b/sound/soc/codecs/wm8983.h
new file mode 100644
index 0000000..71ee619
--- /dev/null
+++ b/sound/soc/codecs/wm8983.h
@@ -0,0 +1,1029 @@
+/*
+ * wm8983.h -- WM8983 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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.
+ */
+
+#ifndef _WM8983_H
+#define _WM8983_H
+
+/*
+ * Register values.
+ */
+#define WM8983_SOFTWARE_RESET 0x00
+#define WM8983_POWER_MANAGEMENT_1 0x01
+#define WM8983_POWER_MANAGEMENT_2 0x02
+#define WM8983_POWER_MANAGEMENT_3 0x03
+#define WM8983_AUDIO_INTERFACE 0x04
+#define WM8983_COMPANDING_CONTROL 0x05
+#define WM8983_CLOCK_GEN_CONTROL 0x06
+#define WM8983_ADDITIONAL_CONTROL 0x07
+#define WM8983_GPIO_CONTROL 0x08
+#define WM8983_JACK_DETECT_CONTROL_1 0x09
+#define WM8983_DAC_CONTROL 0x0A
+#define WM8983_LEFT_DAC_DIGITAL_VOL 0x0B
+#define WM8983_RIGHT_DAC_DIGITAL_VOL 0x0C
+#define WM8983_JACK_DETECT_CONTROL_2 0x0D
+#define WM8983_ADC_CONTROL 0x0E
+#define WM8983_LEFT_ADC_DIGITAL_VOL 0x0F
+#define WM8983_RIGHT_ADC_DIGITAL_VOL 0x10
+#define WM8983_EQ1_LOW_SHELF 0x12
+#define WM8983_EQ2_PEAK_1 0x13
+#define WM8983_EQ3_PEAK_2 0x14
+#define WM8983_EQ4_PEAK_3 0x15
+#define WM8983_EQ5_HIGH_SHELF 0x16
+#define WM8983_DAC_LIMITER_1 0x18
+#define WM8983_DAC_LIMITER_2 0x19
+#define WM8983_NOTCH_FILTER_1 0x1B
+#define WM8983_NOTCH_FILTER_2 0x1C
+#define WM8983_NOTCH_FILTER_3 0x1D
+#define WM8983_NOTCH_FILTER_4 0x1E
+#define WM8983_ALC_CONTROL_1 0x20
+#define WM8983_ALC_CONTROL_2 0x21
+#define WM8983_ALC_CONTROL_3 0x22
+#define WM8983_NOISE_GATE 0x23
+#define WM8983_PLL_N 0x24
+#define WM8983_PLL_K_1 0x25
+#define WM8983_PLL_K_2 0x26
+#define WM8983_PLL_K_3 0x27
+#define WM8983_3D_CONTROL 0x29
+#define WM8983_OUT4_TO_ADC 0x2A
+#define WM8983_BEEP_CONTROL 0x2B
+#define WM8983_INPUT_CTRL 0x2C
+#define WM8983_LEFT_INP_PGA_GAIN_CTRL 0x2D
+#define WM8983_RIGHT_INP_PGA_GAIN_CTRL 0x2E
+#define WM8983_LEFT_ADC_BOOST_CTRL 0x2F
+#define WM8983_RIGHT_ADC_BOOST_CTRL 0x30
+#define WM8983_OUTPUT_CTRL 0x31
+#define WM8983_LEFT_MIXER_CTRL 0x32
+#define WM8983_RIGHT_MIXER_CTRL 0x33
+#define WM8983_LOUT1_HP_VOLUME_CTRL 0x34
+#define WM8983_ROUT1_HP_VOLUME_CTRL 0x35
+#define WM8983_LOUT2_SPK_VOLUME_CTRL 0x36
+#define WM8983_ROUT2_SPK_VOLUME_CTRL 0x37
+#define WM8983_OUT3_MIXER_CTRL 0x38
+#define WM8983_OUT4_MONO_MIX_CTRL 0x39
+#define WM8983_BIAS_CTRL 0x3D
+
+#define WM8983_REGISTER_COUNT 59
+#define WM8983_MAX_REGISTER 0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8983_SOFTWARE_RESET_MASK 0x01FF /* SOFTWARE_RESET - [8:0] */
+#define WM8983_SOFTWARE_RESET_SHIFT 0 /* SOFTWARE_RESET - [8:0] */
+#define WM8983_SOFTWARE_RESET_WIDTH 9 /* SOFTWARE_RESET - [8:0] */
+
+/*
+ * R1 (0x01) - Power management 1
+ */
+#define WM8983_BUFDCOPEN 0x0100 /* BUFDCOPEN */
+#define WM8983_BUFDCOPEN_MASK 0x0100 /* BUFDCOPEN */
+#define WM8983_BUFDCOPEN_SHIFT 8 /* BUFDCOPEN */
+#define WM8983_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */
+#define WM8983_OUT4MIXEN 0x0080 /* OUT4MIXEN */
+#define WM8983_OUT4MIXEN_MASK 0x0080 /* OUT4MIXEN */
+#define WM8983_OUT4MIXEN_SHIFT 7 /* OUT4MIXEN */
+#define WM8983_OUT4MIXEN_WIDTH 1 /* OUT4MIXEN */
+#define WM8983_OUT3MIXEN 0x0040 /* OUT3MIXEN */
+#define WM8983_OUT3MIXEN_MASK 0x0040 /* OUT3MIXEN */
+#define WM8983_OUT3MIXEN_SHIFT 6 /* OUT3MIXEN */
+#define WM8983_OUT3MIXEN_WIDTH 1 /* OUT3MIXEN */
+#define WM8983_PLLEN 0x0020 /* PLLEN */
+#define WM8983_PLLEN_MASK 0x0020 /* PLLEN */
+#define WM8983_PLLEN_SHIFT 5 /* PLLEN */
+#define WM8983_PLLEN_WIDTH 1 /* PLLEN */
+#define WM8983_MICBEN 0x0010 /* MICBEN */
+#define WM8983_MICBEN_MASK 0x0010 /* MICBEN */
+#define WM8983_MICBEN_SHIFT 4 /* MICBEN */
+#define WM8983_MICBEN_WIDTH 1 /* MICBEN */
+#define WM8983_BIASEN 0x0008 /* BIASEN */
+#define WM8983_BIASEN_MASK 0x0008 /* BIASEN */
+#define WM8983_BIASEN_SHIFT 3 /* BIASEN */
+#define WM8983_BIASEN_WIDTH 1 /* BIASEN */
+#define WM8983_BUFIOEN 0x0004 /* BUFIOEN */
+#define WM8983_BUFIOEN_MASK 0x0004 /* BUFIOEN */
+#define WM8983_BUFIOEN_SHIFT 2 /* BUFIOEN */
+#define WM8983_BUFIOEN_WIDTH 1 /* BUFIOEN */
+#define WM8983_VMIDSEL_MASK 0x0003 /* VMIDSEL - [1:0] */
+#define WM8983_VMIDSEL_SHIFT 0 /* VMIDSEL - [1:0] */
+#define WM8983_VMIDSEL_WIDTH 2 /* VMIDSEL - [1:0] */
+
+/*
+ * R2 (0x02) - Power management 2
+ */
+#define WM8983_ROUT1EN 0x0100 /* ROUT1EN */
+#define WM8983_ROUT1EN_MASK 0x0100 /* ROUT1EN */
+#define WM8983_ROUT1EN_SHIFT 8 /* ROUT1EN */
+#define WM8983_ROUT1EN_WIDTH 1 /* ROUT1EN */
+#define WM8983_LOUT1EN 0x0080 /* LOUT1EN */
+#define WM8983_LOUT1EN_MASK 0x0080 /* LOUT1EN */
+#define WM8983_LOUT1EN_SHIFT 7 /* LOUT1EN */
+#define WM8983_LOUT1EN_WIDTH 1 /* LOUT1EN */
+#define WM8983_SLEEP 0x0040 /* SLEEP */
+#define WM8983_SLEEP_MASK 0x0040 /* SLEEP */
+#define WM8983_SLEEP_SHIFT 6 /* SLEEP */
+#define WM8983_SLEEP_WIDTH 1 /* SLEEP */
+#define WM8983_BOOSTENR 0x0020 /* BOOSTENR */
+#define WM8983_BOOSTENR_MASK 0x0020 /* BOOSTENR */
+#define WM8983_BOOSTENR_SHIFT 5 /* BOOSTENR */
+#define WM8983_BOOSTENR_WIDTH 1 /* BOOSTENR */
+#define WM8983_BOOSTENL 0x0010 /* BOOSTENL */
+#define WM8983_BOOSTENL_MASK 0x0010 /* BOOSTENL */
+#define WM8983_BOOSTENL_SHIFT 4 /* BOOSTENL */
+#define WM8983_BOOSTENL_WIDTH 1 /* BOOSTENL */
+#define WM8983_INPGAENR 0x0008 /* INPGAENR */
+#define WM8983_INPGAENR_MASK 0x0008 /* INPGAENR */
+#define WM8983_INPGAENR_SHIFT 3 /* INPGAENR */
+#define WM8983_INPGAENR_WIDTH 1 /* INPGAENR */
+#define WM8983_INPPGAENL 0x0004 /* INPPGAENL */
+#define WM8983_INPPGAENL_MASK 0x0004 /* INPPGAENL */
+#define WM8983_INPPGAENL_SHIFT 2 /* INPPGAENL */
+#define WM8983_INPPGAENL_WIDTH 1 /* INPPGAENL */
+#define WM8983_ADCENR 0x0002 /* ADCENR */
+#define WM8983_ADCENR_MASK 0x0002 /* ADCENR */
+#define WM8983_ADCENR_SHIFT 1 /* ADCENR */
+#define WM8983_ADCENR_WIDTH 1 /* ADCENR */
+#define WM8983_ADCENL 0x0001 /* ADCENL */
+#define WM8983_ADCENL_MASK 0x0001 /* ADCENL */
+#define WM8983_ADCENL_SHIFT 0 /* ADCENL */
+#define WM8983_ADCENL_WIDTH 1 /* ADCENL */
+
+/*
+ * R3 (0x03) - Power management 3
+ */
+#define WM8983_OUT4EN 0x0100 /* OUT4EN */
+#define WM8983_OUT4EN_MASK 0x0100 /* OUT4EN */
+#define WM8983_OUT4EN_SHIFT 8 /* OUT4EN */
+#define WM8983_OUT4EN_WIDTH 1 /* OUT4EN */
+#define WM8983_OUT3EN 0x0080 /* OUT3EN */
+#define WM8983_OUT3EN_MASK 0x0080 /* OUT3EN */
+#define WM8983_OUT3EN_SHIFT 7 /* OUT3EN */
+#define WM8983_OUT3EN_WIDTH 1 /* OUT3EN */
+#define WM8983_LOUT2EN 0x0040 /* LOUT2EN */
+#define WM8983_LOUT2EN_MASK 0x0040 /* LOUT2EN */
+#define WM8983_LOUT2EN_SHIFT 6 /* LOUT2EN */
+#define WM8983_LOUT2EN_WIDTH 1 /* LOUT2EN */
+#define WM8983_ROUT2EN 0x0020 /* ROUT2EN */
+#define WM8983_ROUT2EN_MASK 0x0020 /* ROUT2EN */
+#define WM8983_ROUT2EN_SHIFT 5 /* ROUT2EN */
+#define WM8983_ROUT2EN_WIDTH 1 /* ROUT2EN */
+#define WM8983_RMIXEN 0x0008 /* RMIXEN */
+#define WM8983_RMIXEN_MASK 0x0008 /* RMIXEN */
+#define WM8983_RMIXEN_SHIFT 3 /* RMIXEN */
+#define WM8983_RMIXEN_WIDTH 1 /* RMIXEN */
+#define WM8983_LMIXEN 0x0004 /* LMIXEN */
+#define WM8983_LMIXEN_MASK 0x0004 /* LMIXEN */
+#define WM8983_LMIXEN_SHIFT 2 /* LMIXEN */
+#define WM8983_LMIXEN_WIDTH 1 /* LMIXEN */
+#define WM8983_DACENR 0x0002 /* DACENR */
+#define WM8983_DACENR_MASK 0x0002 /* DACENR */
+#define WM8983_DACENR_SHIFT 1 /* DACENR */
+#define WM8983_DACENR_WIDTH 1 /* DACENR */
+#define WM8983_DACENL 0x0001 /* DACENL */
+#define WM8983_DACENL_MASK 0x0001 /* DACENL */
+#define WM8983_DACENL_SHIFT 0 /* DACENL */
+#define WM8983_DACENL_WIDTH 1 /* DACENL */
+
+/*
+ * R4 (0x04) - Audio Interface
+ */
+#define WM8983_BCP 0x0100 /* BCP */
+#define WM8983_BCP_MASK 0x0100 /* BCP */
+#define WM8983_BCP_SHIFT 8 /* BCP */
+#define WM8983_BCP_WIDTH 1 /* BCP */
+#define WM8983_LRCP 0x0080 /* LRCP */
+#define WM8983_LRCP_MASK 0x0080 /* LRCP */
+#define WM8983_LRCP_SHIFT 7 /* LRCP */
+#define WM8983_LRCP_WIDTH 1 /* LRCP */
+#define WM8983_WL_MASK 0x0060 /* WL - [6:5] */
+#define WM8983_WL_SHIFT 5 /* WL - [6:5] */
+#define WM8983_WL_WIDTH 2 /* WL - [6:5] */
+#define WM8983_FMT_MASK 0x0018 /* FMT - [4:3] */
+#define WM8983_FMT_SHIFT 3 /* FMT - [4:3] */
+#define WM8983_FMT_WIDTH 2 /* FMT - [4:3] */
+#define WM8983_DLRSWAP 0x0004 /* DLRSWAP */
+#define WM8983_DLRSWAP_MASK 0x0004 /* DLRSWAP */
+#define WM8983_DLRSWAP_SHIFT 2 /* DLRSWAP */
+#define WM8983_DLRSWAP_WIDTH 1 /* DLRSWAP */
+#define WM8983_ALRSWAP 0x0002 /* ALRSWAP */
+#define WM8983_ALRSWAP_MASK 0x0002 /* ALRSWAP */
+#define WM8983_ALRSWAP_SHIFT 1 /* ALRSWAP */
+#define WM8983_ALRSWAP_WIDTH 1 /* ALRSWAP */
+#define WM8983_MONO 0x0001 /* MONO */
+#define WM8983_MONO_MASK 0x0001 /* MONO */
+#define WM8983_MONO_SHIFT 0 /* MONO */
+#define WM8983_MONO_WIDTH 1 /* MONO */
+
+/*
+ * R5 (0x05) - Companding control
+ */
+#define WM8983_WL8 0x0020 /* WL8 */
+#define WM8983_WL8_MASK 0x0020 /* WL8 */
+#define WM8983_WL8_SHIFT 5 /* WL8 */
+#define WM8983_WL8_WIDTH 1 /* WL8 */
+#define WM8983_DAC_COMP_MASK 0x0018 /* DAC_COMP - [4:3] */
+#define WM8983_DAC_COMP_SHIFT 3 /* DAC_COMP - [4:3] */
+#define WM8983_DAC_COMP_WIDTH 2 /* DAC_COMP - [4:3] */
+#define WM8983_ADC_COMP_MASK 0x0006 /* ADC_COMP - [2:1] */
+#define WM8983_ADC_COMP_SHIFT 1 /* ADC_COMP - [2:1] */
+#define WM8983_ADC_COMP_WIDTH 2 /* ADC_COMP - [2:1] */
+#define WM8983_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8983_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8983_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8983_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clock Gen control
+ */
+#define WM8983_CLKSEL 0x0100 /* CLKSEL */
+#define WM8983_CLKSEL_MASK 0x0100 /* CLKSEL */
+#define WM8983_CLKSEL_SHIFT 8 /* CLKSEL */
+#define WM8983_CLKSEL_WIDTH 1 /* CLKSEL */
+#define WM8983_MCLKDIV_MASK 0x00E0 /* MCLKDIV - [7:5] */
+#define WM8983_MCLKDIV_SHIFT 5 /* MCLKDIV - [7:5] */
+#define WM8983_MCLKDIV_WIDTH 3 /* MCLKDIV - [7:5] */
+#define WM8983_BCLKDIV_MASK 0x001C /* BCLKDIV - [4:2] */
+#define WM8983_BCLKDIV_SHIFT 2 /* BCLKDIV - [4:2] */
+#define WM8983_BCLKDIV_WIDTH 3 /* BCLKDIV - [4:2] */
+#define WM8983_MS 0x0001 /* MS */
+#define WM8983_MS_MASK 0x0001 /* MS */
+#define WM8983_MS_SHIFT 0 /* MS */
+#define WM8983_MS_WIDTH 1 /* MS */
+
+/*
+ * R7 (0x07) - Additional control
+ */
+#define WM8983_SR_MASK 0x000E /* SR - [3:1] */
+#define WM8983_SR_SHIFT 1 /* SR - [3:1] */
+#define WM8983_SR_WIDTH 3 /* SR - [3:1] */
+#define WM8983_SLOWCLKEN 0x0001 /* SLOWCLKEN */
+#define WM8983_SLOWCLKEN_MASK 0x0001 /* SLOWCLKEN */
+#define WM8983_SLOWCLKEN_SHIFT 0 /* SLOWCLKEN */
+#define WM8983_SLOWCLKEN_WIDTH 1 /* SLOWCLKEN */
+
+/*
+ * R8 (0x08) - GPIO Control
+ */
+#define WM8983_OPCLKDIV_MASK 0x0030 /* OPCLKDIV - [5:4] */
+#define WM8983_OPCLKDIV_SHIFT 4 /* OPCLKDIV - [5:4] */
+#define WM8983_OPCLKDIV_WIDTH 2 /* OPCLKDIV - [5:4] */
+#define WM8983_GPIO1POL 0x0008 /* GPIO1POL */
+#define WM8983_GPIO1POL_MASK 0x0008 /* GPIO1POL */
+#define WM8983_GPIO1POL_SHIFT 3 /* GPIO1POL */
+#define WM8983_GPIO1POL_WIDTH 1 /* GPIO1POL */
+#define WM8983_GPIO1SEL_MASK 0x0007 /* GPIO1SEL - [2:0] */
+#define WM8983_GPIO1SEL_SHIFT 0 /* GPIO1SEL - [2:0] */
+#define WM8983_GPIO1SEL_WIDTH 3 /* GPIO1SEL - [2:0] */
+
+/*
+ * R9 (0x09) - Jack Detect Control 1
+ */
+#define WM8983_JD_VMID1 0x0100 /* JD_VMID1 */
+#define WM8983_JD_VMID1_MASK 0x0100 /* JD_VMID1 */
+#define WM8983_JD_VMID1_SHIFT 8 /* JD_VMID1 */
+#define WM8983_JD_VMID1_WIDTH 1 /* JD_VMID1 */
+#define WM8983_JD_VMID0 0x0080 /* JD_VMID0 */
+#define WM8983_JD_VMID0_MASK 0x0080 /* JD_VMID0 */
+#define WM8983_JD_VMID0_SHIFT 7 /* JD_VMID0 */
+#define WM8983_JD_VMID0_WIDTH 1 /* JD_VMID0 */
+#define WM8983_JD_EN 0x0040 /* JD_EN */
+#define WM8983_JD_EN_MASK 0x0040 /* JD_EN */
+#define WM8983_JD_EN_SHIFT 6 /* JD_EN */
+#define WM8983_JD_EN_WIDTH 1 /* JD_EN */
+#define WM8983_JD_SEL_MASK 0x0030 /* JD_SEL - [5:4] */
+#define WM8983_JD_SEL_SHIFT 4 /* JD_SEL - [5:4] */
+#define WM8983_JD_SEL_WIDTH 2 /* JD_SEL - [5:4] */
+
+/*
+ * R10 (0x0A) - DAC Control
+ */
+#define WM8983_SOFTMUTE 0x0040 /* SOFTMUTE */
+#define WM8983_SOFTMUTE_MASK 0x0040 /* SOFTMUTE */
+#define WM8983_SOFTMUTE_SHIFT 6 /* SOFTMUTE */
+#define WM8983_SOFTMUTE_WIDTH 1 /* SOFTMUTE */
+#define WM8983_DACOSR128 0x0008 /* DACOSR128 */
+#define WM8983_DACOSR128_MASK 0x0008 /* DACOSR128 */
+#define WM8983_DACOSR128_SHIFT 3 /* DACOSR128 */
+#define WM8983_DACOSR128_WIDTH 1 /* DACOSR128 */
+#define WM8983_AMUTE 0x0004 /* AMUTE */
+#define WM8983_AMUTE_MASK 0x0004 /* AMUTE */
+#define WM8983_AMUTE_SHIFT 2 /* AMUTE */
+#define WM8983_AMUTE_WIDTH 1 /* AMUTE */
+#define WM8983_DACRPOL 0x0002 /* DACRPOL */
+#define WM8983_DACRPOL_MASK 0x0002 /* DACRPOL */
+#define WM8983_DACRPOL_SHIFT 1 /* DACRPOL */
+#define WM8983_DACRPOL_WIDTH 1 /* DACRPOL */
+#define WM8983_DACLPOL 0x0001 /* DACLPOL */
+#define WM8983_DACLPOL_MASK 0x0001 /* DACLPOL */
+#define WM8983_DACLPOL_SHIFT 0 /* DACLPOL */
+#define WM8983_DACLPOL_WIDTH 1 /* DACLPOL */
+
+/*
+ * R11 (0x0B) - Left DAC digital Vol
+ */
+#define WM8983_DACVU 0x0100 /* DACVU */
+#define WM8983_DACVU_MASK 0x0100 /* DACVU */
+#define WM8983_DACVU_SHIFT 8 /* DACVU */
+#define WM8983_DACVU_WIDTH 1 /* DACVU */
+#define WM8983_DACLVOL_MASK 0x00FF /* DACLVOL - [7:0] */
+#define WM8983_DACLVOL_SHIFT 0 /* DACLVOL - [7:0] */
+#define WM8983_DACLVOL_WIDTH 8 /* DACLVOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Right DAC digital vol
+ */
+#define WM8983_DACVU 0x0100 /* DACVU */
+#define WM8983_DACVU_MASK 0x0100 /* DACVU */
+#define WM8983_DACVU_SHIFT 8 /* DACVU */
+#define WM8983_DACVU_WIDTH 1 /* DACVU */
+#define WM8983_DACRVOL_MASK 0x00FF /* DACRVOL - [7:0] */
+#define WM8983_DACRVOL_SHIFT 0 /* DACRVOL - [7:0] */
+#define WM8983_DACRVOL_WIDTH 8 /* DACRVOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Jack Detect Control 2
+ */
+#define WM8983_JD_EN1_MASK 0x00F0 /* JD_EN1 - [7:4] */
+#define WM8983_JD_EN1_SHIFT 4 /* JD_EN1 - [7:4] */
+#define WM8983_JD_EN1_WIDTH 4 /* JD_EN1 - [7:4] */
+#define WM8983_JD_EN0_MASK 0x000F /* JD_EN0 - [3:0] */
+#define WM8983_JD_EN0_SHIFT 0 /* JD_EN0 - [3:0] */
+#define WM8983_JD_EN0_WIDTH 4 /* JD_EN0 - [3:0] */
+
+/*
+ * R14 (0x0E) - ADC Control
+ */
+#define WM8983_HPFEN 0x0100 /* HPFEN */
+#define WM8983_HPFEN_MASK 0x0100 /* HPFEN */
+#define WM8983_HPFEN_SHIFT 8 /* HPFEN */
+#define WM8983_HPFEN_WIDTH 1 /* HPFEN */
+#define WM8983_HPFAPP 0x0080 /* HPFAPP */
+#define WM8983_HPFAPP_MASK 0x0080 /* HPFAPP */
+#define WM8983_HPFAPP_SHIFT 7 /* HPFAPP */
+#define WM8983_HPFAPP_WIDTH 1 /* HPFAPP */
+#define WM8983_HPFCUT_MASK 0x0070 /* HPFCUT - [6:4] */
+#define WM8983_HPFCUT_SHIFT 4 /* HPFCUT - [6:4] */
+#define WM8983_HPFCUT_WIDTH 3 /* HPFCUT - [6:4] */
+#define WM8983_ADCOSR128 0x0008 /* ADCOSR128 */
+#define WM8983_ADCOSR128_MASK 0x0008 /* ADCOSR128 */
+#define WM8983_ADCOSR128_SHIFT 3 /* ADCOSR128 */
+#define WM8983_ADCOSR128_WIDTH 1 /* ADCOSR128 */
+#define WM8983_ADCRPOL 0x0002 /* ADCRPOL */
+#define WM8983_ADCRPOL_MASK 0x0002 /* ADCRPOL */
+#define WM8983_ADCRPOL_SHIFT 1 /* ADCRPOL */
+#define WM8983_ADCRPOL_WIDTH 1 /* ADCRPOL */
+#define WM8983_ADCLPOL 0x0001 /* ADCLPOL */
+#define WM8983_ADCLPOL_MASK 0x0001 /* ADCLPOL */
+#define WM8983_ADCLPOL_SHIFT 0 /* ADCLPOL */
+#define WM8983_ADCLPOL_WIDTH 1 /* ADCLPOL */
+
+/*
+ * R15 (0x0F) - Left ADC Digital Vol
+ */
+#define WM8983_ADCVU 0x0100 /* ADCVU */
+#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8983_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8983_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8983_ADCLVOL_MASK 0x00FF /* ADCLVOL - [7:0] */
+#define WM8983_ADCLVOL_SHIFT 0 /* ADCLVOL - [7:0] */
+#define WM8983_ADCLVOL_WIDTH 8 /* ADCLVOL - [7:0] */
+
+/*
+ * R16 (0x10) - Right ADC Digital Vol
+ */
+#define WM8983_ADCVU 0x0100 /* ADCVU */
+#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8983_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8983_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8983_ADCRVOL_MASK 0x00FF /* ADCRVOL - [7:0] */
+#define WM8983_ADCRVOL_SHIFT 0 /* ADCRVOL - [7:0] */
+#define WM8983_ADCRVOL_WIDTH 8 /* ADCRVOL - [7:0] */
+
+/*
+ * R18 (0x12) - EQ1 - low shelf
+ */
+#define WM8983_EQ3DMODE 0x0100 /* EQ3DMODE */
+#define WM8983_EQ3DMODE_MASK 0x0100 /* EQ3DMODE */
+#define WM8983_EQ3DMODE_SHIFT 8 /* EQ3DMODE */
+#define WM8983_EQ3DMODE_WIDTH 1 /* EQ3DMODE */
+#define WM8983_EQ1C_MASK 0x0060 /* EQ1C - [6:5] */
+#define WM8983_EQ1C_SHIFT 5 /* EQ1C - [6:5] */
+#define WM8983_EQ1C_WIDTH 2 /* EQ1C - [6:5] */
+#define WM8983_EQ1G_MASK 0x001F /* EQ1G - [4:0] */
+#define WM8983_EQ1G_SHIFT 0 /* EQ1G - [4:0] */
+#define WM8983_EQ1G_WIDTH 5 /* EQ1G - [4:0] */
+
+/*
+ * R19 (0x13) - EQ2 - peak 1
+ */
+#define WM8983_EQ2BW 0x0100 /* EQ2BW */
+#define WM8983_EQ2BW_MASK 0x0100 /* EQ2BW */
+#define WM8983_EQ2BW_SHIFT 8 /* EQ2BW */
+#define WM8983_EQ2BW_WIDTH 1 /* EQ2BW */
+#define WM8983_EQ2C_MASK 0x0060 /* EQ2C - [6:5] */
+#define WM8983_EQ2C_SHIFT 5 /* EQ2C - [6:5] */
+#define WM8983_EQ2C_WIDTH 2 /* EQ2C - [6:5] */
+#define WM8983_EQ2G_MASK 0x001F /* EQ2G - [4:0] */
+#define WM8983_EQ2G_SHIFT 0 /* EQ2G - [4:0] */
+#define WM8983_EQ2G_WIDTH 5 /* EQ2G - [4:0] */
+
+/*
+ * R20 (0x14) - EQ3 - peak 2
+ */
+#define WM8983_EQ3BW 0x0100 /* EQ3BW */
+#define WM8983_EQ3BW_MASK 0x0100 /* EQ3BW */
+#define WM8983_EQ3BW_SHIFT 8 /* EQ3BW */
+#define WM8983_EQ3BW_WIDTH 1 /* EQ3BW */
+#define WM8983_EQ3C_MASK 0x0060 /* EQ3C - [6:5] */
+#define WM8983_EQ3C_SHIFT 5 /* EQ3C - [6:5] */
+#define WM8983_EQ3C_WIDTH 2 /* EQ3C - [6:5] */
+#define WM8983_EQ3G_MASK 0x001F /* EQ3G - [4:0] */
+#define WM8983_EQ3G_SHIFT 0 /* EQ3G - [4:0] */
+#define WM8983_EQ3G_WIDTH 5 /* EQ3G - [4:0] */
+
+/*
+ * R21 (0x15) - EQ4 - peak 3
+ */
+#define WM8983_EQ4BW 0x0100 /* EQ4BW */
+#define WM8983_EQ4BW_MASK 0x0100 /* EQ4BW */
+#define WM8983_EQ4BW_SHIFT 8 /* EQ4BW */
+#define WM8983_EQ4BW_WIDTH 1 /* EQ4BW */
+#define WM8983_EQ4C_MASK 0x0060 /* EQ4C - [6:5] */
+#define WM8983_EQ4C_SHIFT 5 /* EQ4C - [6:5] */
+#define WM8983_EQ4C_WIDTH 2 /* EQ4C - [6:5] */
+#define WM8983_EQ4G_MASK 0x001F /* EQ4G - [4:0] */
+#define WM8983_EQ4G_SHIFT 0 /* EQ4G - [4:0] */
+#define WM8983_EQ4G_WIDTH 5 /* EQ4G - [4:0] */
+
+/*
+ * R22 (0x16) - EQ5 - high shelf
+ */
+#define WM8983_EQ5C_MASK 0x0060 /* EQ5C - [6:5] */
+#define WM8983_EQ5C_SHIFT 5 /* EQ5C - [6:5] */
+#define WM8983_EQ5C_WIDTH 2 /* EQ5C - [6:5] */
+#define WM8983_EQ5G_MASK 0x001F /* EQ5G - [4:0] */
+#define WM8983_EQ5G_SHIFT 0 /* EQ5G - [4:0] */
+#define WM8983_EQ5G_WIDTH 5 /* EQ5G - [4:0] */
+
+/*
+ * R24 (0x18) - DAC Limiter 1
+ */
+#define WM8983_LIMEN 0x0100 /* LIMEN */
+#define WM8983_LIMEN_MASK 0x0100 /* LIMEN */
+#define WM8983_LIMEN_SHIFT 8 /* LIMEN */
+#define WM8983_LIMEN_WIDTH 1 /* LIMEN */
+#define WM8983_LIMDCY_MASK 0x00F0 /* LIMDCY - [7:4] */
+#define WM8983_LIMDCY_SHIFT 4 /* LIMDCY - [7:4] */
+#define WM8983_LIMDCY_WIDTH 4 /* LIMDCY - [7:4] */
+#define WM8983_LIMATK_MASK 0x000F /* LIMATK - [3:0] */
+#define WM8983_LIMATK_SHIFT 0 /* LIMATK - [3:0] */
+#define WM8983_LIMATK_WIDTH 4 /* LIMATK - [3:0] */
+
+/*
+ * R25 (0x19) - DAC Limiter 2
+ */
+#define WM8983_LIMLVL_MASK 0x0070 /* LIMLVL - [6:4] */
+#define WM8983_LIMLVL_SHIFT 4 /* LIMLVL - [6:4] */
+#define WM8983_LIMLVL_WIDTH 3 /* LIMLVL - [6:4] */
+#define WM8983_LIMBOOST_MASK 0x000F /* LIMBOOST - [3:0] */
+#define WM8983_LIMBOOST_SHIFT 0 /* LIMBOOST - [3:0] */
+#define WM8983_LIMBOOST_WIDTH 4 /* LIMBOOST - [3:0] */
+
+/*
+ * R27 (0x1B) - Notch Filter 1
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFEN 0x0080 /* NFEN */
+#define WM8983_NFEN_MASK 0x0080 /* NFEN */
+#define WM8983_NFEN_SHIFT 7 /* NFEN */
+#define WM8983_NFEN_WIDTH 1 /* NFEN */
+#define WM8983_NFA0_13_7_MASK 0x007F /* NFA0(13:7) - [6:0] */
+#define WM8983_NFA0_13_7_SHIFT 0 /* NFA0(13:7) - [6:0] */
+#define WM8983_NFA0_13_7_WIDTH 7 /* NFA0(13:7) - [6:0] */
+
+/*
+ * R28 (0x1C) - Notch Filter 2
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFA0_6_0_MASK 0x007F /* NFA0(6:0) - [6:0] */
+#define WM8983_NFA0_6_0_SHIFT 0 /* NFA0(6:0) - [6:0] */
+#define WM8983_NFA0_6_0_WIDTH 7 /* NFA0(6:0) - [6:0] */
+
+/*
+ * R29 (0x1D) - Notch Filter 3
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFA1_13_7_MASK 0x007F /* NFA1(13:7) - [6:0] */
+#define WM8983_NFA1_13_7_SHIFT 0 /* NFA1(13:7) - [6:0] */
+#define WM8983_NFA1_13_7_WIDTH 7 /* NFA1(13:7) - [6:0] */
+
+/*
+ * R30 (0x1E) - Notch Filter 4
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFA1_6_0_MASK 0x007F /* NFA1(6:0) - [6:0] */
+#define WM8983_NFA1_6_0_SHIFT 0 /* NFA1(6:0) - [6:0] */
+#define WM8983_NFA1_6_0_WIDTH 7 /* NFA1(6:0) - [6:0] */
+
+/*
+ * R32 (0x20) - ALC control 1
+ */
+#define WM8983_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */
+#define WM8983_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */
+#define WM8983_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */
+#define WM8983_ALCMAX_MASK 0x0038 /* ALCMAX - [5:3] */
+#define WM8983_ALCMAX_SHIFT 3 /* ALCMAX - [5:3] */
+#define WM8983_ALCMAX_WIDTH 3 /* ALCMAX - [5:3] */
+#define WM8983_ALCMIN_MASK 0x0007 /* ALCMIN - [2:0] */
+#define WM8983_ALCMIN_SHIFT 0 /* ALCMIN - [2:0] */
+#define WM8983_ALCMIN_WIDTH 3 /* ALCMIN - [2:0] */
+
+/*
+ * R33 (0x21) - ALC control 2
+ */
+#define WM8983_ALCHLD_MASK 0x00F0 /* ALCHLD - [7:4] */
+#define WM8983_ALCHLD_SHIFT 4 /* ALCHLD - [7:4] */
+#define WM8983_ALCHLD_WIDTH 4 /* ALCHLD - [7:4] */
+#define WM8983_ALCLVL_MASK 0x000F /* ALCLVL - [3:0] */
+#define WM8983_ALCLVL_SHIFT 0 /* ALCLVL - [3:0] */
+#define WM8983_ALCLVL_WIDTH 4 /* ALCLVL - [3:0] */
+
+/*
+ * R34 (0x22) - ALC control 3
+ */
+#define WM8983_ALCMODE 0x0100 /* ALCMODE */
+#define WM8983_ALCMODE_MASK 0x0100 /* ALCMODE */
+#define WM8983_ALCMODE_SHIFT 8 /* ALCMODE */
+#define WM8983_ALCMODE_WIDTH 1 /* ALCMODE */
+#define WM8983_ALCDCY_MASK 0x00F0 /* ALCDCY - [7:4] */
+#define WM8983_ALCDCY_SHIFT 4 /* ALCDCY - [7:4] */
+#define WM8983_ALCDCY_WIDTH 4 /* ALCDCY - [7:4] */
+#define WM8983_ALCATK_MASK 0x000F /* ALCATK - [3:0] */
+#define WM8983_ALCATK_SHIFT 0 /* ALCATK - [3:0] */
+#define WM8983_ALCATK_WIDTH 4 /* ALCATK - [3:0] */
+
+/*
+ * R35 (0x23) - Noise Gate
+ */
+#define WM8983_NGEN 0x0008 /* NGEN */
+#define WM8983_NGEN_MASK 0x0008 /* NGEN */
+#define WM8983_NGEN_SHIFT 3 /* NGEN */
+#define WM8983_NGEN_WIDTH 1 /* NGEN */
+#define WM8983_NGTH_MASK 0x0007 /* NGTH - [2:0] */
+#define WM8983_NGTH_SHIFT 0 /* NGTH - [2:0] */
+#define WM8983_NGTH_WIDTH 3 /* NGTH - [2:0] */
+
+/*
+ * R36 (0x24) - PLL N
+ */
+#define WM8983_PLL_PRESCALE 0x0010 /* PLL_PRESCALE */
+#define WM8983_PLL_PRESCALE_MASK 0x0010 /* PLL_PRESCALE */
+#define WM8983_PLL_PRESCALE_SHIFT 4 /* PLL_PRESCALE */
+#define WM8983_PLL_PRESCALE_WIDTH 1 /* PLL_PRESCALE */
+#define WM8983_PLLN_MASK 0x000F /* PLLN - [3:0] */
+#define WM8983_PLLN_SHIFT 0 /* PLLN - [3:0] */
+#define WM8983_PLLN_WIDTH 4 /* PLLN - [3:0] */
+
+/*
+ * R37 (0x25) - PLL K 1
+ */
+#define WM8983_PLLK_23_18_MASK 0x003F /* PLLK(23:18) - [5:0] */
+#define WM8983_PLLK_23_18_SHIFT 0 /* PLLK(23:18) - [5:0] */
+#define WM8983_PLLK_23_18_WIDTH 6 /* PLLK(23:18) - [5:0] */
+
+/*
+ * R38 (0x26) - PLL K 2
+ */
+#define WM8983_PLLK_17_9_MASK 0x01FF /* PLLK(17:9) - [8:0] */
+#define WM8983_PLLK_17_9_SHIFT 0 /* PLLK(17:9) - [8:0] */
+#define WM8983_PLLK_17_9_WIDTH 9 /* PLLK(17:9) - [8:0] */
+
+/*
+ * R39 (0x27) - PLL K 3
+ */
+#define WM8983_PLLK_8_0_MASK 0x01FF /* PLLK(8:0) - [8:0] */
+#define WM8983_PLLK_8_0_SHIFT 0 /* PLLK(8:0) - [8:0] */
+#define WM8983_PLLK_8_0_WIDTH 9 /* PLLK(8:0) - [8:0] */
+
+/*
+ * R41 (0x29) - 3D control
+ */
+#define WM8983_DEPTH3D_MASK 0x000F /* DEPTH3D - [3:0] */
+#define WM8983_DEPTH3D_SHIFT 0 /* DEPTH3D - [3:0] */
+#define WM8983_DEPTH3D_WIDTH 4 /* DEPTH3D - [3:0] */
+
+/*
+ * R42 (0x2A) - OUT4 to ADC
+ */
+#define WM8983_OUT4_2ADCVOL_MASK 0x01C0 /* OUT4_2ADCVOL - [8:6] */
+#define WM8983_OUT4_2ADCVOL_SHIFT 6 /* OUT4_2ADCVOL - [8:6] */
+#define WM8983_OUT4_2ADCVOL_WIDTH 3 /* OUT4_2ADCVOL - [8:6] */
+#define WM8983_OUT4_2LNR 0x0020 /* OUT4_2LNR */
+#define WM8983_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */
+#define WM8983_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */
+#define WM8983_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */
+#define WM8983_POBCTRL 0x0004 /* POBCTRL */
+#define WM8983_POBCTRL_MASK 0x0004 /* POBCTRL */
+#define WM8983_POBCTRL_SHIFT 2 /* POBCTRL */
+#define WM8983_POBCTRL_WIDTH 1 /* POBCTRL */
+#define WM8983_DELEN 0x0002 /* DELEN */
+#define WM8983_DELEN_MASK 0x0002 /* DELEN */
+#define WM8983_DELEN_SHIFT 1 /* DELEN */
+#define WM8983_DELEN_WIDTH 1 /* DELEN */
+#define WM8983_OUT1DEL 0x0001 /* OUT1DEL */
+#define WM8983_OUT1DEL_MASK 0x0001 /* OUT1DEL */
+#define WM8983_OUT1DEL_SHIFT 0 /* OUT1DEL */
+#define WM8983_OUT1DEL_WIDTH 1 /* OUT1DEL */
+
+/*
+ * R43 (0x2B) - Beep control
+ */
+#define WM8983_BYPL2RMIX 0x0100 /* BYPL2RMIX */
+#define WM8983_BYPL2RMIX_MASK 0x0100 /* BYPL2RMIX */
+#define WM8983_BYPL2RMIX_SHIFT 8 /* BYPL2RMIX */
+#define WM8983_BYPL2RMIX_WIDTH 1 /* BYPL2RMIX */
+#define WM8983_BYPR2LMIX 0x0080 /* BYPR2LMIX */
+#define WM8983_BYPR2LMIX_MASK 0x0080 /* BYPR2LMIX */
+#define WM8983_BYPR2LMIX_SHIFT 7 /* BYPR2LMIX */
+#define WM8983_BYPR2LMIX_WIDTH 1 /* BYPR2LMIX */
+#define WM8983_MUTERPGA2INV 0x0020 /* MUTERPGA2INV */
+#define WM8983_MUTERPGA2INV_MASK 0x0020 /* MUTERPGA2INV */
+#define WM8983_MUTERPGA2INV_SHIFT 5 /* MUTERPGA2INV */
+#define WM8983_MUTERPGA2INV_WIDTH 1 /* MUTERPGA2INV */
+#define WM8983_INVROUT2 0x0010 /* INVROUT2 */
+#define WM8983_INVROUT2_MASK 0x0010 /* INVROUT2 */
+#define WM8983_INVROUT2_SHIFT 4 /* INVROUT2 */
+#define WM8983_INVROUT2_WIDTH 1 /* INVROUT2 */
+#define WM8983_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */
+#define WM8983_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */
+#define WM8983_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */
+#define WM8983_BEEPEN 0x0001 /* BEEPEN */
+#define WM8983_BEEPEN_MASK 0x0001 /* BEEPEN */
+#define WM8983_BEEPEN_SHIFT 0 /* BEEPEN */
+#define WM8983_BEEPEN_WIDTH 1 /* BEEPEN */
+
+/*
+ * R44 (0x2C) - Input ctrl
+ */
+#define WM8983_MBVSEL 0x0100 /* MBVSEL */
+#define WM8983_MBVSEL_MASK 0x0100 /* MBVSEL */
+#define WM8983_MBVSEL_SHIFT 8 /* MBVSEL */
+#define WM8983_MBVSEL_WIDTH 1 /* MBVSEL */
+#define WM8983_R2_2INPPGA 0x0040 /* R2_2INPPGA */
+#define WM8983_R2_2INPPGA_MASK 0x0040 /* R2_2INPPGA */
+#define WM8983_R2_2INPPGA_SHIFT 6 /* R2_2INPPGA */
+#define WM8983_R2_2INPPGA_WIDTH 1 /* R2_2INPPGA */
+#define WM8983_RIN2INPPGA 0x0020 /* RIN2INPPGA */
+#define WM8983_RIN2INPPGA_MASK 0x0020 /* RIN2INPPGA */
+#define WM8983_RIN2INPPGA_SHIFT 5 /* RIN2INPPGA */
+#define WM8983_RIN2INPPGA_WIDTH 1 /* RIN2INPPGA */
+#define WM8983_RIP2INPPGA 0x0010 /* RIP2INPPGA */
+#define WM8983_RIP2INPPGA_MASK 0x0010 /* RIP2INPPGA */
+#define WM8983_RIP2INPPGA_SHIFT 4 /* RIP2INPPGA */
+#define WM8983_RIP2INPPGA_WIDTH 1 /* RIP2INPPGA */
+#define WM8983_L2_2INPPGA 0x0004 /* L2_2INPPGA */
+#define WM8983_L2_2INPPGA_MASK 0x0004 /* L2_2INPPGA */
+#define WM8983_L2_2INPPGA_SHIFT 2 /* L2_2INPPGA */
+#define WM8983_L2_2INPPGA_WIDTH 1 /* L2_2INPPGA */
+#define WM8983_LIN2INPPGA 0x0002 /* LIN2INPPGA */
+#define WM8983_LIN2INPPGA_MASK 0x0002 /* LIN2INPPGA */
+#define WM8983_LIN2INPPGA_SHIFT 1 /* LIN2INPPGA */
+#define WM8983_LIN2INPPGA_WIDTH 1 /* LIN2INPPGA */
+#define WM8983_LIP2INPPGA 0x0001 /* LIP2INPPGA */
+#define WM8983_LIP2INPPGA_MASK 0x0001 /* LIP2INPPGA */
+#define WM8983_LIP2INPPGA_SHIFT 0 /* LIP2INPPGA */
+#define WM8983_LIP2INPPGA_WIDTH 1 /* LIP2INPPGA */
+
+/*
+ * R45 (0x2D) - Left INP PGA gain ctrl
+ */
+#define WM8983_INPGAVU 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */
+#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */
+#define WM8983_INPPGAZCL 0x0080 /* INPPGAZCL */
+#define WM8983_INPPGAZCL_MASK 0x0080 /* INPPGAZCL */
+#define WM8983_INPPGAZCL_SHIFT 7 /* INPPGAZCL */
+#define WM8983_INPPGAZCL_WIDTH 1 /* INPPGAZCL */
+#define WM8983_INPPGAMUTEL 0x0040 /* INPPGAMUTEL */
+#define WM8983_INPPGAMUTEL_MASK 0x0040 /* INPPGAMUTEL */
+#define WM8983_INPPGAMUTEL_SHIFT 6 /* INPPGAMUTEL */
+#define WM8983_INPPGAMUTEL_WIDTH 1 /* INPPGAMUTEL */
+#define WM8983_INPPGAVOLL_MASK 0x003F /* INPPGAVOLL - [5:0] */
+#define WM8983_INPPGAVOLL_SHIFT 0 /* INPPGAVOLL - [5:0] */
+#define WM8983_INPPGAVOLL_WIDTH 6 /* INPPGAVOLL - [5:0] */
+
+/*
+ * R46 (0x2E) - Right INP PGA gain ctrl
+ */
+#define WM8983_INPGAVU 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */
+#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */
+#define WM8983_INPPGAZCR 0x0080 /* INPPGAZCR */
+#define WM8983_INPPGAZCR_MASK 0x0080 /* INPPGAZCR */
+#define WM8983_INPPGAZCR_SHIFT 7 /* INPPGAZCR */
+#define WM8983_INPPGAZCR_WIDTH 1 /* INPPGAZCR */
+#define WM8983_INPPGAMUTER 0x0040 /* INPPGAMUTER */
+#define WM8983_INPPGAMUTER_MASK 0x0040 /* INPPGAMUTER */
+#define WM8983_INPPGAMUTER_SHIFT 6 /* INPPGAMUTER */
+#define WM8983_INPPGAMUTER_WIDTH 1 /* INPPGAMUTER */
+#define WM8983_INPPGAVOLR_MASK 0x003F /* INPPGAVOLR - [5:0] */
+#define WM8983_INPPGAVOLR_SHIFT 0 /* INPPGAVOLR - [5:0] */
+#define WM8983_INPPGAVOLR_WIDTH 6 /* INPPGAVOLR - [5:0] */
+
+/*
+ * R47 (0x2F) - Left ADC BOOST ctrl
+ */
+#define WM8983_PGABOOSTL 0x0100 /* PGABOOSTL */
+#define WM8983_PGABOOSTL_MASK 0x0100 /* PGABOOSTL */
+#define WM8983_PGABOOSTL_SHIFT 8 /* PGABOOSTL */
+#define WM8983_PGABOOSTL_WIDTH 1 /* PGABOOSTL */
+#define WM8983_L2_2BOOSTVOL_MASK 0x0070 /* L2_2BOOSTVOL - [6:4] */
+#define WM8983_L2_2BOOSTVOL_SHIFT 4 /* L2_2BOOSTVOL - [6:4] */
+#define WM8983_L2_2BOOSTVOL_WIDTH 3 /* L2_2BOOSTVOL - [6:4] */
+#define WM8983_AUXL2BOOSTVOL_MASK 0x0007 /* AUXL2BOOSTVOL - [2:0] */
+#define WM8983_AUXL2BOOSTVOL_SHIFT 0 /* AUXL2BOOSTVOL - [2:0] */
+#define WM8983_AUXL2BOOSTVOL_WIDTH 3 /* AUXL2BOOSTVOL - [2:0] */
+
+/*
+ * R48 (0x30) - Right ADC BOOST ctrl
+ */
+#define WM8983_PGABOOSTR 0x0100 /* PGABOOSTR */
+#define WM8983_PGABOOSTR_MASK 0x0100 /* PGABOOSTR */
+#define WM8983_PGABOOSTR_SHIFT 8 /* PGABOOSTR */
+#define WM8983_PGABOOSTR_WIDTH 1 /* PGABOOSTR */
+#define WM8983_R2_2BOOSTVOL_MASK 0x0070 /* R2_2BOOSTVOL - [6:4] */
+#define WM8983_R2_2BOOSTVOL_SHIFT 4 /* R2_2BOOSTVOL - [6:4] */
+#define WM8983_R2_2BOOSTVOL_WIDTH 3 /* R2_2BOOSTVOL - [6:4] */
+#define WM8983_AUXR2BOOSTVOL_MASK 0x0007 /* AUXR2BOOSTVOL - [2:0] */
+#define WM8983_AUXR2BOOSTVOL_SHIFT 0 /* AUXR2BOOSTVOL - [2:0] */
+#define WM8983_AUXR2BOOSTVOL_WIDTH 3 /* AUXR2BOOSTVOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output ctrl
+ */
+#define WM8983_DACL2RMIX 0x0040 /* DACL2RMIX */
+#define WM8983_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */
+#define WM8983_DACL2RMIX_SHIFT 6 /* DACL2RMIX */
+#define WM8983_DACL2RMIX_WIDTH 1 /* DACL2RMIX */
+#define WM8983_DACR2LMIX 0x0020 /* DACR2LMIX */
+#define WM8983_DACR2LMIX_MASK 0x0020 /* DACR2LMIX */
+#define WM8983_DACR2LMIX_SHIFT 5 /* DACR2LMIX */
+#define WM8983_DACR2LMIX_WIDTH 1 /* DACR2LMIX */
+#define WM8983_OUT4BOOST 0x0010 /* OUT4BOOST */
+#define WM8983_OUT4BOOST_MASK 0x0010 /* OUT4BOOST */
+#define WM8983_OUT4BOOST_SHIFT 4 /* OUT4BOOST */
+#define WM8983_OUT4BOOST_WIDTH 1 /* OUT4BOOST */
+#define WM8983_OUT3BOOST 0x0008 /* OUT3BOOST */
+#define WM8983_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */
+#define WM8983_OUT3BOOST_SHIFT 3 /* OUT3BOOST */
+#define WM8983_OUT3BOOST_WIDTH 1 /* OUT3BOOST */
+#define WM8983_SPKBOOST 0x0004 /* SPKBOOST */
+#define WM8983_SPKBOOST_MASK 0x0004 /* SPKBOOST */
+#define WM8983_SPKBOOST_SHIFT 2 /* SPKBOOST */
+#define WM8983_SPKBOOST_WIDTH 1 /* SPKBOOST */
+#define WM8983_TSDEN 0x0002 /* TSDEN */
+#define WM8983_TSDEN_MASK 0x0002 /* TSDEN */
+#define WM8983_TSDEN_SHIFT 1 /* TSDEN */
+#define WM8983_TSDEN_WIDTH 1 /* TSDEN */
+#define WM8983_VROI 0x0001 /* VROI */
+#define WM8983_VROI_MASK 0x0001 /* VROI */
+#define WM8983_VROI_SHIFT 0 /* VROI */
+#define WM8983_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R50 (0x32) - Left mixer ctrl
+ */
+#define WM8983_AUXLMIXVOL_MASK 0x01C0 /* AUXLMIXVOL - [8:6] */
+#define WM8983_AUXLMIXVOL_SHIFT 6 /* AUXLMIXVOL - [8:6] */
+#define WM8983_AUXLMIXVOL_WIDTH 3 /* AUXLMIXVOL - [8:6] */
+#define WM8983_AUXL2LMIX 0x0020 /* AUXL2LMIX */
+#define WM8983_AUXL2LMIX_MASK 0x0020 /* AUXL2LMIX */
+#define WM8983_AUXL2LMIX_SHIFT 5 /* AUXL2LMIX */
+#define WM8983_AUXL2LMIX_WIDTH 1 /* AUXL2LMIX */
+#define WM8983_BYPLMIXVOL_MASK 0x001C /* BYPLMIXVOL - [4:2] */
+#define WM8983_BYPLMIXVOL_SHIFT 2 /* BYPLMIXVOL - [4:2] */
+#define WM8983_BYPLMIXVOL_WIDTH 3 /* BYPLMIXVOL - [4:2] */
+#define WM8983_BYPL2LMIX 0x0002 /* BYPL2LMIX */
+#define WM8983_BYPL2LMIX_MASK 0x0002 /* BYPL2LMIX */
+#define WM8983_BYPL2LMIX_SHIFT 1 /* BYPL2LMIX */
+#define WM8983_BYPL2LMIX_WIDTH 1 /* BYPL2LMIX */
+#define WM8983_DACL2LMIX 0x0001 /* DACL2LMIX */
+#define WM8983_DACL2LMIX_MASK 0x0001 /* DACL2LMIX */
+#define WM8983_DACL2LMIX_SHIFT 0 /* DACL2LMIX */
+#define WM8983_DACL2LMIX_WIDTH 1 /* DACL2LMIX */
+
+/*
+ * R51 (0x33) - Right mixer ctrl
+ */
+#define WM8983_AUXRMIXVOL_MASK 0x01C0 /* AUXRMIXVOL - [8:6] */
+#define WM8983_AUXRMIXVOL_SHIFT 6 /* AUXRMIXVOL - [8:6] */
+#define WM8983_AUXRMIXVOL_WIDTH 3 /* AUXRMIXVOL - [8:6] */
+#define WM8983_AUXR2RMIX 0x0020 /* AUXR2RMIX */
+#define WM8983_AUXR2RMIX_MASK 0x0020 /* AUXR2RMIX */
+#define WM8983_AUXR2RMIX_SHIFT 5 /* AUXR2RMIX */
+#define WM8983_AUXR2RMIX_WIDTH 1 /* AUXR2RMIX */
+#define WM8983_BYPRMIXVOL_MASK 0x001C /* BYPRMIXVOL - [4:2] */
+#define WM8983_BYPRMIXVOL_SHIFT 2 /* BYPRMIXVOL - [4:2] */
+#define WM8983_BYPRMIXVOL_WIDTH 3 /* BYPRMIXVOL - [4:2] */
+#define WM8983_BYPR2RMIX 0x0002 /* BYPR2RMIX */
+#define WM8983_BYPR2RMIX_MASK 0x0002 /* BYPR2RMIX */
+#define WM8983_BYPR2RMIX_SHIFT 1 /* BYPR2RMIX */
+#define WM8983_BYPR2RMIX_WIDTH 1 /* BYPR2RMIX */
+#define WM8983_DACR2RMIX 0x0001 /* DACR2RMIX */
+#define WM8983_DACR2RMIX_MASK 0x0001 /* DACR2RMIX */
+#define WM8983_DACR2RMIX_SHIFT 0 /* DACR2RMIX */
+#define WM8983_DACR2RMIX_WIDTH 1 /* DACR2RMIX */
+
+/*
+ * R52 (0x34) - LOUT1 (HP) volume ctrl
+ */
+#define WM8983_OUT1VU 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8983_LOUT1ZC 0x0080 /* LOUT1ZC */
+#define WM8983_LOUT1ZC_MASK 0x0080 /* LOUT1ZC */
+#define WM8983_LOUT1ZC_SHIFT 7 /* LOUT1ZC */
+#define WM8983_LOUT1ZC_WIDTH 1 /* LOUT1ZC */
+#define WM8983_LOUT1MUTE 0x0040 /* LOUT1MUTE */
+#define WM8983_LOUT1MUTE_MASK 0x0040 /* LOUT1MUTE */
+#define WM8983_LOUT1MUTE_SHIFT 6 /* LOUT1MUTE */
+#define WM8983_LOUT1MUTE_WIDTH 1 /* LOUT1MUTE */
+#define WM8983_LOUT1VOL_MASK 0x003F /* LOUT1VOL - [5:0] */
+#define WM8983_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [5:0] */
+#define WM8983_LOUT1VOL_WIDTH 6 /* LOUT1VOL - [5:0] */
+
+/*
+ * R53 (0x35) - ROUT1 (HP) volume ctrl
+ */
+#define WM8983_OUT1VU 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8983_ROUT1ZC 0x0080 /* ROUT1ZC */
+#define WM8983_ROUT1ZC_MASK 0x0080 /* ROUT1ZC */
+#define WM8983_ROUT1ZC_SHIFT 7 /* ROUT1ZC */
+#define WM8983_ROUT1ZC_WIDTH 1 /* ROUT1ZC */
+#define WM8983_ROUT1MUTE 0x0040 /* ROUT1MUTE */
+#define WM8983_ROUT1MUTE_MASK 0x0040 /* ROUT1MUTE */
+#define WM8983_ROUT1MUTE_SHIFT 6 /* ROUT1MUTE */
+#define WM8983_ROUT1MUTE_WIDTH 1 /* ROUT1MUTE */
+#define WM8983_ROUT1VOL_MASK 0x003F /* ROUT1VOL - [5:0] */
+#define WM8983_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [5:0] */
+#define WM8983_ROUT1VOL_WIDTH 6 /* ROUT1VOL - [5:0] */
+
+/*
+ * R54 (0x36) - LOUT2 (SPK) volume ctrl
+ */
+#define WM8983_OUT2VU 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */
+#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */
+#define WM8983_LOUT2ZC 0x0080 /* LOUT2ZC */
+#define WM8983_LOUT2ZC_MASK 0x0080 /* LOUT2ZC */
+#define WM8983_LOUT2ZC_SHIFT 7 /* LOUT2ZC */
+#define WM8983_LOUT2ZC_WIDTH 1 /* LOUT2ZC */
+#define WM8983_LOUT2MUTE 0x0040 /* LOUT2MUTE */
+#define WM8983_LOUT2MUTE_MASK 0x0040 /* LOUT2MUTE */
+#define WM8983_LOUT2MUTE_SHIFT 6 /* LOUT2MUTE */
+#define WM8983_LOUT2MUTE_WIDTH 1 /* LOUT2MUTE */
+#define WM8983_LOUT2VOL_MASK 0x003F /* LOUT2VOL - [5:0] */
+#define WM8983_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [5:0] */
+#define WM8983_LOUT2VOL_WIDTH 6 /* LOUT2VOL - [5:0] */
+
+/*
+ * R55 (0x37) - ROUT2 (SPK) volume ctrl
+ */
+#define WM8983_OUT2VU 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */
+#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */
+#define WM8983_ROUT2ZC 0x0080 /* ROUT2ZC */
+#define WM8983_ROUT2ZC_MASK 0x0080 /* ROUT2ZC */
+#define WM8983_ROUT2ZC_SHIFT 7 /* ROUT2ZC */
+#define WM8983_ROUT2ZC_WIDTH 1 /* ROUT2ZC */
+#define WM8983_ROUT2MUTE 0x0040 /* ROUT2MUTE */
+#define WM8983_ROUT2MUTE_MASK 0x0040 /* ROUT2MUTE */
+#define WM8983_ROUT2MUTE_SHIFT 6 /* ROUT2MUTE */
+#define WM8983_ROUT2MUTE_WIDTH 1 /* ROUT2MUTE */
+#define WM8983_ROUT2VOL_MASK 0x003F /* ROUT2VOL - [5:0] */
+#define WM8983_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [5:0] */
+#define WM8983_ROUT2VOL_WIDTH 6 /* ROUT2VOL - [5:0] */
+
+/*
+ * R56 (0x38) - OUT3 mixer ctrl
+ */
+#define WM8983_OUT3MUTE 0x0040 /* OUT3MUTE */
+#define WM8983_OUT3MUTE_MASK 0x0040 /* OUT3MUTE */
+#define WM8983_OUT3MUTE_SHIFT 6 /* OUT3MUTE */
+#define WM8983_OUT3MUTE_WIDTH 1 /* OUT3MUTE */
+#define WM8983_OUT4_2OUT3 0x0008 /* OUT4_2OUT3 */
+#define WM8983_OUT4_2OUT3_MASK 0x0008 /* OUT4_2OUT3 */
+#define WM8983_OUT4_2OUT3_SHIFT 3 /* OUT4_2OUT3 */
+#define WM8983_OUT4_2OUT3_WIDTH 1 /* OUT4_2OUT3 */
+#define WM8983_BYPL2OUT3 0x0004 /* BYPL2OUT3 */
+#define WM8983_BYPL2OUT3_MASK 0x0004 /* BYPL2OUT3 */
+#define WM8983_BYPL2OUT3_SHIFT 2 /* BYPL2OUT3 */
+#define WM8983_BYPL2OUT3_WIDTH 1 /* BYPL2OUT3 */
+#define WM8983_LMIX2OUT3 0x0002 /* LMIX2OUT3 */
+#define WM8983_LMIX2OUT3_MASK 0x0002 /* LMIX2OUT3 */
+#define WM8983_LMIX2OUT3_SHIFT 1 /* LMIX2OUT3 */
+#define WM8983_LMIX2OUT3_WIDTH 1 /* LMIX2OUT3 */
+#define WM8983_LDAC2OUT3 0x0001 /* LDAC2OUT3 */
+#define WM8983_LDAC2OUT3_MASK 0x0001 /* LDAC2OUT3 */
+#define WM8983_LDAC2OUT3_SHIFT 0 /* LDAC2OUT3 */
+#define WM8983_LDAC2OUT3_WIDTH 1 /* LDAC2OUT3 */
+
+/*
+ * R57 (0x39) - OUT4 (MONO) mix ctrl
+ */
+#define WM8983_OUT3_2OUT4 0x0080 /* OUT3_2OUT4 */
+#define WM8983_OUT3_2OUT4_MASK 0x0080 /* OUT3_2OUT4 */
+#define WM8983_OUT3_2OUT4_SHIFT 7 /* OUT3_2OUT4 */
+#define WM8983_OUT3_2OUT4_WIDTH 1 /* OUT3_2OUT4 */
+#define WM8983_OUT4MUTE 0x0040 /* OUT4MUTE */
+#define WM8983_OUT4MUTE_MASK 0x0040 /* OUT4MUTE */
+#define WM8983_OUT4MUTE_SHIFT 6 /* OUT4MUTE */
+#define WM8983_OUT4MUTE_WIDTH 1 /* OUT4MUTE */
+#define WM8983_OUT4ATTN 0x0020 /* OUT4ATTN */
+#define WM8983_OUT4ATTN_MASK 0x0020 /* OUT4ATTN */
+#define WM8983_OUT4ATTN_SHIFT 5 /* OUT4ATTN */
+#define WM8983_OUT4ATTN_WIDTH 1 /* OUT4ATTN */
+#define WM8983_LMIX2OUT4 0x0010 /* LMIX2OUT4 */
+#define WM8983_LMIX2OUT4_MASK 0x0010 /* LMIX2OUT4 */
+#define WM8983_LMIX2OUT4_SHIFT 4 /* LMIX2OUT4 */
+#define WM8983_LMIX2OUT4_WIDTH 1 /* LMIX2OUT4 */
+#define WM8983_LDAC2OUT4 0x0008 /* LDAC2OUT4 */
+#define WM8983_LDAC2OUT4_MASK 0x0008 /* LDAC2OUT4 */
+#define WM8983_LDAC2OUT4_SHIFT 3 /* LDAC2OUT4 */
+#define WM8983_LDAC2OUT4_WIDTH 1 /* LDAC2OUT4 */
+#define WM8983_BYPR2OUT4 0x0004 /* BYPR2OUT4 */
+#define WM8983_BYPR2OUT4_MASK 0x0004 /* BYPR2OUT4 */
+#define WM8983_BYPR2OUT4_SHIFT 2 /* BYPR2OUT4 */
+#define WM8983_BYPR2OUT4_WIDTH 1 /* BYPR2OUT4 */
+#define WM8983_RMIX2OUT4 0x0002 /* RMIX2OUT4 */
+#define WM8983_RMIX2OUT4_MASK 0x0002 /* RMIX2OUT4 */
+#define WM8983_RMIX2OUT4_SHIFT 1 /* RMIX2OUT4 */
+#define WM8983_RMIX2OUT4_WIDTH 1 /* RMIX2OUT4 */
+#define WM8983_RDAC2OUT4 0x0001 /* RDAC2OUT4 */
+#define WM8983_RDAC2OUT4_MASK 0x0001 /* RDAC2OUT4 */
+#define WM8983_RDAC2OUT4_SHIFT 0 /* RDAC2OUT4 */
+#define WM8983_RDAC2OUT4_WIDTH 1 /* RDAC2OUT4 */
+
+/*
+ * R61 (0x3D) - BIAS CTRL
+ */
+#define WM8983_BIASCUT 0x0100 /* BIASCUT */
+#define WM8983_BIASCUT_MASK 0x0100 /* BIASCUT */
+#define WM8983_BIASCUT_SHIFT 8 /* BIASCUT */
+#define WM8983_BIASCUT_WIDTH 1 /* BIASCUT */
+#define WM8983_HALFIPBIAS 0x0080 /* HALFIPBIAS */
+#define WM8983_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */
+#define WM8983_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */
+#define WM8983_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */
+#define WM8983_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */
+#define WM8983_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */
+#define WM8983_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */
+#define WM8983_BUFBIAS_MASK 0x0018 /* BUFBIAS - [4:3] */
+#define WM8983_BUFBIAS_SHIFT 3 /* BUFBIAS - [4:3] */
+#define WM8983_BUFBIAS_WIDTH 2 /* BUFBIAS - [4:3] */
+#define WM8983_ADCBIAS_MASK 0x0006 /* ADCBIAS - [2:1] */
+#define WM8983_ADCBIAS_SHIFT 1 /* ADCBIAS - [2:1] */
+#define WM8983_ADCBIAS_WIDTH 2 /* ADCBIAS - [2:1] */
+#define WM8983_HALFOPBIAS 0x0001 /* HALFOPBIAS */
+#define WM8983_HALFOPBIAS_MASK 0x0001 /* HALFOPBIAS */
+#define WM8983_HALFOPBIAS_SHIFT 0 /* HALFOPBIAS */
+#define WM8983_HALFOPBIAS_WIDTH 1 /* HALFOPBIAS */
+
+enum clk_src {
+ WM8983_CLKSRC_MCLK,
+ WM8983_CLKSRC_PLL
+};
+
+#endif /* _WM8983_H */
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 9e5ff78..6e85b88 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -876,7 +876,7 @@ SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
-
+SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route routes[] = {
@@ -1434,6 +1434,7 @@ static int wm8993_probe(struct snd_soc_codec *codec)
wm8993->hubs_data.hp_startup_mode = 1;
wm8993->hubs_data.dcs_codes = -2;
+ wm8993->hubs_data.series_startup = 1;
ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
if (ret != 0) {
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 970a95c..09e680a 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -195,10 +195,6 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
aif + 1, rate);
}
- if (rate && rate < 3000000)
- dev_warn(codec->dev, "AIF%dCLK is %dHz, should be >=3MHz for optimal performance\n",
- aif + 1, rate);
-
wm8994->aifclk[aif] = rate;
snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1 + offset,
@@ -1146,13 +1142,33 @@ SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer),
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer),
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
};
static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = {
SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0),
-SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0)
+SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
};
static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = {
@@ -1190,7 +1206,6 @@ SND_SOC_DAPM_INPUT("DMIC1DAT"),
SND_SOC_DAPM_INPUT("DMIC2DAT"),
SND_SOC_DAPM_INPUT("Clock"),
-SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0),
SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
SND_SOC_DAPM_PRE_PMU),
@@ -1283,14 +1298,6 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
-SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
-
-SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
- left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
-SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
- right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
-
SND_SOC_DAPM_POST("Debug log", post_ev),
};
@@ -1509,8 +1516,10 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
{ "AIF2DACDAT", NULL, "AIF1DACDAT" },
{ "AIF1ADCDAT", NULL, "AIF2ADCDAT" },
{ "AIF2ADCDAT", NULL, "AIF1ADCDAT" },
- { "MICBIAS", NULL, "CLK_SYS" },
- { "MICBIAS", NULL, "MICBIAS Supply" },
+ { "MICBIAS1", NULL, "CLK_SYS" },
+ { "MICBIAS1", NULL, "MICBIAS Supply" },
+ { "MICBIAS2", NULL, "CLK_SYS" },
+ { "MICBIAS2", NULL, "MICBIAS Supply" },
};
static const struct snd_soc_dapm_route wm8994_intercon[] = {
@@ -1623,6 +1632,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
int reg_offset, ret;
struct fll_div fll;
u16 reg, aif1, aif2;
+ unsigned long timeout;
aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
& WM8994_AIF1CLK_ENA;
@@ -1704,6 +1714,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
(fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
(src - 1));
+ /* Clear any pending completion from a previous failure */
+ try_wait_for_completion(&wm8994->fll_locked[id]);
+
/* Enable (with fractional mode if required) */
if (freq_out) {
if (fll.k)
@@ -1713,6 +1726,16 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
WM8994_FLL1_ENA | WM8994_FLL1_FRAC,
reg);
+
+ if (wm8994->fll_locked_irq) {
+ timeout = wait_for_completion_timeout(&wm8994->fll_locked[id],
+ msecs_to_jiffies(10));
+ if (timeout == 0)
+ dev_warn(codec->dev,
+ "Timed out waiting for FLL lock\n");
+ } else {
+ msleep(5);
+ }
}
wm8994->fll[id].in = freq_in;
@@ -1730,6 +1753,14 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
return 0;
}
+static irqreturn_t wm8994_fll_locked_irq(int irq, void *data)
+{
+ struct completion *completion = data;
+
+ complete(completion);
+
+ return IRQ_HANDLED;
+}
static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
@@ -2269,6 +2300,33 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream,
return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
}
+static void wm8994_aif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int rate_reg = 0;
+
+ switch (dai->id) {
+ case 1:
+ rate_reg = WM8994_AIF1_RATE;
+ break;
+ case 2:
+ rate_reg = WM8994_AIF1_RATE;
+ break;
+ default:
+ break;
+ }
+
+ /* If the DAI is idle then configure the divider tree for the
+ * lowest output rate to save a little power if the clock is
+ * still active (eg, because it is system clock).
+ */
+ if (rate_reg && !dai->playback_active && !dai->capture_active)
+ snd_soc_update_bits(codec, rate_reg,
+ WM8994_AIF1_SR_MASK |
+ WM8994_AIF1CLK_RATE_MASK, 0x9);
+}
+
static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -2335,6 +2393,7 @@ static struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
+ .shutdown = wm8994_aif_shutdown,
.digital_mute = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
@@ -2344,6 +2403,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
+ .shutdown = wm8994_aif_shutdown,
.digital_mute = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
@@ -2761,7 +2821,7 @@ static void wm8958_default_micdet(u16 status, void *data)
report = SND_JACK_MICROPHONE;
/* Everything else is buttons; just assign slots */
- if (status & 0x1c0)
+ if (status & 0x1c)
report |= SND_JACK_BTN_0;
done:
@@ -2847,6 +2907,15 @@ out:
return IRQ_HANDLED;
}
+static irqreturn_t wm8994_fifo_error(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+
+ dev_err(codec->dev, "FIFO error\n");
+
+ return IRQ_HANDLED;
+}
+
static int wm8994_codec_probe(struct snd_soc_codec *codec)
{
struct wm8994 *control;
@@ -2865,6 +2934,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->pdata = dev_get_platdata(codec->dev->parent);
wm8994->codec = codec;
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
+ init_completion(&wm8994->fll_locked[i]);
+
if (wm8994->pdata && wm8994->pdata->micdet_irq)
wm8994->micdet_irq = wm8994->pdata->micdet_irq;
else if (wm8994->pdata && wm8994->pdata->irq_base)
@@ -2903,6 +2975,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->hubs.dcs_codes = -5;
wm8994->hubs.hp_startup_mode = 1;
wm8994->hubs.dcs_readback_mode = 1;
+ wm8994->hubs.series_startup = 1;
break;
default:
wm8994->hubs.dcs_readback_mode = 1;
@@ -2917,6 +2990,15 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
+ wm8994_request_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR,
+ wm8994_fifo_error, "FIFO error", codec);
+
+ ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
+ wm_hubs_dcs_done, "DC servo done",
+ &wm8994->hubs);
+ if (ret == 0)
+ wm8994->hubs.dcs_done_irq = true;
+
switch (control->type) {
case WM8994:
if (wm8994->micdet_irq) {
@@ -2973,6 +3055,16 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
}
+ wm8994->fll_locked_irq = true;
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) {
+ ret = wm8994_request_irq(codec->control_data,
+ WM8994_IRQ_FLL1_LOCK + i,
+ wm8994_fll_locked_irq, "FLL lock",
+ &wm8994->fll_locked[i]);
+ if (ret != 0)
+ wm8994->fll_locked_irq = false;
+ }
+
/* Remember if AIFnLRCLK is configured as a GPIO. This should be
* configured on init - if a system wants to do this dynamically
* at runtime we can deal with that then.
@@ -3048,10 +3140,18 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
- /* Unconditionally enable AIF1 ADC TDM mode; it only affects
- * behaviour on idle TDM clock cycles. */
- snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
- WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
+ /* Unconditionally enable AIF1 ADC TDM mode on chips which can
+ * use this; it only affects behaviour on idle TDM clock
+ * cycles. */
+ switch (control->type) {
+ case WM8994:
+ case WM8958:
+ snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
+ WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
+ break;
+ default:
+ break;
+ }
wm8994_update_class_w(codec);
@@ -3150,6 +3250,12 @@ err_irq:
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
if (wm8994->micdet_irq)
free_irq(wm8994->micdet_irq, wm8994);
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i,
+ &wm8994->fll_locked[i]);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
+ &wm8994->hubs);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
err:
kfree(wm8994);
return ret;
@@ -3159,11 +3265,20 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = codec->control_data;
+ int i;
wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
pm_runtime_disable(codec->dev);
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i,
+ &wm8994->fll_locked[i]);
+
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
+ &wm8994->hubs);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
+
switch (control->type) {
case WM8994:
if (wm8994->micdet_irq)
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 0a1db04..1ab2266 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -11,6 +11,7 @@
#include <sound/soc.h>
#include <linux/firmware.h>
+#include <linux/completion.h>
#include "wm_hubs.h"
@@ -79,6 +80,8 @@ struct wm8994_priv {
int mclk[2];
int aifclk[2];
struct wm8994_fll_config fll[2], fll_suspend[2];
+ struct completion fll_locked[2];
+ bool fll_locked_irq;
int dac_rates[2];
int lrclk_shared[2];
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 91c6b39..a469132 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -727,7 +727,7 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0),
-SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LINEOUT"),
SND_SOC_DAPM_OUTPUT("SPKN"),
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 9e370d1..4cc2d56 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -63,8 +63,10 @@ static const struct soc_enum speaker_mode =
static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
int count = 0;
+ int timeout;
unsigned int val;
val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
@@ -74,18 +76,39 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
dev_dbg(codec->dev, "Waiting for DC servo...\n");
+ if (hubs->dcs_done_irq)
+ timeout = 4;
+ else
+ timeout = 400;
+
do {
count++;
- msleep(1);
+
+ if (hubs->dcs_done_irq)
+ wait_for_completion_timeout(&hubs->dcs_done,
+ msecs_to_jiffies(250));
+ else
+ msleep(1);
+
reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
dev_dbg(codec->dev, "DC servo: %x\n", reg);
- } while (reg & op && count < 400);
+ } while (reg & op && count < timeout);
if (reg & op)
dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
op);
}
+irqreturn_t wm_hubs_dcs_done(int irq, void *data)
+{
+ struct wm_hubs_data *hubs = data;
+
+ complete(&hubs->dcs_done);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
+
/*
* Startup calibration of the DC servo
*/
@@ -107,8 +130,7 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
return;
}
- /* Devices not using a DCS code correction have startup mode */
- if (hubs->dcs_codes) {
+ if (hubs->series_startup) {
/* Set for 32 series updates */
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_SERIES_NO_01_MASK,
@@ -134,9 +156,9 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
break;
case 1:
reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
- reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
+ reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
- reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
+ reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
break;
default:
WARN(1, "Unknown DCS readback method\n");
@@ -150,13 +172,13 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
hubs->dcs_codes);
- /* HPOUT1L */
- offset = reg_l;
+ /* HPOUT1R */
+ offset = reg_r;
offset += hubs->dcs_codes;
dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
- /* HPOUT1R */
- offset = reg_r;
+ /* HPOUT1L */
+ offset = reg_l;
offset += hubs->dcs_codes;
dcs_cfg |= (u8)offset;
@@ -168,8 +190,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1);
} else {
- dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
- dcs_cfg |= reg_r;
+ dcs_cfg = reg_r << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+ dcs_cfg |= reg_l;
}
/* Save the callibrated offset if we're in class W mode and
@@ -195,7 +217,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
/* If we're applying an offset correction then updating the
* callibration would be likely to introduce further offsets. */
- if (hubs->dcs_codes)
+ if (hubs->dcs_codes || hubs->no_series_update)
return ret;
/* Only need to do this if the outputs are active */
@@ -599,9 +621,6 @@ SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0,
SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0,
in2r_pga, ARRAY_SIZE(in2r_pga)),
-/* Dummy widgets to represent differential paths */
-SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
-
SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
mixinl, ARRAY_SIZE(mixinl)),
SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
@@ -867,8 +886,11 @@ EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls);
int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
int lineout1_diff, int lineout2_diff)
{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ init_completion(&hubs->dcs_done);
+
snd_soc_dapm_add_routes(dapm, analogue_routes,
ARRAY_SIZE(analogue_routes));
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index f8a5e97..676b125 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -14,6 +14,9 @@
#ifndef _WM_HUBS_H
#define _WM_HUBS_H
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+
struct snd_soc_codec;
extern const unsigned int wm_hubs_spkmix_tlv[];
@@ -23,9 +26,14 @@ struct wm_hubs_data {
int dcs_codes;
int dcs_readback_mode;
int hp_startup_mode;
+ int series_startup;
+ int no_series_update;
bool class_w;
u16 class_w_dcs;
+
+ bool dcs_done_irq;
+ struct completion dcs_done;
};
extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
@@ -36,4 +44,6 @@ extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
int jd_scthr, int jd_thr,
int micbias1_lvl, int micbias2_lvl);
+extern irqreturn_t wm_hubs_dcs_done(int irq, void *data);
+
#endif
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 9d35b8c..a49e667 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -46,11 +46,28 @@ static void print_buf_info(int slot, char *name)
}
#endif
+#define DAVINCI_PCM_FMTBITS (\
+ SNDRV_PCM_FMTBIT_S8 |\
+ SNDRV_PCM_FMTBIT_U8 |\
+ SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_U16_BE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_BE |\
+ SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_U24_BE |\
+ SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_S32_BE |\
+ SNDRV_PCM_FMTBIT_U32_LE |\
+ SNDRV_PCM_FMTBIT_U32_BE)
+
static struct snd_pcm_hardware pcm_hardware_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME|
+ SNDRV_PCM_INFO_BATCH),
+ .formats = DAVINCI_PCM_FMTBITS,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
@@ -59,7 +76,7 @@ static struct snd_pcm_hardware pcm_hardware_playback = {
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 384,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
@@ -71,8 +88,9 @@ static struct snd_pcm_hardware pcm_hardware_playback = {
static struct snd_pcm_hardware pcm_hardware_capture = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE),
- .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH),
+ .formats = DAVINCI_PCM_FMTBITS,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
@@ -81,7 +99,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = {
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 384,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
@@ -139,6 +157,22 @@ struct davinci_runtime_data {
struct edmacc_param ram_params;
};
+static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ prtd->period++;
+ if (unlikely(prtd->period >= runtime->periods))
+ prtd->period = 0;
+}
+
+static void davinci_pcm_period_reset(struct snd_pcm_substream *substream)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+
+ prtd->period = 0;
+}
/*
* Not used with ping/pong
*/
@@ -199,10 +233,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
else
edma_set_transfer_params(link, acnt, fifo_level, count,
fifo_level, ABSYNC);
-
- prtd->period++;
- if (unlikely(prtd->period >= runtime->periods))
- prtd->period = 0;
}
static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
@@ -217,12 +247,13 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
return;
if (snd_pcm_running(substream)) {
+ spin_lock(&prtd->lock);
if (prtd->ram_channel < 0) {
/* No ping/pong must fix up link dma data*/
- spin_lock(&prtd->lock);
davinci_pcm_enqueue_dma(substream);
- spin_unlock(&prtd->lock);
}
+ davinci_pcm_period_elapsed(substream);
+ spin_unlock(&prtd->lock);
snd_pcm_period_elapsed(substream);
}
}
@@ -425,7 +456,8 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
edma_read_slot(link, &prtd->asp_params);
prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
- prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f);
+ prtd->asp_params.opt |= TCCHEN |
+ EDMA_TCC(prtd->ram_channel & 0x3f);
edma_write_slot(link, &prtd->asp_params);
/* pong */
@@ -439,7 +471,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
/* interrupt after every pong completion */
prtd->asp_params.opt |= TCINTEN | TCCHEN |
- EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel));
+ EDMA_TCC(prtd->ram_channel & 0x3f);
edma_write_slot(link, &prtd->asp_params);
/* ram */
@@ -527,6 +559,13 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ edma_start(prtd->asp_channel);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ prtd->ram_channel >= 0) {
+ /* copy 1st iram buffer */
+ edma_start(prtd->ram_channel);
+ }
+ break;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
edma_resume(prtd->asp_channel);
@@ -550,6 +589,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ davinci_pcm_period_reset(substream);
if (prtd->ram_channel >= 0) {
int ret = ping_pong_dma_setup(substream);
if (ret < 0)
@@ -565,21 +605,31 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
print_buf_info(prtd->asp_link[0], "asp_link[0]");
print_buf_info(prtd->asp_link[1], "asp_link[1]");
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* copy 1st iram buffer */
- edma_start(prtd->ram_channel);
- }
- edma_start(prtd->asp_channel);
+ /*
+ * There is a phase offset of 2 periods between the position
+ * used by dma setup and the position reported in the pointer
+ * function.
+ *
+ * The phase offset, when not using ping-pong buffers, is due to
+ * the two consecutive calls to davinci_pcm_enqueue_dma() below.
+ *
+ * Whereas here, with ping-pong buffers, the phase is due to
+ * there being an entire buffer transfer complete before the
+ * first dma completion event triggers davinci_pcm_dma_irq().
+ */
+ davinci_pcm_period_elapsed(substream);
+ davinci_pcm_period_elapsed(substream);
+
return 0;
}
- prtd->period = 0;
davinci_pcm_enqueue_dma(substream);
+ davinci_pcm_period_elapsed(substream);
/* Copy self-linked parameter RAM entry into master channel */
edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
edma_write_slot(prtd->asp_channel, &prtd->asp_params);
davinci_pcm_enqueue_dma(substream);
- edma_start(prtd->asp_channel);
+ davinci_pcm_period_elapsed(substream);
return 0;
}
@@ -591,51 +641,23 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream)
struct davinci_runtime_data *prtd = runtime->private_data;
unsigned int offset;
int asp_count;
- dma_addr_t asp_src, asp_dst;
-
+ unsigned int period_size = snd_pcm_lib_period_bytes(substream);
+
+ /*
+ * There is a phase offset of 2 periods between the position used by dma
+ * setup and the position reported in the pointer function. Either +2 in
+ * the dma setup or -2 here in the pointer function (with wrapping,
+ * both) accounts for this offset -- choose the latter since it makes
+ * the first-time setup clearer.
+ */
spin_lock(&prtd->lock);
- if (prtd->ram_channel >= 0) {
- int ram_count;
- int mod_ram;
- dma_addr_t ram_src, ram_dst;
- unsigned int period_size = snd_pcm_lib_period_bytes(substream);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* reading ram before asp should be safe
- * as long as the asp transfers less than a ping size
- * of bytes between the 2 reads
- */
- edma_get_position(prtd->ram_channel,
- &ram_src, &ram_dst);
- edma_get_position(prtd->asp_channel,
- &asp_src, &asp_dst);
- asp_count = asp_src - prtd->asp_params.src;
- ram_count = ram_src - prtd->ram_params.src;
- mod_ram = ram_count % period_size;
- mod_ram -= asp_count;
- if (mod_ram < 0)
- mod_ram += period_size;
- else if (mod_ram == 0) {
- if (snd_pcm_running(substream))
- mod_ram += period_size;
- }
- ram_count -= mod_ram;
- if (ram_count < 0)
- ram_count += period_size * runtime->periods;
- } else {
- edma_get_position(prtd->ram_channel,
- &ram_src, &ram_dst);
- ram_count = ram_dst - prtd->ram_params.dst;
- }
- asp_count = ram_count;
- } else {
- edma_get_position(prtd->asp_channel, &asp_src, &asp_dst);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- asp_count = asp_src - runtime->dma_addr;
- else
- asp_count = asp_dst - runtime->dma_addr;
- }
+ asp_count = prtd->period - 2;
spin_unlock(&prtd->lock);
+ if (asp_count < 0)
+ asp_count += runtime->periods;
+ asp_count *= period_size;
+
offset = bytes_to_frames(runtime, asp_count);
if (offset >= runtime->buffer_size)
offset = 0;
@@ -811,9 +833,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
static u64 davinci_pcm_dmamask = 0xffffffff;
-static int davinci_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret;
if (!card->dev->dma_mask)
diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c
index a456e49..e27c417 100644
--- a/sound/soc/ep93xx/ep93xx-pcm.c
+++ b/sound/soc/ep93xx/ep93xx-pcm.c
@@ -266,9 +266,11 @@ static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 ep93xx_pcm_dmamask = 0xffffffff;
-static int ep93xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 6680c0b..732208c 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -294,9 +294,11 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* Regardless of where the memory is actually allocated, since the device can
* technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
*/
-static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
static u64 fsl_dma_dmamask = DMA_BIT_MASK(36);
int ret;
@@ -939,7 +941,7 @@ static int __devinit fsl_soc_dma_probe(struct platform_device *pdev)
iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL);
if (iprop)
- dma->ssi_fifo_depth = *iprop;
+ dma->ssi_fifo_depth = be32_to_cpup(iprop);
else
/* Older 8610 DTs didn't have the fifo-depth property */
dma->ssi_fifo_depth = 8;
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 313e0cc..d48afea 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -678,7 +678,12 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
kfree(ssi_private);
return ret;
}
- ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start);
+ ssi_private->ssi = of_iomap(np, 0);
+ if (!ssi_private->ssi) {
+ dev_err(&pdev->dev, "could not map device resources\n");
+ kfree(ssi_private);
+ return -ENOMEM;
+ }
ssi_private->ssi_phys = res.start;
ssi_private->irq = irq_of_parse_and_map(np, 0);
@@ -691,7 +696,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
/* Determine the FIFO depth. */
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
if (iprop)
- ssi_private->fifo_depth = *iprop;
+ ssi_private->fifo_depth = be32_to_cpup(iprop);
else
/* Older 8610 DTs didn't have the fifo-depth property */
ssi_private->fifo_depth = 8;
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index fff695c..19ad0c1 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -299,10 +299,11 @@ static struct snd_pcm_ops psc_dma_ops = {
};
static u64 psc_dma_dmamask = 0xffffffff;
-static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
size_t size = psc_dma_hardware.buffer_bytes_max;
int rc = 0;
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index c16c6b2..a192979 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -233,7 +233,7 @@ static int get_parent_cell_index(struct device_node *np)
if (!iprop)
return -1;
- return *iprop;
+ return be32_to_cpup(iprop);
}
/**
@@ -258,7 +258,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
if (!iprop)
return -EINVAL;
- addr = *iprop;
+ addr = be32_to_cpup(iprop);
bus = get_parent_cell_index(np);
if (bus < 0)
@@ -305,7 +305,7 @@ static int get_dma_channel(struct device_node *ssi_np,
return -EINVAL;
}
- *dma_channel_id = *iprop;
+ *dma_channel_id = be32_to_cpup(iprop);
*dma_id = get_parent_cell_index(dma_channel_np);
of_node_put(dma_channel_np);
@@ -379,7 +379,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
- machine_data->ssi_id = *iprop;
+ machine_data->ssi_id = be32_to_cpup(iprop);
/* Get the serial format and clock direction. */
sprop = of_get_property(np, "fsl,mode", NULL);
@@ -405,7 +405,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
- machine_data->clk_frequency = *iprop;
+ machine_data->clk_frequency = be32_to_cpup(iprop);
} else if (strcasecmp(sprop, "i2s-master") == 0) {
machine_data->dai_format = SND_SOC_DAIFMT_I2S;
machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index 66e0b68..8fa4d5f 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -232,7 +232,7 @@ static int get_parent_cell_index(struct device_node *np)
iprop = of_get_property(parent, "cell-index", NULL);
if (iprop)
- ret = *iprop;
+ ret = be32_to_cpup(iprop);
of_node_put(parent);
@@ -261,7 +261,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
if (!iprop)
return -EINVAL;
- addr = *iprop;
+ addr = be32_to_cpup(iprop);
bus = get_parent_cell_index(np);
if (bus < 0)
@@ -308,7 +308,7 @@ static int get_dma_channel(struct device_node *ssi_np,
return -EINVAL;
}
- *dma_channel_id = *iprop;
+ *dma_channel_id = be32_to_cpup(iprop);
*dma_id = get_parent_cell_index(dma_channel_np);
of_node_put(dma_channel_np);
@@ -379,7 +379,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
- mdata->ssi_id = *iprop;
+ mdata->ssi_id = be32_to_cpup(iprop);
/* Get the serial format and clock direction. */
sprop = of_get_property(np, "fsl,mode", NULL);
@@ -405,7 +405,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
- mdata->clk_frequency = *iprop;
+ mdata->clk_frequency = be32_to_cpup(iprop);
} else if (strcasecmp(sprop, "i2s-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_I2S;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c
index 413b78d..309c59e 100644
--- a/sound/soc/imx/imx-pcm-fiq.c
+++ b/sound/soc/imx/imx-pcm-fiq.c
@@ -238,12 +238,14 @@ static struct snd_pcm_ops imx_pcm_ops = {
static int ssi_irq = 0;
-static int imx_pcm_fiq_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret;
- ret = imx_pcm_new(card, dai, pcm);
+ ret = imx_pcm_new(rtd);
if (ret)
return ret;
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
index 61fceb0..10a8e27 100644
--- a/sound/soc/imx/imx-ssi.c
+++ b/sound/soc/imx/imx-ssi.c
@@ -388,10 +388,11 @@ static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
-int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
-
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h
index dc8a875..0a84cec 100644
--- a/sound/soc/imx/imx-ssi.h
+++ b/sound/soc/imx/imx-ssi.h
@@ -225,8 +225,7 @@ struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
struct imx_ssi *ssi);
int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
-int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm);
+int imx_pcm_new(struct snd_soc_pcm_runtime *rtd);
void imx_pcm_free(struct snd_pcm *pcm);
/*
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
index fb1483f..a7c9578 100644
--- a/sound/soc/jz4740/jz4740-pcm.c
+++ b/sound/soc/jz4740/jz4740-pcm.c
@@ -299,9 +299,11 @@ static void jz4740_pcm_free(struct snd_pcm *pcm)
static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
-int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index e13c6ce..cd33de1 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -312,9 +312,11 @@ static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
return 0;
}
-static int kirkwood_dma_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret;
if (!card->dev->dma_mask)
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c
index 5a946b4..3e78260 100644
--- a/sound/soc/mid-x86/sst_platform.c
+++ b/sound/soc/mid-x86/sst_platform.c
@@ -402,9 +402,10 @@ static void sst_pcm_free(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
-int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int retval = 0;
pr_debug("sst_pcm_new called\n");
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index dac6732..9c0edad 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -356,7 +356,7 @@ static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev)
nuc900_audio->irq_num = platform_get_irq(pdev, 0);
if (!nuc900_audio->irq_num) {
ret = -EBUSY;
- goto out2;
+ goto out3;
}
nuc900_ac97_data = nuc900_audio;
diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c
index 8263f56..d589ef1 100644
--- a/sound/soc/nuc900/nuc900-pcm.c
+++ b/sound/soc/nuc900/nuc900-pcm.c
@@ -315,9 +315,12 @@ static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
}
static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
-static int nuc900_dma_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
+
if (!card->dev->dma_mask)
card->dev->dma_mask = &nuc900_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 99054cf..fe83d0d 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -9,6 +9,9 @@ config SND_OMAP_SOC_MCBSP
config SND_OMAP_SOC_MCPDM
tristate
+config SND_OMAP_SOC_HDMI
+ tristate
+
config SND_OMAP_SOC_N810
tristate "SoC Audio support for Nokia N810"
depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
@@ -100,6 +103,14 @@ config SND_OMAP_SOC_SDP4430
Say Y if you want to add support for SoC audio on Texas Instruments
SDP4430.
+config SND_OMAP_SOC_OMAP4_HDMI
+ tristate "SoC Audio support for Texas Instruments OMAP4 HDMI"
+ depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4
+ select SND_OMAP_SOC_HDMI
+ help
+ Say Y if you want to add support for SoC HDMI audio on Texas Instruments
+ OMAP4 chips
+
config SND_OMAP_SOC_OMAP3_PANDORA
tristate "SoC Audio support for OMAP3 Pandora"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 6c2c87e..59e2c8d 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -2,10 +2,12 @@
snd-soc-omap-objs := omap-pcm.o
snd-soc-omap-mcbsp-objs := omap-mcbsp.o
snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o
+snd-soc-omap-hdmi-objs := omap-hdmi.o
obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
+obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o
# OMAP Machine Support
snd-soc-n810-objs := n810.o
@@ -21,6 +23,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
snd-soc-zoom2-objs := zoom2.o
snd-soc-igep0020-objs := igep0020.o
+snd-soc-omap4-hdmi-objs := omap4-hdmi-card.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
@@ -36,3 +39,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) += snd-soc-omap4-hdmi.o
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
index 462cbcb..b40095a 100644
--- a/sound/soc/omap/ams-delta.c
+++ b/sound/soc/omap/ams-delta.c
@@ -427,7 +427,8 @@ static struct snd_soc_ops ams_delta_ops = {
/* Board specific codec bias level control */
static int ams_delta_set_bias_level(struct snd_soc_card *card,
- enum snd_soc_bias_level level)
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
{
struct snd_soc_codec *codec = card->rtd->codec;
diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c
new file mode 100644
index 0000000..36c6eae
--- /dev/null
+++ b/sound/soc/omap/omap-hdmi.c
@@ -0,0 +1,158 @@
+/*
+ * omap-hdmi.c
+ *
+ * OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Jorge Candelaria <jorge.candelaria@ti.com>
+ * Ricardo Neri <ricardo.neri@ti.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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <plat/dma.h>
+#include "omap-pcm.h"
+#include "omap-hdmi.h"
+
+#define DRV_NAME "hdmi-audio-dai"
+
+static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = {
+ .name = "HDMI playback",
+ .sync_mode = OMAP_DMA_SYNC_PACKET,
+};
+
+static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int err;
+ /*
+ * Make sure that the period bytes are multiple of the DMA packet size.
+ * Largest packet size we use is 32 32-bit words = 128 bytes
+ */
+ err = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int err = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ omap_hdmi_dai_dma_params.packet_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ omap_hdmi_dai_dma_params.packet_size = 32;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
+
+ snd_soc_dai_set_dma_data(dai, substream,
+ &omap_hdmi_dai_dma_params);
+
+ return err;
+}
+
+static struct snd_soc_dai_ops omap_hdmi_dai_ops = {
+ .startup = omap_hdmi_dai_startup,
+ .hw_params = omap_hdmi_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver omap_hdmi_dai = {
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = OMAP_HDMI_RATES,
+ .formats = OMAP_HDMI_FORMATS,
+ },
+ .ops = &omap_hdmi_dai_ops,
+};
+
+static __devinit int omap_hdmi_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *hdmi_rsrc;
+
+ hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!hdmi_rsrc) {
+ dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n");
+ return -EINVAL;
+ }
+
+ omap_hdmi_dai_dma_params.port_addr = hdmi_rsrc->start
+ + OMAP_HDMI_AUDIO_DMA_PORT;
+
+ hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!hdmi_rsrc) {
+ dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n");
+ return -EINVAL;
+ }
+
+ omap_hdmi_dai_dma_params.dma_req = hdmi_rsrc->start;
+
+ ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai);
+ return ret;
+}
+
+static int __devexit omap_hdmi_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver hdmi_dai_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = omap_hdmi_probe,
+ .remove = __devexit_p(omap_hdmi_remove),
+};
+
+static int __init hdmi_dai_init(void)
+{
+ return platform_driver_register(&hdmi_dai_driver);
+}
+module_init(hdmi_dai_init);
+
+static void __exit hdmi_dai_exit(void)
+{
+ platform_driver_unregister(&hdmi_dai_driver);
+}
+module_exit(hdmi_dai_exit);
+
+MODULE_AUTHOR("Jorge Candelaria <jorge.candelaria@ti.com>");
+MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
+MODULE_DESCRIPTION("OMAP HDMI SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/omap/omap-hdmi.h b/sound/soc/omap/omap-hdmi.h
new file mode 100644
index 0000000..34c298d
--- /dev/null
+++ b/sound/soc/omap/omap-hdmi.h
@@ -0,0 +1,36 @@
+/*
+ * omap-hdmi.h
+ *
+ * Definitions for OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Jorge Candelaria <jorge.candelaria@ti.com>
+ * Ricardo Neri <ricardo.neri@ti.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
+ *
+ */
+
+#ifndef __OMAP_HDMI_H__
+#define __OMAP_HDMI_H__
+
+#define OMAP_HDMI_AUDIO_DMA_PORT 0x8c
+
+#define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#endif
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index e6a6b99..b2f5751 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -366,9 +366,11 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
}
-static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
diff --git a/sound/soc/omap/omap4-hdmi-card.c b/sound/soc/omap/omap4-hdmi-card.c
new file mode 100644
index 0000000..9f32615
--- /dev/null
+++ b/sound/soc/omap/omap4-hdmi-card.c
@@ -0,0 +1,129 @@
+/*
+ * omap4-hdmi-card.c
+ *
+ * OMAP ALSA SoC machine driver for TI OMAP4 HDMI
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Ricardo Neri <ricardo.neri@ti.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 <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-types.h>
+#include <video/omapdss.h>
+
+#define DRV_NAME "omap4-hdmi-audio"
+
+static int omap4_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int i;
+ struct omap_overlay_manager *mgr = NULL;
+ struct device *dev = substream->pcm->card->dev;
+
+ /* Find DSS HDMI device */
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
+ mgr = omap_dss_get_overlay_manager(i);
+ if (mgr && mgr->device
+ && mgr->device->type == OMAP_DISPLAY_TYPE_HDMI)
+ break;
+ }
+
+ if (i == omap_dss_get_num_overlay_managers()) {
+ dev_err(dev, "HDMI display device not found!\n");
+ return -ENODEV;
+ }
+
+ /* Make sure HDMI is power-on to avoid L3 interconnect errors */
+ if (mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) {
+ dev_err(dev, "HDMI display is not active!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops omap4_hdmi_dai_ops = {
+ .hw_params = omap4_hdmi_dai_hw_params,
+};
+
+static struct snd_soc_dai_link omap4_hdmi_dai = {
+ .name = "HDMI",
+ .stream_name = "HDMI",
+ .cpu_dai_name = "hdmi-audio-dai",
+ .platform_name = "omap-pcm-audio",
+ .codec_name = "omapdss_hdmi",
+ .codec_dai_name = "hdmi-audio-codec",
+ .ops = &omap4_hdmi_dai_ops,
+};
+
+static struct snd_soc_card snd_soc_omap4_hdmi = {
+ .name = "OMAP4HDMI",
+ .dai_link = &omap4_hdmi_dai,
+ .num_links = 1,
+};
+
+static __devinit int omap4_hdmi_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_omap4_hdmi;
+ 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);
+ card->dev = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static int __devexit omap4_hdmi_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ card->dev = NULL;
+ return 0;
+}
+
+static struct platform_driver omap4_hdmi_driver = {
+ .driver = {
+ .name = "omap4-hdmi-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = omap4_hdmi_probe,
+ .remove = __devexit_p(omap4_hdmi_remove),
+};
+
+static int __init omap4_hdmi_init(void)
+{
+ return platform_driver_register(&omap4_hdmi_driver);
+}
+module_init(omap4_hdmi_init);
+
+static void __exit omap4_hdmi_exit(void)
+{
+ platform_driver_unregister(&omap4_hdmi_driver);
+}
+module_exit(omap4_hdmi_exit);
+
+MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
+MODULE_DESCRIPTION("OMAP4 HDMI machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index fab20a5..c430600 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -85,9 +85,10 @@ static struct snd_pcm_ops pxa2xx_pcm_ops = {
static u64 pxa2xx_pcm_dmamask = DMA_BIT_MASK(32);
-static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c
index ab3ccae..80c85fd6 100644
--- a/sound/soc/s6000/s6000-pcm.c
+++ b/sound/soc/s6000/s6000-pcm.c
@@ -443,10 +443,11 @@ static void s6000_pcm_free(struct snd_pcm *pcm)
static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32);
-static int s6000_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime)
{
- struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+ struct snd_card *card = runtime->card->snd_card;
+ struct snd_soc_dai *dai = runtime->cpu_dai;
+ struct snd_pcm *pcm = runtime->pcm;
struct s6000_pcm_dma_params *params;
int res;
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index d155cbb..54b0e4b 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -158,7 +158,7 @@ config SND_SOC_GONI_AQUILA_WM8994
config SND_SOC_SAMSUNG_SMDK_SPDIF
tristate "SoC S/PDIF Audio support for SMDK"
- depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210)
+ depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310)
select SND_SAMSUNG_SPDIF
help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
@@ -171,9 +171,23 @@ config SND_SOC_SMDK_WM8580_PCM
help
Say Y if you want to add support for SoC audio on the SMDK.
+config SND_SOC_SMDK_WM8994_PCM
+ tristate "SoC PCM Audio support for WM8994 on SMDK"
+ depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310)
+ select SND_SOC_WM8994
+ 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
+
+config SND_SOC_SPEYSIDE_WM8962
+ tristate "Audio support for Wolfson Speyside with WM8962"
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+ select SND_SAMSUNG_I2S
+ select SND_SOC_WM8962
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 683843a..9eb3b12 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -35,7 +35,9 @@ 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-smdk-wm8994pcm-objs := smdk_wm8994pcm.o
snd-soc-speyside-objs := speyside.o
+snd-soc-speyside-wm8962-objs := speyside_wm8962.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
@@ -54,4 +56,6 @@ 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_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o
obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
+obj-$(CONFIG_SND_SOC_SPEYSIDE_WM8962) += snd-soc-speyside-wm8962.o
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index 5cb3b88..9465588 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -425,9 +425,11 @@ static void dma_free_dma_buffers(struct snd_pcm *pcm)
static u64 dma_mask = DMA_BIT_MASK(32);
-static int dma_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int dma_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
pr_debug("Entered %s\n", __func__);
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
new file mode 100644
index 0000000..c0e6d9a
--- /dev/null
+++ b/sound/soc/samsung/i2s-regs.h
@@ -0,0 +1,143 @@
+/*
+ * linux/sound/soc/samsung/i2s-regs.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung I2S driver's register header
+ *
+ * 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 __SND_SOC_SAMSUNG_I2S_REGS_H
+#define __SND_SOC_SAMSUNG_I2S_REGS_H
+
+#define I2SCON 0x0
+#define I2SMOD 0x4
+#define I2SFIC 0x8
+#define I2SPSR 0xc
+#define I2STXD 0x10
+#define I2SRXD 0x14
+#define I2SFICS 0x18
+#define I2STXDS 0x1c
+#define I2SAHB 0x20
+#define I2SSTR0 0x24
+#define I2SSIZE 0x28
+#define I2STRNCNT 0x2c
+#define I2SLVL0ADDR 0x30
+#define I2SLVL1ADDR 0x34
+#define I2SLVL2ADDR 0x38
+#define I2SLVL3ADDR 0x3c
+
+#define CON_RSTCLR (1 << 31)
+#define CON_FRXOFSTATUS (1 << 26)
+#define CON_FRXORINTEN (1 << 25)
+#define CON_FTXSURSTAT (1 << 24)
+#define CON_FTXSURINTEN (1 << 23)
+#define CON_TXSDMA_PAUSE (1 << 20)
+#define CON_TXSDMA_ACTIVE (1 << 18)
+
+#define CON_FTXURSTATUS (1 << 17)
+#define CON_FTXURINTEN (1 << 16)
+#define CON_TXFIFO2_EMPTY (1 << 15)
+#define CON_TXFIFO1_EMPTY (1 << 14)
+#define CON_TXFIFO2_FULL (1 << 13)
+#define CON_TXFIFO1_FULL (1 << 12)
+
+#define CON_LRINDEX (1 << 11)
+#define CON_TXFIFO_EMPTY (1 << 10)
+#define CON_RXFIFO_EMPTY (1 << 9)
+#define CON_TXFIFO_FULL (1 << 8)
+#define CON_RXFIFO_FULL (1 << 7)
+#define CON_TXDMA_PAUSE (1 << 6)
+#define CON_RXDMA_PAUSE (1 << 5)
+#define CON_TXCH_PAUSE (1 << 4)
+#define CON_RXCH_PAUSE (1 << 3)
+#define CON_TXDMA_ACTIVE (1 << 2)
+#define CON_RXDMA_ACTIVE (1 << 1)
+#define CON_ACTIVE (1 << 0)
+
+#define MOD_OPCLK_CDCLK_OUT (0 << 30)
+#define MOD_OPCLK_CDCLK_IN (1 << 30)
+#define MOD_OPCLK_BCLK_OUT (2 << 30)
+#define MOD_OPCLK_PCLK (3 << 30)
+#define MOD_OPCLK_MASK (3 << 30)
+#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
+
+#define MOD_BLCS_SHIFT 26
+#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT)
+#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT)
+#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT)
+#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT)
+#define MOD_BLCP_SHIFT 24
+#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT)
+#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT)
+#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT)
+#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT)
+
+#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
+#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
+#define MOD_C1DD_HHALF (1 << 19)
+#define MOD_C1DD_LHALF (1 << 18)
+#define MOD_DC2_EN (1 << 17)
+#define MOD_DC1_EN (1 << 16)
+#define MOD_BLC_16BIT (0 << 13)
+#define MOD_BLC_8BIT (1 << 13)
+#define MOD_BLC_24BIT (2 << 13)
+#define MOD_BLC_MASK (3 << 13)
+
+#define MOD_IMS_SYSMUX (1 << 10)
+#define MOD_SLAVE (1 << 11)
+#define MOD_TXONLY (0 << 8)
+#define MOD_RXONLY (1 << 8)
+#define MOD_TXRX (2 << 8)
+#define MOD_MASK (3 << 8)
+#define MOD_LR_LLOW (0 << 7)
+#define MOD_LR_RLOW (1 << 7)
+#define MOD_SDF_IIS (0 << 5)
+#define MOD_SDF_MSB (1 << 5)
+#define MOD_SDF_LSB (2 << 5)
+#define MOD_SDF_MASK (3 << 5)
+#define MOD_RCLK_256FS (0 << 3)
+#define MOD_RCLK_512FS (1 << 3)
+#define MOD_RCLK_384FS (2 << 3)
+#define MOD_RCLK_768FS (3 << 3)
+#define MOD_RCLK_MASK (3 << 3)
+#define MOD_BCLK_32FS (0 << 1)
+#define MOD_BCLK_48FS (1 << 1)
+#define MOD_BCLK_16FS (2 << 1)
+#define MOD_BCLK_24FS (3 << 1)
+#define MOD_BCLK_MASK (3 << 1)
+#define MOD_8BIT (1 << 0)
+
+#define MOD_CDCLKCON (1 << 12)
+
+#define PSR_PSREN (1 << 15)
+
+#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
+#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
+
+#define FIC_TXFLUSH (1 << 15)
+#define FIC_RXFLUSH (1 << 7)
+
+#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
+#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
+#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
+
+#define AHB_INTENLVL0 (1 << 24)
+#define AHB_LVL0INT (1 << 20)
+#define AHB_CLRLVL0INT (1 << 16)
+#define AHB_DMARLD (1 << 5)
+#define AHB_INTMASK (1 << 3)
+#define AHB_DMAEN (1 << 0)
+#define AHB_LVLINTMASK (0xf << 20)
+
+#define I2SSIZE_TRNMSK (0xffff)
+#define I2SSIZE_SHIFT (16)
+
+#endif /* __SND_SOC_SAMSUNG_I2S_REGS_H */
+
+
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 992a732..1568eea 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -22,109 +22,7 @@
#include "dma.h"
#include "i2s.h"
-
-#define I2SCON 0x0
-#define I2SMOD 0x4
-#define I2SFIC 0x8
-#define I2SPSR 0xc
-#define I2STXD 0x10
-#define I2SRXD 0x14
-#define I2SFICS 0x18
-#define I2STXDS 0x1c
-
-#define CON_RSTCLR (1 << 31)
-#define CON_FRXOFSTATUS (1 << 26)
-#define CON_FRXORINTEN (1 << 25)
-#define CON_FTXSURSTAT (1 << 24)
-#define CON_FTXSURINTEN (1 << 23)
-#define CON_TXSDMA_PAUSE (1 << 20)
-#define CON_TXSDMA_ACTIVE (1 << 18)
-
-#define CON_FTXURSTATUS (1 << 17)
-#define CON_FTXURINTEN (1 << 16)
-#define CON_TXFIFO2_EMPTY (1 << 15)
-#define CON_TXFIFO1_EMPTY (1 << 14)
-#define CON_TXFIFO2_FULL (1 << 13)
-#define CON_TXFIFO1_FULL (1 << 12)
-
-#define CON_LRINDEX (1 << 11)
-#define CON_TXFIFO_EMPTY (1 << 10)
-#define CON_RXFIFO_EMPTY (1 << 9)
-#define CON_TXFIFO_FULL (1 << 8)
-#define CON_RXFIFO_FULL (1 << 7)
-#define CON_TXDMA_PAUSE (1 << 6)
-#define CON_RXDMA_PAUSE (1 << 5)
-#define CON_TXCH_PAUSE (1 << 4)
-#define CON_RXCH_PAUSE (1 << 3)
-#define CON_TXDMA_ACTIVE (1 << 2)
-#define CON_RXDMA_ACTIVE (1 << 1)
-#define CON_ACTIVE (1 << 0)
-
-#define MOD_OPCLK_CDCLK_OUT (0 << 30)
-#define MOD_OPCLK_CDCLK_IN (1 << 30)
-#define MOD_OPCLK_BCLK_OUT (2 << 30)
-#define MOD_OPCLK_PCLK (3 << 30)
-#define MOD_OPCLK_MASK (3 << 30)
-#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
-
-#define MOD_BLCS_SHIFT 26
-#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT)
-#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT)
-#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT)
-#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT)
-#define MOD_BLCP_SHIFT 24
-#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT)
-#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT)
-#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT)
-#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT)
-
-#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
-#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
-#define MOD_C1DD_HHALF (1 << 19)
-#define MOD_C1DD_LHALF (1 << 18)
-#define MOD_DC2_EN (1 << 17)
-#define MOD_DC1_EN (1 << 16)
-#define MOD_BLC_16BIT (0 << 13)
-#define MOD_BLC_8BIT (1 << 13)
-#define MOD_BLC_24BIT (2 << 13)
-#define MOD_BLC_MASK (3 << 13)
-
-#define MOD_IMS_SYSMUX (1 << 10)
-#define MOD_SLAVE (1 << 11)
-#define MOD_TXONLY (0 << 8)
-#define MOD_RXONLY (1 << 8)
-#define MOD_TXRX (2 << 8)
-#define MOD_MASK (3 << 8)
-#define MOD_LR_LLOW (0 << 7)
-#define MOD_LR_RLOW (1 << 7)
-#define MOD_SDF_IIS (0 << 5)
-#define MOD_SDF_MSB (1 << 5)
-#define MOD_SDF_LSB (2 << 5)
-#define MOD_SDF_MASK (3 << 5)
-#define MOD_RCLK_256FS (0 << 3)
-#define MOD_RCLK_512FS (1 << 3)
-#define MOD_RCLK_384FS (2 << 3)
-#define MOD_RCLK_768FS (3 << 3)
-#define MOD_RCLK_MASK (3 << 3)
-#define MOD_BCLK_32FS (0 << 1)
-#define MOD_BCLK_48FS (1 << 1)
-#define MOD_BCLK_16FS (2 << 1)
-#define MOD_BCLK_24FS (3 << 1)
-#define MOD_BCLK_MASK (3 << 1)
-#define MOD_8BIT (1 << 0)
-
-#define MOD_CDCLKCON (1 << 12)
-
-#define PSR_PSREN (1 << 15)
-
-#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
-#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
-
-#define FIC_TXFLUSH (1 << 15)
-#define FIC_RXFLUSH (1 << 7)
-#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
-#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
-#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
+#include "i2s-regs.h"
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index e7c1009..45fbe2b 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -8,6 +8,7 @@
*/
#include "../codecs/wm8994.h"
+#include <sound/pcm_params.h>
/*
* Default CFG switch settings to use this driver:
@@ -44,7 +45,9 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
int ret;
/* AIF1CLK should be >=3MHz for optimal performance */
- if (params_rate(params) == 8000 || params_rate(params) == 11025)
+ if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
+ pll_out = params_rate(params) * 384;
+ else if (params_rate(params) == 8000 || params_rate(params) == 11025)
pll_out = params_rate(params) * 512;
else
pll_out = params_rate(params) * 256;
diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c
new file mode 100644
index 0000000..5f21116
--- /dev/null
+++ b/sound/soc/samsung/smdk_wm8994pcm.c
@@ -0,0 +1,176 @@
+/*
+ * sound/soc/samsung/smdk_wm8994pcm.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ * http://www.samsung.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 <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/wm8994.h"
+#include "dma.h"
+#include "pcm.h"
+
+/*
+ * Board Settings:
+ * o '1' means 'ON'
+ * o '0' means 'OFF'
+ * o 'X' means 'Don't care'
+ *
+ * SMDKC210, SMDKV310: CFG3- 1001, CFG5-1000, CFG7-111111
+ */
+
+/*
+ * Configure audio route as :-
+ * $ amixer sset 'DAC1' on,on
+ * $ amixer sset 'Right Headphone Mux' 'DAC'
+ * $ amixer sset 'Left Headphone Mux' 'DAC'
+ * $ amixer sset 'DAC1R Mixer AIF1.1' on
+ * $ amixer sset 'DAC1L Mixer AIF1.1' on
+ * $ amixer sset 'IN2L' on
+ * $ amixer sset 'IN2L PGA IN2LN' on
+ * $ amixer sset 'MIXINL IN2L' on
+ * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
+ * $ amixer sset 'IN2R' on
+ * $ amixer sset 'IN2R PGA IN2RN' on
+ * $ amixer sset 'MIXINR IN2R' on
+ * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
+ */
+
+/* SMDK has a 16.9344MHZ crystal attached to WM8994 */
+#define SMDK_WM8994_FREQ 16934400
+
+static int smdk_wm8994_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;
+ unsigned long mclk_freq;
+ int rfs, ret;
+
+ switch(params_rate(params)) {
+ case 8000:
+ rfs = 512;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "%s:%d Sampling Rate %u not supported!\n",
+ __func__, __LINE__, params_rate(params));
+ return -EINVAL;
+ }
+
+ mclk_freq = params_rate(params) * rfs;
+
+ /* 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;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
+ SMDK_WM8994_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_wm8994_pcm_ops = {
+ .hw_params = smdk_wm8994_pcm_hw_params,
+};
+
+static struct snd_soc_dai_link smdk_dai[] = {
+ {
+ .name = "WM8994 PAIF PCM",
+ .stream_name = "Primary PCM",
+ .cpu_dai_name = "samsung-pcm.0",
+ .codec_dai_name = "wm8994-aif1",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8994-codec",
+ .ops = &smdk_wm8994_pcm_ops,
+ },
+};
+
+static struct snd_soc_card smdk_pcm = {
+ .name = "SMDK-PCM",
+ .dai_link = smdk_dai,
+ .num_links = 1,
+};
+
+static int __devinit snd_smdk_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ 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 WM8994 for PCM");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index 360a333..d6dee4d 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -20,24 +20,29 @@
#define WM8915_HPSEL_GPIO 214
static int speyside_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
int ret;
+ if (dapm->dev != codec_dai->dev)
+ return 0;
+
switch (level) {
case SND_SOC_BIAS_STANDBY:
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK2,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK2,
0, 0, 0);
if (ret < 0) {
pr_err("Failed to stop FLL\n");
return ret;
}
+ break;
default:
break;
@@ -46,6 +51,45 @@ static int speyside_set_bias_level(struct snd_soc_card *card,
return 0;
}
+static int speyside_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ int ret;
+
+ if (dapm->dev != codec_dai->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ WM8915_FLL_MCLK2,
+ 32768, 48000 * 256);
+ if (ret < 0) {
+ pr_err("Failed to start FLL\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ WM8915_SYSCLK_FLL,
+ 48000 * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ card->dapm.bias_level = level;
+
+ return 0;
+}
+
static int speyside_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -66,16 +110,6 @@ static int speyside_hw_params(struct snd_pcm_substream *substream,
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;
}
@@ -127,7 +161,7 @@ static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_codec *codec = rtd->codec;
int ret;
- ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
+ ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK2, 32768, 0);
if (ret < 0)
return ret;
@@ -267,6 +301,7 @@ static struct snd_soc_card speyside = {
.num_configs = ARRAY_SIZE(speyside_codec_conf),
.set_bias_level = speyside_set_bias_level,
+ .set_bias_level_post = speyside_set_bias_level_post,
.controls = controls,
.num_controls = ARRAY_SIZE(controls),
diff --git a/sound/soc/samsung/speyside_wm8962.c b/sound/soc/samsung/speyside_wm8962.c
new file mode 100644
index 0000000..8ac42bf
--- /dev/null
+++ b/sound/soc/samsung/speyside_wm8962.c
@@ -0,0 +1,264 @@
+/*
+ * Speyside with WM8962 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/wm8962.h"
+
+static int speyside_wm8962_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ 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_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+ WM8962_FLL_MCLK, 32768,
+ 44100 * 256);
+ if (ret < 0)
+ pr_err("Failed to start FLL: %d\n", ret);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ WM8962_SYSCLK_FLL,
+ 44100 * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err("Failed to set SYSCLK: %d\n");
+ return ret;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int speyside_wm8962_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ 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, WM8962_SYSCLK_MCLK,
+ 32768, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err("Failed to switch away from FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+ 0, 0, 0);
+ if (ret < 0) {
+ pr_err("Failed to stop FLL: %d\n", ret);
+ return ret;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ dapm->bias_level = level;
+
+ return 0;
+}
+
+static int speyside_wm8962_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;
+
+ return 0;
+}
+
+static struct snd_soc_ops speyside_wm8962_ops = {
+ .hw_params = speyside_wm8962_hw_params,
+};
+
+static struct snd_soc_dai_link speyside_wm8962_dai[] = {
+ {
+ .name = "CPU",
+ .stream_name = "CPU",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_dai_name = "wm8962",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8962.1-001a",
+ .ops = &speyside_wm8962_ops,
+ },
+};
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Main Speaker"),
+};
+
+static struct snd_soc_dapm_widget widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+
+ SND_SOC_DAPM_SPK("Main Speaker", NULL),
+};
+
+static struct snd_soc_dapm_route audio_paths[] = {
+ { "Headphone", NULL, "HPOUTL" },
+ { "Headphone", NULL, "HPOUTR" },
+
+ { "Main Speaker", NULL, "SPKOUTL" },
+ { "Main Speaker", NULL, "SPKOUTR" },
+
+ { "MICBIAS", NULL, "Headset Mic" },
+ { "IN4L", NULL, "MICBIAS" },
+ { "IN4R", NULL, "MICBIAS" },
+
+ { "MICBIAS", NULL, "DMIC" },
+ { "DMICDAT", NULL, "MICBIAS" },
+};
+
+static struct snd_soc_jack speyside_wm8962_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin speyside_wm8962_headset_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int speyside_wm8962_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_codec *codec = card->rtd[0].codec;
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
+ 32768, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_jack_new(codec, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &speyside_wm8962_headset);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_pins(&speyside_wm8962_headset,
+ ARRAY_SIZE(speyside_wm8962_headset_pins),
+ speyside_wm8962_headset_pins);
+ if (ret)
+ return ret;
+
+ wm8962_mic_detect(codec, &speyside_wm8962_headset);
+
+ return 0;
+}
+
+static struct snd_soc_card speyside_wm8962 = {
+ .name = "Speyside WM8962",
+ .dai_link = speyside_wm8962_dai,
+ .num_links = ARRAY_SIZE(speyside_wm8962_dai),
+
+ .set_bias_level = speyside_wm8962_set_bias_level,
+ .set_bias_level_post = speyside_wm8962_set_bias_level_post,
+
+ .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_wm8962_late_probe,
+};
+
+static __devinit int speyside_wm8962_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &speyside_wm8962;
+ 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_wm8962_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_wm8962_driver = {
+ .driver = {
+ .name = "speyside-wm8962",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = speyside_wm8962_probe,
+ .remove = __devexit_p(speyside_wm8962_remove),
+};
+
+static int __init speyside_wm8962_audio_init(void)
+{
+ return platform_driver_register(&speyside_wm8962_driver);
+}
+module_init(speyside_wm8962_audio_init);
+
+static void __exit speyside_wm8962_audio_exit(void)
+{
+ platform_driver_unregister(&speyside_wm8962_driver);
+}
+module_exit(speyside_wm8962_audio_exit);
+
+MODULE_DESCRIPTION("Speyside WM8962 audio support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:speyside-wm8962");
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index c326d29..db74005 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -327,10 +327,10 @@ static void camelot_pcm_free(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
-static int camelot_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_pcm *pcm = rtd->pcm;
+
/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
* in MMAP mode (i.e. aplay -M)
*/
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
index d6f4703..770a71a 100644
--- a/sound/soc/sh/fsi-ak4642.c
+++ b/sound/soc/sh/fsi-ak4642.c
@@ -97,7 +97,7 @@ static int fsi_ak4642_remove(struct platform_device *pdev)
static struct fsi_ak4642_data fsi_a_ak4642 = {
.name = "AK4642",
- .card = "FSIA (AK4642)",
+ .card = "FSIA-AK4642",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi.0",
@@ -106,7 +106,7 @@ static struct fsi_ak4642_data fsi_a_ak4642 = {
static struct fsi_ak4642_data fsi_b_ak4642 = {
.name = "AK4642",
- .card = "FSIB (AK4642)",
+ .card = "FSIB-AK4642",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi.0",
@@ -115,7 +115,7 @@ static struct fsi_ak4642_data fsi_b_ak4642 = {
static struct fsi_ak4642_data fsi_a_ak4643 = {
.name = "AK4643",
- .card = "FSIA (AK4643)",
+ .card = "FSIA-AK4643",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi.0",
@@ -124,7 +124,7 @@ static struct fsi_ak4642_data fsi_a_ak4643 = {
static struct fsi_ak4642_data fsi_b_ak4643 = {
.name = "AK4643",
- .card = "FSIB (AK4643)",
+ .card = "FSIB-AK4643",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi.0",
@@ -133,7 +133,7 @@ static struct fsi_ak4642_data fsi_b_ak4643 = {
static struct fsi_ak4642_data fsi2_a_ak4642 = {
.name = "AK4642",
- .card = "FSI2A (AK4642)",
+ .card = "FSI2A-AK4642",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi2",
@@ -142,7 +142,7 @@ static struct fsi_ak4642_data fsi2_a_ak4642 = {
static struct fsi_ak4642_data fsi2_b_ak4642 = {
.name = "AK4642",
- .card = "FSI2B (AK4642)",
+ .card = "FSI2B-AK4642",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi2",
@@ -151,7 +151,7 @@ static struct fsi_ak4642_data fsi2_b_ak4642 = {
static struct fsi_ak4642_data fsi2_a_ak4643 = {
.name = "AK4643",
- .card = "FSI2A (AK4643)",
+ .card = "FSI2A-AK4643",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi2",
@@ -160,7 +160,7 @@ static struct fsi_ak4642_data fsi2_a_ak4643 = {
static struct fsi_ak4642_data fsi2_b_ak4643 = {
.name = "AK4643",
- .card = "FSI2B (AK4643)",
+ .card = "FSI2B-AK4643",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi2",
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
index dbafd7a..59553fd 100644
--- a/sound/soc/sh/fsi-da7210.c
+++ b/sound/soc/sh/fsi-da7210.c
@@ -42,7 +42,7 @@ static struct snd_soc_dai_link fsi_da7210_dai = {
};
static struct snd_soc_card fsi_soc_card = {
- .name = "FSI (DA7210)",
+ .name = "FSI-DA7210",
.dai_link = &fsi_da7210_dai,
.num_links = 1,
};
diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c
index 9719985..d3d9fd8 100644
--- a/sound/soc/sh/fsi-hdmi.c
+++ b/sound/soc/sh/fsi-hdmi.c
@@ -83,13 +83,13 @@ static int fsi_hdmi_remove(struct platform_device *pdev)
static struct fsi_hdmi_data fsi2_a_hdmi = {
.cpu_dai = "fsia-dai",
- .card = "FSI2A (SH MOBILE HDMI)",
+ .card = "FSI2A-HDMI",
.id = FSI_PORT_A,
};
static struct fsi_hdmi_data fsi2_b_hdmi = {
.cpu_dai = "fsib-dai",
- .card = "FSI2B (SH MOBILE HDMI)",
+ .card = "FSI2B-HDMI",
.id = FSI_PORT_B,
};
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 4a9da6b..8e112cc 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -118,10 +118,38 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena
/*
* FSI driver use below type name for variable
*
- * xxx_len : data length
- * xxx_width : data width
- * xxx_offset : data offset
* xxx_num : number of data
+ * xxx_pos : position of data
+ * xxx_capa : capacity of data
+ */
+
+/*
+ * period/frame/sample image
+ *
+ * ex) PCM (2ch)
+ *
+ * period pos period pos
+ * [n] [n + 1]
+ * |<-------------------- period--------------------->|
+ * ==|============================================ ... =|==
+ * | |
+ * ||<----- frame ----->|<------ frame ----->| ... |
+ * |+--------------------+--------------------+- ... |
+ * ||[ sample ][ sample ]|[ sample ][ sample ]| ... |
+ * |+--------------------+--------------------+- ... |
+ * ==|============================================ ... =|==
+ */
+
+/*
+ * FSI FIFO image
+ *
+ * | |
+ * | |
+ * | [ sample ] |
+ * | [ sample ] |
+ * | [ sample ] |
+ * | [ sample ] |
+ * --> go to codecs
*/
/*
@@ -131,12 +159,11 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena
struct fsi_stream {
struct snd_pcm_substream *substream;
- int fifo_max_num;
-
- int buff_offset;
- int buff_len;
- int period_len;
- int period_num;
+ int fifo_sample_capa; /* sample capacity of FSI FIFO */
+ int buff_sample_capa; /* sample capacity of ALSA buffer */
+ int buff_sample_pos; /* sample position of ALSA buffer */
+ int period_samples; /* sample number / 1 period */
+ int period_pos; /* current period position */
int uerr_num;
int oerr_num;
@@ -149,17 +176,14 @@ struct fsi_priv {
struct fsi_stream playback;
struct fsi_stream capture;
+ u32 do_fmt;
+ u32 di_fmt;
+
int chan_num:16;
int clk_master:1;
+ int spdif: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 {
@@ -180,14 +204,6 @@ 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;
};
/*
@@ -271,6 +287,11 @@ static int fsi_is_port_a(struct fsi_priv *fsi)
return fsi->master->base == fsi->base;
}
+static int fsi_is_spdif(struct fsi_priv *fsi)
+{
+ return fsi->spdif;
+}
+
static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -342,28 +363,59 @@ static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play)
return shift;
}
+static int fsi_frame2sample(struct fsi_priv *fsi, int frames)
+{
+ return frames * fsi->chan_num;
+}
+
+static int fsi_sample2frame(struct fsi_priv *fsi, int samples)
+{
+ return samples / fsi->chan_num;
+}
+
+static int fsi_stream_is_working(struct fsi_priv *fsi,
+ int is_play)
+{
+ struct fsi_stream *io = fsi_get_stream(fsi, is_play);
+ struct fsi_master *master = fsi_get_master(fsi);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&master->lock, flags);
+ ret = !!io->substream;
+ spin_unlock_irqrestore(&master->lock, flags);
+
+ return ret;
+}
+
static void fsi_stream_push(struct fsi_priv *fsi,
int is_play,
- struct snd_pcm_substream *substream,
- u32 buffer_len,
- u32 period_len)
+ struct snd_pcm_substream *substream)
{
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsi_master *master = fsi_get_master(fsi);
+ unsigned long flags;
+ spin_lock_irqsave(&master->lock, flags);
io->substream = substream;
- io->buff_len = buffer_len;
- io->buff_offset = 0;
- io->period_len = period_len;
- io->period_num = 0;
+ io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size);
+ io->buff_sample_pos = 0;
+ io->period_samples = fsi_frame2sample(fsi, runtime->period_size);
+ io->period_pos = 0;
io->oerr_num = -1; /* ignore 1st err */
io->uerr_num = -1; /* ignore 1st err */
+ spin_unlock_irqrestore(&master->lock, flags);
}
static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
{
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+ struct fsi_master *master = fsi_get_master(fsi);
+ unsigned long flags;
+ spin_lock_irqsave(&master->lock, flags);
if (io->oerr_num > 0)
dev_err(dai->dev, "over_run = %d\n", io->oerr_num);
@@ -372,47 +424,27 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
dev_err(dai->dev, "under_run = %d\n", io->uerr_num);
io->substream = NULL;
- io->buff_len = 0;
- io->buff_offset = 0;
- io->period_len = 0;
- io->period_num = 0;
+ io->buff_sample_capa = 0;
+ io->buff_sample_pos = 0;
+ io->period_samples = 0;
+ io->period_pos = 0;
io->oerr_num = 0;
io->uerr_num = 0;
+ spin_unlock_irqrestore(&master->lock, flags);
}
-static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play)
+static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, int is_play)
{
u32 status;
- int data_num;
+ int frames;
status = is_play ?
fsi_reg_read(fsi, DOFF_ST) :
fsi_reg_read(fsi, DIFF_ST);
- data_num = 0x1ff & (status >> 8);
- data_num *= fsi->chan_num;
-
- return data_num;
-}
-
-static int fsi_len2num(int len, int width)
-{
- return len / width;
-}
-
-#define fsi_num2offset(a, b) fsi_num2len(a, b)
-static int fsi_num2len(int num, int width)
-{
- return num * width;
-}
-
-static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play)
-{
- struct fsi_stream *io = fsi_get_stream(fsi, is_play);
- struct snd_pcm_substream *substream = io->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ frames = 0x1ff & (status >> 8);
- return frames_to_bytes(runtime, 1) / fsi->chan_num;
+ return fsi_frame2sample(fsi, frames);
}
static void fsi_count_fifo_err(struct fsi_priv *fsi)
@@ -444,8 +476,10 @@ static u8 *fsi_dma_get_area(struct fsi_priv *fsi, int stream)
{
int is_play = fsi_stream_is_play(stream);
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
+ struct snd_pcm_runtime *runtime = io->substream->runtime;
- return io->substream->runtime->dma_area + io->buff_offset;
+ return runtime->dma_area +
+ samples_to_bytes(runtime, io->buff_sample_pos);
}
static void fsi_dma_soft_push16(struct fsi_priv *fsi, int num)
@@ -559,37 +593,94 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
/*
* 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)
+static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
+ long rate, int enable)
{
- pm_runtime_get_sync(dev);
+ struct fsi_master *master = fsi_get_master(fsi);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
+ int fsi_ver = master->core->ver;
+ int ret;
- 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);
+ ret = set_rate(dev, fsi_is_port_a(fsi), rate, enable);
+ if (ret < 0) /* error */
+ return ret;
+
+ if (!enable)
+ return 0;
+
+ if (ret > 0) {
+ u32 data = 0;
+
+ switch (ret & SH_FSI_ACKMD_MASK) {
+ default:
+ /* FALL THROUGH */
+ case SH_FSI_ACKMD_512:
+ data |= (0x0 << 12);
+ break;
+ case SH_FSI_ACKMD_256:
+ data |= (0x1 << 12);
+ break;
+ case SH_FSI_ACKMD_128:
+ data |= (0x2 << 12);
+ break;
+ case SH_FSI_ACKMD_64:
+ data |= (0x3 << 12);
+ break;
+ case SH_FSI_ACKMD_32:
+ if (fsi_ver < 2)
+ dev_err(dev, "unsupported ACKMD\n");
+ else
+ data |= (0x4 << 12);
+ break;
+ }
+
+ switch (ret & SH_FSI_BPFMD_MASK) {
+ default:
+ /* FALL THROUGH */
+ case SH_FSI_BPFMD_32:
+ data |= (0x0 << 8);
+ break;
+ case SH_FSI_BPFMD_64:
+ data |= (0x1 << 8);
+ break;
+ case SH_FSI_BPFMD_128:
+ data |= (0x2 << 8);
+ break;
+ case SH_FSI_BPFMD_256:
+ data |= (0x3 << 8);
+ break;
+ case SH_FSI_BPFMD_512:
+ data |= (0x4 << 8);
+ break;
+ case SH_FSI_BPFMD_16:
+ if (fsi_ver < 2)
+ dev_err(dev, "unsupported ACKMD\n");
+ else
+ data |= (0x7 << 8);
+ break;
+ }
+
+ fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
+ udelay(10);
+ ret = 0;
}
- pm_runtime_put_sync(dev);
+ return ret;
}
-#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)
+#define fsi_port_start(f, i) __fsi_port_clk_ctrl(f, i, 1)
+#define fsi_port_stop(f, i) __fsi_port_clk_ctrl(f, i, 0)
+static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable)
{
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);
- fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0);
- if (is_master)
+ if (enable)
+ fsi_irq_enable(fsi, is_play);
+ else
+ fsi_irq_disable(fsi, is_play);
+
+ if (fsi_is_clk_master(fsi))
fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
}
@@ -598,18 +689,19 @@ static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
*/
static void fsi_fifo_init(struct fsi_priv *fsi,
int is_play,
- struct snd_soc_dai *dai)
+ struct device *dev)
{
struct fsi_master *master = fsi_get_master(fsi);
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
u32 shift, i;
+ int frame_capa;
/* get on-chip RAM capacity */
shift = fsi_master_read(master, FIFO_SZ);
shift >>= fsi_get_port_shift(fsi, is_play);
shift &= FIFO_SZ_MASK;
- io->fifo_max_num = 256 << shift;
- dev_dbg(dai->dev, "fifo = %d words\n", io->fifo_max_num);
+ frame_capa = 256 << shift;
+ dev_dbg(dev, "fifo = %d words\n", frame_capa);
/*
* The maximum number of sample data varies depending
@@ -631,9 +723,11 @@ static void fsi_fifo_init(struct fsi_priv *fsi,
* 8 channels: 32 ( 32 x 8 = 256)
*/
for (i = 1; i < fsi->chan_num; i <<= 1)
- io->fifo_max_num >>= 1;
- dev_dbg(dai->dev, "%d channel %d store\n",
- fsi->chan_num, io->fifo_max_num);
+ frame_capa >>= 1;
+ dev_dbg(dev, "%d channel %d store\n",
+ fsi->chan_num, frame_capa);
+
+ io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa);
/*
* set interrupt generation factor
@@ -654,10 +748,10 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
struct snd_pcm_substream *substream = NULL;
int is_play = fsi_stream_is_play(stream);
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
- int data_residue_num;
- int data_num;
- int data_num_max;
- int ch_width;
+ int sample_residues;
+ int sample_width;
+ int samples;
+ int samples_max;
int over_period;
void (*fn)(struct fsi_priv *fsi, int size);
@@ -673,36 +767,35 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
/* FSI FIFO has limit.
* So, this driver can not send periods data at a time
*/
- if (io->buff_offset >=
- fsi_num2offset(io->period_num + 1, io->period_len)) {
+ if (io->buff_sample_pos >=
+ io->period_samples * (io->period_pos + 1)) {
over_period = 1;
- io->period_num = (io->period_num + 1) % runtime->periods;
+ io->period_pos = (io->period_pos + 1) % runtime->periods;
- if (0 == io->period_num)
- io->buff_offset = 0;
+ if (0 == io->period_pos)
+ io->buff_sample_pos = 0;
}
- /* get 1 channel data width */
- ch_width = fsi_get_frame_width(fsi, is_play);
+ /* get 1 sample data width */
+ sample_width = samples_to_bytes(runtime, 1);
- /* get residue data number of alsa */
- data_residue_num = fsi_len2num(io->buff_len - io->buff_offset,
- ch_width);
+ /* get number of residue samples */
+ sample_residues = io->buff_sample_capa - io->buff_sample_pos;
if (is_play) {
/*
* for play-back
*
- * data_num_max : number of FSI fifo free space
- * data_num : number of ALSA residue data
+ * samples_max : number of FSI fifo free samples space
+ * samples : number of ALSA residue samples
*/
- data_num_max = io->fifo_max_num * fsi->chan_num;
- data_num_max -= fsi_get_fifo_data_num(fsi, is_play);
+ samples_max = io->fifo_sample_capa;
+ samples_max -= fsi_get_current_fifo_samples(fsi, is_play);
- data_num = data_residue_num;
+ samples = sample_residues;
- switch (ch_width) {
+ switch (sample_width) {
case 2:
fn = fsi_dma_soft_push16;
break;
@@ -716,13 +809,13 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
/*
* for capture
*
- * data_num_max : number of ALSA free space
- * data_num : number of data in FSI fifo
+ * samples_max : number of ALSA free samples space
+ * samples : number of samples in FSI fifo
*/
- data_num_max = data_residue_num;
- data_num = fsi_get_fifo_data_num(fsi, is_play);
+ samples_max = sample_residues;
+ samples = fsi_get_current_fifo_samples(fsi, is_play);
- switch (ch_width) {
+ switch (sample_width) {
case 2:
fn = fsi_dma_soft_pop16;
break;
@@ -734,12 +827,12 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
}
}
- data_num = min(data_num, data_num_max);
+ samples = min(samples, samples_max);
- fn(fsi, data_num);
+ fn(fsi, samples);
- /* update buff_offset */
- io->buff_offset += fsi_num2offset(data_num, ch_width);
+ /* update buff_sample_pos */
+ io->buff_sample_pos += samples;
if (over_period)
snd_pcm_period_elapsed(substream);
@@ -788,16 +881,20 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
* dai ops
*/
-static int fsi_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int fsi_hw_startup(struct fsi_priv *fsi,
+ int is_play,
+ struct device *dev)
{
- struct fsi_priv *fsi = fsi_get_priv(substream);
u32 flags = fsi_get_info_flags(fsi);
- u32 data;
- int is_play = fsi_is_play(substream);
+ u32 data = 0;
- pm_runtime_get_sync(dai->dev);
+ pm_runtime_get_sync(dev);
+ /* clock setting */
+ if (fsi_is_clk_master(fsi))
+ data = DIMD | DOMD;
+
+ fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
/* clock inversion (CKG2) */
data = 0;
@@ -812,54 +909,70 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
fsi_reg_write(fsi, CKG2, data);
+ /* set format */
+ fsi_reg_write(fsi, DO_FMT, fsi->do_fmt);
+ fsi_reg_write(fsi, DI_FMT, fsi->di_fmt);
+
+ /* spdif ? */
+ if (fsi_is_spdif(fsi)) {
+ fsi_spdif_clk_ctrl(fsi, 1);
+ fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+ }
+
/* irq clear */
fsi_irq_disable(fsi, is_play);
fsi_irq_clear_status(fsi);
/* fifo init */
- fsi_fifo_init(fsi, is_play, dai);
+ fsi_fifo_init(fsi, is_play, dev);
return 0;
}
-static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static void fsi_hw_shutdown(struct fsi_priv *fsi,
+ int is_play,
+ struct device *dev)
+{
+ if (fsi_is_clk_master(fsi))
+ fsi_set_master_clk(dev, fsi, fsi->rate, 0);
+
+ pm_runtime_put_sync(dev);
+}
+
+static int fsi_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
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 = fsi_get_info_set_rate(master);
- fsi_irq_disable(fsi, is_play);
+ return fsi_hw_startup(fsi, is_play, dai->dev);
+}
- if (fsi_is_clk_master(fsi))
- set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
+static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsi_priv *fsi = fsi_get_priv(substream);
+ int is_play = fsi_is_play(substream);
+ fsi_hw_shutdown(fsi, is_play, dai->dev);
fsi->rate = 0;
-
- pm_runtime_put_sync(dai->dev);
}
static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
int is_play = fsi_is_play(substream);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- fsi_stream_push(fsi, is_play, substream,
- frames_to_bytes(runtime, runtime->buffer_size),
- frames_to_bytes(runtime, runtime->period_size));
+ fsi_stream_push(fsi, is_play, substream);
ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
- fsi_irq_enable(fsi, is_play);
- fsi_port_start(fsi);
+ fsi_port_start(fsi, is_play);
break;
case SNDRV_PCM_TRIGGER_STOP:
- fsi_port_stop(fsi);
- fsi_irq_disable(fsi, is_play);
+ fsi_port_stop(fsi, is_play);
fsi_stream_pop(fsi, is_play);
break;
}
@@ -884,8 +997,8 @@ static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
return -EINVAL;
}
- fsi_reg_write(fsi, DO_FMT, data);
- fsi_reg_write(fsi, DI_FMT, data);
+ fsi->do_fmt = data;
+ fsi->di_fmt = data;
return 0;
}
@@ -900,11 +1013,10 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
fsi->chan_num = 2;
- fsi_spdif_clk_ctrl(fsi, 1);
- fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+ fsi->spdif = 1;
- fsi_reg_write(fsi, DO_FMT, data);
- fsi_reg_write(fsi, DI_FMT, data);
+ fsi->do_fmt = data;
+ fsi->di_fmt = data;
return 0;
}
@@ -915,32 +1027,24 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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;
- pm_runtime_get_sync(dai->dev);
-
/* set master/slave audio interface */
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;
default:
- ret = -EINVAL;
- goto set_fmt_exit;
+ return -EINVAL;
}
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;
+ return -EINVAL;
}
- fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
-
/* set format */
switch (flags & SH_FSI_FMT_MASK) {
case SH_FSI_FMT_DAI:
@@ -953,9 +1057,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
ret = -EINVAL;
}
-set_fmt_exit:
- pm_runtime_put_sync(dai->dev);
-
return ret;
}
@@ -964,79 +1065,19 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate = fsi_get_info_set_rate(master);
- int fsi_ver = master->core->ver;
long rate = params_rate(params);
int ret;
if (!fsi_is_clk_master(fsi))
return 0;
- ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1);
- if (ret < 0) /* error */
+ ret = fsi_set_master_clk(dai->dev, fsi, rate, 1);
+ if (ret < 0)
return ret;
fsi->rate = rate;
- if (ret > 0) {
- u32 data = 0;
-
- switch (ret & SH_FSI_ACKMD_MASK) {
- default:
- /* FALL THROUGH */
- case SH_FSI_ACKMD_512:
- data |= (0x0 << 12);
- break;
- case SH_FSI_ACKMD_256:
- data |= (0x1 << 12);
- break;
- case SH_FSI_ACKMD_128:
- data |= (0x2 << 12);
- break;
- case SH_FSI_ACKMD_64:
- data |= (0x3 << 12);
- break;
- case SH_FSI_ACKMD_32:
- if (fsi_ver < 2)
- dev_err(dai->dev, "unsupported ACKMD\n");
- else
- data |= (0x4 << 12);
- break;
- }
-
- switch (ret & SH_FSI_BPFMD_MASK) {
- default:
- /* FALL THROUGH */
- case SH_FSI_BPFMD_32:
- data |= (0x0 << 8);
- break;
- case SH_FSI_BPFMD_64:
- data |= (0x1 << 8);
- break;
- case SH_FSI_BPFMD_128:
- data |= (0x2 << 8);
- break;
- case SH_FSI_BPFMD_256:
- data |= (0x3 << 8);
- break;
- case SH_FSI_BPFMD_512:
- data |= (0x4 << 8);
- break;
- case SH_FSI_BPFMD_16:
- if (fsi_ver < 2)
- dev_err(dai->dev, "unsupported ACKMD\n");
- else
- data |= (0x7 << 8);
- break;
- }
-
- fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
- udelay(10);
- ret = 0;
- }
return ret;
-
}
static struct snd_soc_dai_ops fsi_dai_ops = {
@@ -1097,16 +1138,14 @@ static int fsi_hw_free(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream));
- long location;
+ int samples_pos = io->buff_sample_pos - 1;
- location = (io->buff_offset - 1);
- if (location < 0)
- location = 0;
+ if (samples_pos < 0)
+ samples_pos = 0;
- return bytes_to_frames(runtime, location);
+ return fsi_sample2frame(fsi, samples_pos);
}
static struct snd_pcm_ops fsi_pcm_ops = {
@@ -1129,10 +1168,10 @@ static void fsi_pcm_free(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
-static int fsi_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_pcm *pcm = rtd->pcm;
+
/*
* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
* in MMAP mode (i.e. aplay -M)
@@ -1246,8 +1285,6 @@ static int fsi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master);
- fsi_module_init(master, &pdev->dev);
-
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
id_entry->name, master);
if (ret) {
@@ -1290,8 +1327,6 @@ 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);
@@ -1305,53 +1340,43 @@ static int fsi_remove(struct platform_device *pdev)
}
static void __fsi_suspend(struct fsi_priv *fsi,
- struct device *dev,
- set_rate_func set_rate)
+ int is_play,
+ struct device *dev)
{
- 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_stream_is_working(fsi, is_play))
+ return;
- if (fsi_is_clk_master(fsi))
- set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0);
+ fsi_port_stop(fsi, is_play);
+ fsi_hw_shutdown(fsi, is_play, dev);
}
static void __fsi_resume(struct fsi_priv *fsi,
- struct device *dev,
- set_rate_func set_rate)
+ int is_play,
+ struct device *dev)
{
- 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_stream_is_working(fsi, is_play))
+ return;
+
+ fsi_hw_startup(fsi, is_play, dev);
+
+ if (fsi_is_clk_master(fsi) && fsi->rate)
+ fsi_set_master_clk(dev, fsi, fsi->rate, 1);
+
+ fsi_port_start(fsi, is_play);
- 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);
+ struct fsi_priv *fsia = &master->fsia;
+ struct fsi_priv *fsib = &master->fsib;
- 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_suspend(fsia, 1, dev);
+ __fsi_suspend(fsia, 0, dev);
- fsi_module_kill(master, dev);
-
- pm_runtime_put_sync(dev);
+ __fsi_suspend(fsib, 1, dev);
+ __fsi_suspend(fsib, 0, dev);
return 0;
}
@@ -1359,23 +1384,14 @@ static int fsi_suspend(struct device *dev)
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);
+ struct fsi_priv *fsia = &master->fsia;
+ struct fsi_priv *fsib = &master->fsib;
- 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(fsia, 1, dev);
+ __fsi_resume(fsia, 0, dev);
- __fsi_resume(&master->fsia, dev, set_rate);
- __fsi_resume(&master->fsib, dev, set_rate);
-
- pm_runtime_put_sync(dev);
+ __fsi_resume(fsib, 1, dev);
+ __fsi_resume(fsib, 0, dev);
return 0;
}
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
index a423bab..f8f6816 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/sh/siu_pcm.c
@@ -527,10 +527,11 @@ static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, ptr);
}
-static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
/* card->dev == socdev->dev, see snd_soc_new_pcms() */
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
struct siu_info *info = siu_i2s_data;
struct platform_device *pdev = to_platform_device(card->dev);
int ret;
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 039b953..d9f8ade 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -20,422 +20,6 @@
#include <trace/events/asoc.h>
-#ifdef CONFIG_SPI_MASTER
-static int do_spi_write(void *control, const char *data, int len)
-{
- struct spi_device *spi = control;
- int ret;
-
- ret = spi_write(spi, data, len);
- if (ret < 0)
- return ret;
-
- return len;
-}
-#endif
-
-static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value, const void *data, int len)
-{
- int ret;
-
- 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, len);
- if (ret == len)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-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;
-
- 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;
-}
-
-static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- return do_hw_read(codec, reg);
-}
-
-static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u16 data;
-
- data = cpu_to_be16((reg << 12) | (value & 0xffffff));
-
- return do_hw_write(codec, reg, value, &data, 2);
-}
-
-static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- return do_hw_read(codec, reg);
-}
-
-static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- return do_hw_write(codec, reg, value, data, 2);
-}
-
-static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- reg &= 0xff;
- data[0] = reg;
- data[1] = value & 0xff;
-
- 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)
-{
- return do_hw_read(codec, reg);
-}
-
-static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[3];
-
- data[0] = reg;
- data[1] = (value >> 8) & 0xff;
- data[2] = value & 0xff;
-
- 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)
-{
- return do_hw_read(codec, reg);
-}
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int do_i2c_read(struct snd_soc_codec *codec,
- void *reg, int reglen,
- void *data, int datalen)
-{
- struct i2c_msg xfer[2];
- int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = reglen;
- xfer[0].buf = reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = datalen;
- xfer[1].buf = data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- 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
-#define snd_soc_8_8_read_i2c NULL
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
- unsigned int r)
-{
- u8 reg = r;
- u16 data;
- int ret;
-
- ret = do_i2c_read(codec, &reg, 1, &data, 2);
- if (ret < 0)
- return 0;
- return (data >> 8) | ((data & 0xff) << 8);
-}
-#else
-#define snd_soc_8_16_read_i2c NULL
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
- unsigned int r)
-{
- u16 reg = r;
- u8 data;
- int ret;
-
- ret = do_i2c_read(codec, &reg, 2, &data, 1);
- if (ret < 0)
- return 0;
- return data;
-}
-#else
-#define snd_soc_16_8_read_i2c NULL
-#endif
-
-static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- return do_hw_read(codec, reg);
-}
-
-static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[3];
-
- data[0] = (reg >> 8) & 0xff;
- data[1] = reg & 0xff;
- data[2] = value;
-
- return do_hw_write(codec, reg, value, data, 3);
-}
-
-#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)
-{
- u16 reg = cpu_to_be16(r);
- u16 data;
- int ret;
-
- ret = do_i2c_read(codec, &reg, 2, &data, 2);
- if (ret < 0)
- return 0;
- return be16_to_cpu(data);
-}
-#else
-#define snd_soc_16_16_read_i2c NULL
-#endif
-
-static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- 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];
-
- data[0] = (reg >> 8) & 0xff;
- data[1] = reg & 0xff;
- data[2] = (value >> 8) & 0xff;
- data[3] = value & 0xff;
-
- return do_hw_write(codec, reg, value, data, 4);
-}
-
-/* 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();
- }
-
- if (ret == len)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-static struct {
- int addr_bits;
- int data_bits;
- int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned 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,
- },
- {
- .addr_bits = 7, .data_bits = 9,
- .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
- },
- {
- .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,
- },
- {
- .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,
- },
- {
- .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,
- },
- {
- .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,
- },
-};
-
-/**
- * snd_soc_codec_set_cache_io: Set up standard I/O functions.
- *
- * @codec: CODEC to configure.
- * @addr_bits: Number of bits of register address data.
- * @data_bits: Number of bits of data per register.
- * @control: Control bus used.
- *
- * Register formats are frequently shared between many I2C and SPI
- * devices. In order to promote code reuse the ASoC core provides
- * some standard implementations of CODEC read and write operations
- * which can be set up using this function.
- *
- * The caller is responsible for allocating and initialising the
- * actual cache.
- *
- * Note that at present this code cannot be used by CODECs with
- * volatile registers.
- */
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
- int addr_bits, int data_bits,
- enum snd_soc_control_type control)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(io_types); i++)
- if (io_types[i].addr_bits == addr_bits &&
- io_types[i].data_bits == data_bits)
- break;
- if (i == ARRAY_SIZE(io_types)) {
- printk(KERN_ERR
- "No I/O functions for %d bit address %d bit data\n",
- addr_bits, data_bits);
- return -EINVAL;
- }
-
- 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_I2C:
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
- codec->hw_write = (hw_write_t)i2c_master_send;
-#endif
- if (io_types[i].i2c_read)
- codec->hw_read = io_types[i].i2c_read;
-
- codec->control_data = container_of(codec->dev,
- struct i2c_client,
- dev);
- break;
-
- case SND_SOC_SPI:
-#ifdef CONFIG_SPI_MASTER
- codec->hw_write = do_spi_write;
-#endif
-
- codec->control_data = container_of(codec->dev,
- struct spi_device,
- dev);
- break;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
-
static bool snd_soc_set_cache_val(void *base, unsigned int idx,
unsigned int val, unsigned int word_size)
{
@@ -483,31 +67,86 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
}
struct snd_soc_rbtree_node {
- struct rb_node node;
- unsigned int reg;
- unsigned int value;
- unsigned int defval;
+ struct rb_node node; /* the actual rbtree node holding this block */
+ unsigned int base_reg; /* base register handled by this block */
+ unsigned int word_size; /* number of bytes needed to represent the register index */
+ void *block; /* block of adjacent registers */
+ unsigned int blklen; /* number of registers available in the block */
} __attribute__ ((packed));
struct snd_soc_rbtree_ctx {
struct rb_root root;
+ struct snd_soc_rbtree_node *cached_rbnode;
};
+static inline void snd_soc_rbtree_get_base_top_reg(
+ struct snd_soc_rbtree_node *rbnode,
+ unsigned int *base, unsigned int *top)
+{
+ *base = rbnode->base_reg;
+ *top = rbnode->base_reg + rbnode->blklen - 1;
+}
+
+static unsigned int snd_soc_rbtree_get_register(
+ struct snd_soc_rbtree_node *rbnode, unsigned int idx)
+{
+ unsigned int val;
+
+ switch (rbnode->word_size) {
+ case 1: {
+ u8 *p = rbnode->block;
+ val = p[idx];
+ return val;
+ }
+ case 2: {
+ u16 *p = rbnode->block;
+ val = p[idx];
+ return val;
+ }
+ default:
+ BUG();
+ break;
+ }
+ return -1;
+}
+
+static void snd_soc_rbtree_set_register(struct snd_soc_rbtree_node *rbnode,
+ unsigned int idx, unsigned int val)
+{
+ switch (rbnode->word_size) {
+ case 1: {
+ u8 *p = rbnode->block;
+ p[idx] = val;
+ break;
+ }
+ case 2: {
+ u16 *p = rbnode->block;
+ p[idx] = val;
+ break;
+ }
+ default:
+ BUG();
+ break;
+ }
+}
+
static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup(
struct rb_root *root, unsigned int reg)
{
struct rb_node *node;
struct snd_soc_rbtree_node *rbnode;
+ unsigned int base_reg, top_reg;
node = root->rb_node;
while (node) {
rbnode = container_of(node, struct snd_soc_rbtree_node, node);
- if (rbnode->reg < reg)
- node = node->rb_left;
- else if (rbnode->reg > reg)
- node = node->rb_right;
- else
+ snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+ if (reg >= base_reg && reg <= top_reg)
return rbnode;
+ else if (reg > top_reg)
+ node = node->rb_right;
+ else if (reg < base_reg)
+ node = node->rb_left;
}
return NULL;
@@ -518,19 +157,28 @@ static int snd_soc_rbtree_insert(struct rb_root *root,
{
struct rb_node **new, *parent;
struct snd_soc_rbtree_node *rbnode_tmp;
+ unsigned int base_reg_tmp, top_reg_tmp;
+ unsigned int base_reg;
parent = NULL;
new = &root->rb_node;
while (*new) {
rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node,
node);
+ /* base and top registers of the current rbnode */
+ snd_soc_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
+ &top_reg_tmp);
+ /* base register of the rbnode to be added */
+ base_reg = rbnode->base_reg;
parent = *new;
- if (rbnode_tmp->reg < rbnode->reg)
- new = &((*new)->rb_left);
- else if (rbnode_tmp->reg > rbnode->reg)
- new = &((*new)->rb_right);
- else
+ /* if this register has already been inserted, just return */
+ if (base_reg >= base_reg_tmp &&
+ base_reg <= top_reg_tmp)
return 0;
+ else if (base_reg > top_reg_tmp)
+ new = &((*new)->rb_right);
+ else if (base_reg < base_reg_tmp)
+ new = &((*new)->rb_left);
}
/* insert the node into the rbtree */
@@ -545,58 +193,146 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
struct snd_soc_rbtree_ctx *rbtree_ctx;
struct rb_node *node;
struct snd_soc_rbtree_node *rbnode;
- unsigned int val;
+ unsigned int regtmp;
+ unsigned int val, def;
int ret;
+ int i;
rbtree_ctx = codec->reg_cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
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;
- codec->cache_bypass = 1;
- ret = snd_soc_write(codec, rbnode->reg, val);
- codec->cache_bypass = 0;
- if (ret)
- return ret;
- dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
- rbnode->reg, val);
+ for (i = 0; i < rbnode->blklen; ++i) {
+ regtmp = rbnode->base_reg + i;
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, regtmp));
+ val = snd_soc_rbtree_get_register(rbnode, i);
+ def = snd_soc_get_cache_val(codec->reg_def_copy, i,
+ rbnode->word_size);
+ if (val == def)
+ continue;
+
+ codec->cache_bypass = 1;
+ ret = snd_soc_write(codec, regtmp, val);
+ codec->cache_bypass = 0;
+ if (ret)
+ return ret;
+ dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
+ regtmp, val);
+ }
}
return 0;
}
+static int snd_soc_rbtree_insert_to_block(struct snd_soc_rbtree_node *rbnode,
+ unsigned int pos, unsigned int reg,
+ unsigned int value)
+{
+ u8 *blk;
+
+ blk = krealloc(rbnode->block,
+ (rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL);
+ if (!blk)
+ return -ENOMEM;
+
+ /* insert the register value in the correct place in the rbnode block */
+ memmove(blk + (pos + 1) * rbnode->word_size,
+ blk + pos * rbnode->word_size,
+ (rbnode->blklen - pos) * rbnode->word_size);
+
+ /* update the rbnode block, its size and the base register */
+ rbnode->block = blk;
+ rbnode->blklen++;
+ if (!pos)
+ rbnode->base_reg = reg;
+
+ snd_soc_rbtree_set_register(rbnode, pos, value);
+ return 0;
+}
+
static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
struct snd_soc_rbtree_ctx *rbtree_ctx;
- struct snd_soc_rbtree_node *rbnode;
+ struct snd_soc_rbtree_node *rbnode, *rbnode_tmp;
+ struct rb_node *node;
+ unsigned int val;
+ unsigned int reg_tmp;
+ unsigned int base_reg, top_reg;
+ unsigned int pos;
+ int i;
+ int ret;
rbtree_ctx = codec->reg_cache;
+ /* look up the required register in the cached rbnode */
+ rbnode = rbtree_ctx->cached_rbnode;
+ if (rbnode) {
+ snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+ if (reg >= base_reg && reg <= top_reg) {
+ reg_tmp = reg - base_reg;
+ val = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+ if (val == value)
+ return 0;
+ snd_soc_rbtree_set_register(rbnode, reg_tmp, value);
+ return 0;
+ }
+ }
+ /* if we can't locate it in the cached rbnode we'll have
+ * to traverse the rbtree looking for it.
+ */
rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
if (rbnode) {
- if (rbnode->value == value)
+ reg_tmp = reg - rbnode->base_reg;
+ val = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+ if (val == value)
return 0;
- rbnode->value = value;
+ snd_soc_rbtree_set_register(rbnode, reg_tmp, value);
+ rbtree_ctx->cached_rbnode = rbnode;
} else {
/* bail out early, no need to create the rbnode yet */
if (!value)
return 0;
- /*
- * for uninitialized registers whose value is changed
- * from the default zero, create an rbnode and insert
- * it into the tree.
+ /* look for an adjacent register to the one we are about to add */
+ for (node = rb_first(&rbtree_ctx->root); node;
+ node = rb_next(node)) {
+ rbnode_tmp = rb_entry(node, struct snd_soc_rbtree_node, node);
+ for (i = 0; i < rbnode_tmp->blklen; ++i) {
+ reg_tmp = rbnode_tmp->base_reg + i;
+ if (abs(reg_tmp - reg) != 1)
+ continue;
+ /* decide where in the block to place our register */
+ if (reg_tmp + 1 == reg)
+ pos = i + 1;
+ else
+ pos = i;
+ ret = snd_soc_rbtree_insert_to_block(rbnode_tmp, pos,
+ reg, value);
+ if (ret)
+ return ret;
+ rbtree_ctx->cached_rbnode = rbnode_tmp;
+ return 0;
+ }
+ }
+ /* we did not manage to find a place to insert it in an existing
+ * block so create a new rbnode with a single register in its block.
+ * This block will get populated further if any other adjacent
+ * registers get modified in the future.
*/
rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
if (!rbnode)
return -ENOMEM;
- rbnode->reg = reg;
- rbnode->value = value;
+ rbnode->blklen = 1;
+ rbnode->base_reg = reg;
+ rbnode->word_size = codec->driver->reg_word_size;
+ rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size,
+ GFP_KERNEL);
+ if (!rbnode->block) {
+ kfree(rbnode);
+ return -ENOMEM;
+ }
+ snd_soc_rbtree_set_register(rbnode, 0, value);
snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode);
+ rbtree_ctx->cached_rbnode = rbnode;
}
return 0;
@@ -607,11 +343,28 @@ static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec,
{
struct snd_soc_rbtree_ctx *rbtree_ctx;
struct snd_soc_rbtree_node *rbnode;
+ unsigned int base_reg, top_reg;
+ unsigned int reg_tmp;
rbtree_ctx = codec->reg_cache;
+ /* look up the required register in the cached rbnode */
+ rbnode = rbtree_ctx->cached_rbnode;
+ if (rbnode) {
+ snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+ if (reg >= base_reg && reg <= top_reg) {
+ reg_tmp = reg - base_reg;
+ *value = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+ return 0;
+ }
+ }
+ /* if we can't locate it in the cached rbnode we'll have
+ * to traverse the rbtree looking for it.
+ */
rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
if (rbnode) {
- *value = rbnode->value;
+ reg_tmp = reg - rbnode->base_reg;
+ *value = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+ rbtree_ctx->cached_rbnode = rbnode;
} else {
/* uninitialized registers default to 0 */
*value = 0;
@@ -637,6 +390,7 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node);
next = rb_next(&rbtree_node->node);
rb_erase(&rbtree_node->node, &rbtree_ctx->root);
+ kfree(rbtree_node->block);
kfree(rbtree_node);
}
@@ -649,10 +403,9 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
{
- struct snd_soc_rbtree_node *rbtree_node;
struct snd_soc_rbtree_ctx *rbtree_ctx;
- unsigned int val;
unsigned int word_size;
+ unsigned int val;
int i;
int ret;
@@ -662,32 +415,27 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
rbtree_ctx = codec->reg_cache;
rbtree_ctx->root = RB_ROOT;
+ rbtree_ctx->cached_rbnode = NULL;
if (!codec->reg_def_copy)
return 0;
- /*
- * populate the rbtree with the initialized registers. All other
- * registers will be inserted when they are first modified.
- */
word_size = codec->driver->reg_word_size;
for (i = 0; i < codec->driver->reg_cache_size; ++i) {
- val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size);
+ val = snd_soc_get_cache_val(codec->reg_def_copy, i,
+ word_size);
if (!val)
continue;
- rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);
- if (!rbtree_node) {
- ret = -ENOMEM;
- snd_soc_cache_exit(codec);
- break;
- }
- rbtree_node->reg = i;
- rbtree_node->value = val;
- rbtree_node->defval = val;
- snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node);
+ ret = snd_soc_rbtree_cache_write(codec, i, val);
+ if (ret)
+ goto err;
}
return 0;
+
+err:
+ snd_soc_cache_exit(codec);
+ return ret;
}
#ifdef CONFIG_SND_SOC_CACHE_LZO
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index d75043e..e44267f 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -44,7 +44,6 @@
#define NAME_SIZE 32
-static DEFINE_MUTEX(pcm_mutex);
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
#ifdef CONFIG_DEBUG_FS
@@ -58,7 +57,7 @@ static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
-static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
+int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
@@ -485,552 +484,6 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
}
#endif
-static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
-{
- 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;
-
- if (!codec_dai->driver->symmetric_rates &&
- !cpu_dai->driver->symmetric_rates &&
- !rtd->dai_link->symmetric_rates)
- return 0;
-
- /* This can happen if multiple streams are starting simultaneously -
- * the second can need to get its constraints before the first has
- * picked a rate. Complain and allow the application to carry on.
- */
- if (!rtd->rate) {
- dev_warn(&rtd->dev,
- "Not enforcing symmetric_rates due to race\n");
- return 0;
- }
-
- dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
-
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- rtd->rate, rtd->rate);
- if (ret < 0) {
- dev_err(&rtd->dev,
- "Unable to apply rate symmetry constraint: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Called by ALSA when a PCM substream is opened, the runtime->hw record is
- * then initialized and any private data can be allocated. This also calls
- * startup for the cpu DAI, platform, machine and codec DAI.
- */
-static int soc_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
- struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
- int ret = 0;
-
- mutex_lock(&pcm_mutex);
-
- /* startup the audio subsystem */
- if (cpu_dai->driver->ops->startup) {
- ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't open interface %s\n",
- cpu_dai->name);
- goto out;
- }
- }
-
- 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);
- goto platform_err;
- }
- }
-
- if (codec_dai->driver->ops->startup) {
- ret = codec_dai->driver->ops->startup(substream, codec_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't open codec %s\n",
- codec_dai->name);
- goto codec_dai_err;
- }
- }
-
- if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
- ret = rtd->dai_link->ops->startup(substream);
- if (ret < 0) {
- printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
- goto machine_err;
- }
- }
-
- /* Check that the codec and cpu DAIs are compatible */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw.rate_min =
- max(codec_dai_drv->playback.rate_min,
- cpu_dai_drv->playback.rate_min);
- runtime->hw.rate_max =
- min(codec_dai_drv->playback.rate_max,
- cpu_dai_drv->playback.rate_max);
- runtime->hw.channels_min =
- max(codec_dai_drv->playback.channels_min,
- cpu_dai_drv->playback.channels_min);
- runtime->hw.channels_max =
- min(codec_dai_drv->playback.channels_max,
- cpu_dai_drv->playback.channels_max);
- runtime->hw.formats =
- codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
- runtime->hw.rates =
- codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
- if (codec_dai_drv->playback.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= cpu_dai_drv->playback.rates;
- if (cpu_dai_drv->playback.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= codec_dai_drv->playback.rates;
- } else {
- runtime->hw.rate_min =
- max(codec_dai_drv->capture.rate_min,
- cpu_dai_drv->capture.rate_min);
- runtime->hw.rate_max =
- min(codec_dai_drv->capture.rate_max,
- cpu_dai_drv->capture.rate_max);
- runtime->hw.channels_min =
- max(codec_dai_drv->capture.channels_min,
- cpu_dai_drv->capture.channels_min);
- runtime->hw.channels_max =
- min(codec_dai_drv->capture.channels_max,
- cpu_dai_drv->capture.channels_max);
- runtime->hw.formats =
- codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
- runtime->hw.rates =
- codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
- if (codec_dai_drv->capture.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= cpu_dai_drv->capture.rates;
- if (cpu_dai_drv->capture.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= codec_dai_drv->capture.rates;
- }
-
- ret = -EINVAL;
- snd_pcm_limit_hw_rates(runtime);
- if (!runtime->hw.rates) {
- printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
- codec_dai->name, cpu_dai->name);
- goto config_err;
- }
- if (!runtime->hw.formats) {
- printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
- codec_dai->name, cpu_dai->name);
- goto config_err;
- }
- if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
- runtime->hw.channels_min > runtime->hw.channels_max) {
- printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
- codec_dai->name, cpu_dai->name);
- goto config_err;
- }
-
- /* Symmetry only applies if we've already got an active stream. */
- if (cpu_dai->active || codec_dai->active) {
- ret = soc_pcm_apply_symmetry(substream);
- if (ret != 0)
- goto config_err;
- }
-
- pr_debug("asoc: %s <-> %s info:\n",
- codec_dai->name, cpu_dai->name);
- pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
- pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
- runtime->hw.channels_max);
- pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
- runtime->hw.rate_max);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- cpu_dai->playback_active++;
- codec_dai->playback_active++;
- } else {
- cpu_dai->capture_active++;
- codec_dai->capture_active++;
- }
- cpu_dai->active++;
- codec_dai->active++;
- rtd->codec->active++;
- mutex_unlock(&pcm_mutex);
- return 0;
-
-config_err:
- if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
- rtd->dai_link->ops->shutdown(substream);
-
-machine_err:
- if (codec_dai->driver->ops->shutdown)
- codec_dai->driver->ops->shutdown(substream, codec_dai);
-
-codec_dai_err:
- if (platform->driver->ops && platform->driver->ops->close)
- platform->driver->ops->close(substream);
-
-platform_err:
- if (cpu_dai->driver->ops->shutdown)
- cpu_dai->driver->ops->shutdown(substream, cpu_dai);
-out:
- mutex_unlock(&pcm_mutex);
- return ret;
-}
-
-/*
- * Power down the audio subsystem pmdown_time msecs after close is called.
- * This is to ensure there are no pops or clicks in between any music tracks
- * due to DAPM power cycling.
- */
-static void close_delayed_work(struct work_struct *work)
-{
- struct snd_soc_pcm_runtime *rtd =
- container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- mutex_lock(&pcm_mutex);
-
- pr_debug("pop wq checking: %s status: %s waiting: %s\n",
- codec_dai->driver->playback.stream_name,
- codec_dai->playback_active ? "active" : "inactive",
- codec_dai->pop_wait ? "yes" : "no");
-
- /* are we waiting on this codec DAI stream */
- if (codec_dai->pop_wait == 1) {
- codec_dai->pop_wait = 0;
- snd_soc_dapm_stream_event(rtd,
- codec_dai->driver->playback.stream_name,
- SND_SOC_DAPM_STREAM_STOP);
- }
-
- mutex_unlock(&pcm_mutex);
-}
-
-/*
- * Called by ALSA when a PCM substream is closed. Private data can be
- * freed here. The cpu DAI, codec DAI, machine and platform are also
- * shutdown.
- */
-static int soc_codec_close(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = rtd->codec;
-
- mutex_lock(&pcm_mutex);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- cpu_dai->playback_active--;
- codec_dai->playback_active--;
- } else {
- cpu_dai->capture_active--;
- codec_dai->capture_active--;
- }
-
- cpu_dai->active--;
- codec_dai->active--;
- codec->active--;
-
- /* Muting the DAC suppresses artifacts caused during digital
- * shutdown, for example from stopping clocks.
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dai_digital_mute(codec_dai, 1);
-
- if (cpu_dai->driver->ops->shutdown)
- cpu_dai->driver->ops->shutdown(substream, cpu_dai);
-
- if (codec_dai->driver->ops->shutdown)
- codec_dai->driver->ops->shutdown(substream, codec_dai);
-
- if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
- rtd->dai_link->ops->shutdown(substream);
-
- if (platform->driver->ops && platform->driver->ops->close)
- platform->driver->ops->close(substream);
- cpu_dai->runtime = NULL;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* start delayed pop wq here for playback streams */
- codec_dai->pop_wait = 1;
- schedule_delayed_work(&rtd->delayed_work,
- msecs_to_jiffies(rtd->pmdown_time));
- } else {
- /* capture streams can be powered down now */
- snd_soc_dapm_stream_event(rtd,
- codec_dai->driver->capture.stream_name,
- SND_SOC_DAPM_STREAM_STOP);
- }
-
- mutex_unlock(&pcm_mutex);
- return 0;
-}
-
-/*
- * Called by ALSA when the PCM substream is prepared, can set format, sample
- * rate, etc. This function is non atomic and can be called multiple times,
- * it can refer to the runtime info.
- */
-static int soc_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret = 0;
-
- mutex_lock(&pcm_mutex);
-
- if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
- ret = rtd->dai_link->ops->prepare(substream);
- if (ret < 0) {
- printk(KERN_ERR "asoc: machine prepare error\n");
- goto out;
- }
- }
-
- 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");
- goto out;
- }
- }
-
- if (codec_dai->driver->ops->prepare) {
- ret = codec_dai->driver->ops->prepare(substream, codec_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: codec DAI prepare error\n");
- goto out;
- }
- }
-
- if (cpu_dai->driver->ops->prepare) {
- ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: cpu DAI prepare error\n");
- goto out;
- }
- }
-
- /* cancel any delayed stream shutdown that is pending */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- codec_dai->pop_wait) {
- codec_dai->pop_wait = 0;
- cancel_delayed_work(&rtd->delayed_work);
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dapm_stream_event(rtd,
- codec_dai->driver->playback.stream_name,
- SND_SOC_DAPM_STREAM_START);
- else
- snd_soc_dapm_stream_event(rtd,
- codec_dai->driver->capture.stream_name,
- SND_SOC_DAPM_STREAM_START);
-
- snd_soc_dai_digital_mute(codec_dai, 0);
-
-out:
- mutex_unlock(&pcm_mutex);
- return ret;
-}
-
-/*
- * Called by ALSA when the hardware params are set by application. This
- * function can also be called multiple times and can allocate buffers
- * (using snd_pcm_lib_* ). It's non-atomic.
- */
-static int soc_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_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret = 0;
-
- mutex_lock(&pcm_mutex);
-
- if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
- ret = rtd->dai_link->ops->hw_params(substream, params);
- if (ret < 0) {
- printk(KERN_ERR "asoc: machine hw_params failed\n");
- goto out;
- }
- }
-
- if (codec_dai->driver->ops->hw_params) {
- ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't set codec %s hw params\n",
- codec_dai->name);
- goto codec_err;
- }
- }
-
- if (cpu_dai->driver->ops->hw_params) {
- ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: interface %s hw params failed\n",
- cpu_dai->name);
- goto interface_err;
- }
- }
-
- 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",
- platform->name);
- goto platform_err;
- }
- }
-
- rtd->rate = params_rate(params);
-
-out:
- mutex_unlock(&pcm_mutex);
- return ret;
-
-platform_err:
- if (cpu_dai->driver->ops->hw_free)
- cpu_dai->driver->ops->hw_free(substream, cpu_dai);
-
-interface_err:
- if (codec_dai->driver->ops->hw_free)
- codec_dai->driver->ops->hw_free(substream, codec_dai);
-
-codec_err:
- if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
- rtd->dai_link->ops->hw_free(substream);
-
- mutex_unlock(&pcm_mutex);
- return ret;
-}
-
-/*
- * Frees resources allocated by hw_params, can be called multiple times
- */
-static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = rtd->codec;
-
- mutex_lock(&pcm_mutex);
-
- /* apply codec digital mute */
- if (!codec->active)
- snd_soc_dai_digital_mute(codec_dai, 1);
-
- /* free any machine hw params */
- if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
- rtd->dai_link->ops->hw_free(substream);
-
- /* free any DMA resources */
- if (platform->driver->ops && platform->driver->ops->hw_free)
- platform->driver->ops->hw_free(substream);
-
- /* now free hw params for the DAIs */
- if (codec_dai->driver->ops->hw_free)
- codec_dai->driver->ops->hw_free(substream, codec_dai);
-
- if (cpu_dai->driver->ops->hw_free)
- cpu_dai->driver->ops->hw_free(substream, cpu_dai);
-
- mutex_unlock(&pcm_mutex);
- return 0;
-}
-
-static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- if (codec_dai->driver->ops->trigger) {
- ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
- if (ret < 0)
- return ret;
- }
-
- if (platform->driver->ops && platform->driver->ops->trigger) {
- ret = platform->driver->ops->trigger(substream, cmd);
- if (ret < 0)
- return ret;
- }
-
- if (cpu_dai->driver->ops->trigger) {
- ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
- if (ret < 0)
- return ret;
- }
- return 0;
-}
-
-/*
- * soc level wrapper for pointer callback
- * If cpu_dai, codec_dai, platform driver has the delay callback, than
- * the runtime->delay will be updated accordingly.
- */
-static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t offset = 0;
- snd_pcm_sframes_t delay = 0;
-
- if (platform->driver->ops && platform->driver->ops->pointer)
- offset = platform->driver->ops->pointer(substream);
-
- if (cpu_dai->driver->ops->delay)
- delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
-
- if (codec_dai->driver->ops->delay)
- delay += codec_dai->driver->ops->delay(substream, codec_dai);
-
- if (platform->driver->delay)
- delay += platform->driver->delay(substream, codec_dai);
-
- runtime->delay = delay;
-
- return offset;
-}
-
-/* ASoC PCM operations */
-static struct snd_pcm_ops soc_pcm_ops = {
- .open = soc_pcm_open,
- .close = soc_codec_close,
- .hw_params = soc_pcm_hw_params,
- .hw_free = soc_pcm_hw_free,
- .prepare = soc_pcm_prepare,
- .trigger = soc_pcm_trigger,
- .pointer = soc_pcm_pointer,
-};
-
#ifdef CONFIG_PM_SLEEP
/* powers down audio subsystem for suspend */
int snd_soc_suspend(struct device *dev)
@@ -1256,7 +709,7 @@ static void soc_resume_deferred(struct work_struct *work)
int snd_soc_resume(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
- int i;
+ int i, ac97_control = 0;
/* AC97 devices might have other drivers hanging off them so
* need to resume immediately. Other drivers don't have that
@@ -1265,14 +718,15 @@ int snd_soc_resume(struct device *dev)
*/
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- if (cpu_dai->driver->ac97_control) {
- dev_dbg(dev, "Resuming AC97 immediately\n");
- soc_resume_deferred(&card->deferred_resume_work);
- } else {
- dev_dbg(dev, "Scheduling resume work\n");
- if (!schedule_work(&card->deferred_resume_work))
- dev_err(dev, "resume work item may be lost\n");
- }
+ ac97_control |= cpu_dai->driver->ac97_control;
+ }
+ if (ac97_control) {
+ dev_dbg(dev, "Resuming AC97 immediately\n");
+ soc_resume_deferred(&card->deferred_resume_work);
+ } else {
+ dev_dbg(dev, "Scheduling resume work\n");
+ if (!schedule_work(&card->deferred_resume_work))
+ dev_err(dev, "resume work item may be lost\n");
}
return 0;
@@ -1393,7 +847,7 @@ static void soc_remove_codec(struct snd_soc_codec *codec)
module_put(codec->dev->driver->owner);
}
-static void soc_remove_dai_link(struct snd_soc_card *card, int num)
+static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_codec *codec = rtd->codec;
@@ -1410,7 +864,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the CODEC DAI */
- if (codec_dai && codec_dai->probed) {
+ if (codec_dai && codec_dai->probed &&
+ codec_dai->driver->remove_order == order) {
if (codec_dai->driver->remove) {
err = codec_dai->driver->remove(codec_dai);
if (err < 0)
@@ -1421,7 +876,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the platform */
- if (platform && platform->probed) {
+ if (platform && platform->probed &&
+ platform->driver->remove_order == order) {
if (platform->driver->remove) {
err = platform->driver->remove(platform);
if (err < 0)
@@ -1433,11 +889,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the CODEC */
- if (codec && codec->probed)
+ if (codec && codec->probed &&
+ codec->driver->remove_order == order)
soc_remove_codec(codec);
/* remove the cpu_dai */
- if (cpu_dai && cpu_dai->probed) {
+ if (cpu_dai && cpu_dai->probed &&
+ cpu_dai->driver->remove_order == order) {
if (cpu_dai->driver->remove) {
err = cpu_dai->driver->remove(cpu_dai);
if (err < 0)
@@ -1451,11 +909,13 @@ 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);
+ int dai, order;
+ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+ order++) {
+ for (dai = 0; dai < card->num_rtd; dai++)
+ soc_remove_dai_link(card, dai, order);
+ }
card->num_rtd = 0;
}
@@ -1526,6 +986,52 @@ err_probe:
return ret;
}
+static int soc_probe_platform(struct snd_soc_card *card,
+ struct snd_soc_platform *platform)
+{
+ int ret = 0;
+ const struct snd_soc_platform_driver *driver = platform->driver;
+
+ platform->card = card;
+ platform->dapm.card = card;
+
+ if (!try_module_get(platform->dev->driver->owner))
+ return -ENODEV;
+
+ if (driver->dapm_widgets)
+ snd_soc_dapm_new_controls(&platform->dapm,
+ driver->dapm_widgets, driver->num_dapm_widgets);
+
+ if (driver->probe) {
+ ret = driver->probe(platform);
+ if (ret < 0) {
+ dev_err(platform->dev,
+ "asoc: failed to probe platform %s: %d\n",
+ platform->name, ret);
+ goto err_probe;
+ }
+ }
+
+ if (driver->controls)
+ snd_soc_add_platform_controls(platform, driver->controls,
+ driver->num_controls);
+ if (driver->dapm_routes)
+ snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes,
+ driver->num_dapm_routes);
+
+ /* mark platform as probed and add to card platform list */
+ platform->probed = 1;
+ list_add(&platform->card_list, &card->platform_dev_list);
+ list_add(&platform->dapm.list, &card->dapm_list);
+
+ return 0;
+
+err_probe:
+ module_put(platform->dev->driver->owner);
+
+ return ret;
+}
+
static void rtd_release(struct device *dev) {}
static int soc_post_component_init(struct snd_soc_card *card,
@@ -1572,6 +1078,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
rtd->dev.parent = card->dev;
rtd->dev.release = rtd_release;
rtd->dev.init_name = name;
+ mutex_init(&rtd->pcm_mutex);
ret = device_register(&rtd->dev);
if (ret < 0) {
dev_err(card->dev,
@@ -1596,7 +1103,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
return 0;
}
-static int soc_probe_dai_link(struct snd_soc_card *card, int num)
+static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
@@ -1605,7 +1112,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
int ret;
- dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);
+ dev_dbg(card->dev, "probe %s dai link %d late %d\n",
+ card->name, num, order);
/* config components */
codec_dai->codec = codec;
@@ -1617,7 +1125,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
rtd->pmdown_time = pmdown_time;
/* probe the cpu_dai */
- if (!cpu_dai->probed) {
+ if (!cpu_dai->probed &&
+ cpu_dai->driver->probe_order == order) {
if (!try_module_get(cpu_dai->dev->driver->owner))
return -ENODEV;
@@ -1636,33 +1145,23 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
}
/* probe the CODEC */
- if (!codec->probed) {
+ if (!codec->probed &&
+ codec->driver->probe_order == order) {
ret = soc_probe_codec(card, codec);
if (ret < 0)
return ret;
}
/* probe the platform */
- if (!platform->probed) {
- if (!try_module_get(platform->dev->driver->owner))
- return -ENODEV;
-
- if (platform->driver->probe) {
- ret = platform->driver->probe(platform);
- if (ret < 0) {
- printk(KERN_ERR "asoc: failed to probe platform %s\n",
- platform->name);
- module_put(platform->dev->driver->owner);
- return ret;
- }
- }
- /* mark platform as probed and add to card platform list */
- platform->probed = 1;
- list_add(&platform->card_list, &card->platform_dev_list);
+ if (!platform->probed &&
+ platform->driver->probe_order == order) {
+ ret = soc_probe_platform(card, platform);
+ if (ret < 0)
+ return ret;
}
/* probe the CODEC DAI */
- if (!codec_dai->probed) {
+ if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
if (codec_dai->driver->probe) {
ret = codec_dai->driver->probe(codec_dai);
if (ret < 0) {
@@ -1677,8 +1176,9 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
list_add(&codec_dai->card_list, &card->dai_dev_list);
}
- /* DAPM dai link stream work */
- INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+ /* complete DAI probe during last probe */
+ if (order != SND_SOC_COMP_ORDER_LAST)
+ return 0;
ret = soc_post_component_init(card, codec, num, 0);
if (ret)
@@ -1817,7 +1317,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
struct snd_soc_codec *codec;
struct snd_soc_codec_conf *codec_conf;
enum snd_soc_compress_type compress_type;
- int ret, i;
+ int ret, i, order;
mutex_lock(&card->mutex);
@@ -1895,12 +1395,16 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
goto card_probe_error;
}
- for (i = 0; i < card->num_links; i++) {
- ret = soc_probe_dai_link(card, i);
- if (ret < 0) {
- pr_err("asoc: failed to instantiate card %s: %d\n",
+ /* early DAI link probe */
+ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+ order++) {
+ for (i = 0; i < card->num_links; i++) {
+ ret = soc_probe_dai_link(card, i, order);
+ if (ret < 0) {
+ pr_err("asoc: failed to instantiate card %s: %d\n",
card->name, ret);
- goto probe_dai_err;
+ goto probe_dai_err;
+ }
}
}
@@ -1929,8 +1433,9 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
"%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->driver_name)
+ strlcpy(card->snd_card->driver, card->driver_name,
+ sizeof(card->snd_card->driver));
if (card->late_probe) {
ret = card->late_probe(card);
@@ -2095,67 +1600,6 @@ static struct platform_driver soc_driver = {
.remove = soc_remove,
};
-/* create a new pcm */
-static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_pcm *pcm;
- char new_name[64];
- int ret = 0, playback = 0, capture = 0;
-
- /* check client and interface hw capabilities */
- snprintf(new_name, sizeof(new_name), "%s %s-%d",
- rtd->dai_link->stream_name, codec_dai->name, num);
-
- if (codec_dai->driver->playback.channels_min)
- playback = 1;
- if (codec_dai->driver->capture.channels_min)
- capture = 1;
-
- dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
- ret = snd_pcm_new(rtd->card->snd_card, new_name,
- num, playback, capture, &pcm);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
- return ret;
- }
-
- rtd->pcm = pcm;
- pcm->private_data = rtd;
- 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);
-
- if (capture)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
-
- 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;
- printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
- cpu_dai->name);
- return ret;
-}
-
/**
* snd_soc_codec_volatile_register: Report if a register is volatile.
*
@@ -2210,6 +1654,38 @@ int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register);
+int snd_soc_platform_read(struct snd_soc_platform *platform,
+ unsigned int reg)
+{
+ unsigned int ret;
+
+ if (!platform->driver->read) {
+ dev_err(platform->dev, "platform has no read back\n");
+ return -1;
+ }
+
+ ret = platform->driver->read(platform, reg);
+ dev_dbg(platform->dev, "read %x => %x\n", reg, ret);
+ trace_snd_soc_preg_read(platform, reg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_read);
+
+int snd_soc_platform_write(struct snd_soc_platform *platform,
+ unsigned int reg, unsigned int val)
+{
+ if (!platform->driver->write) {
+ dev_err(platform->dev, "platform has no write back\n");
+ return -1;
+ }
+
+ dev_dbg(platform->dev, "write %x = %x\n", reg, val);
+ trace_snd_soc_preg_write(platform, reg, val);
+ return platform->driver->write(platform, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_write);
+
/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @codec: audio codec
@@ -2322,7 +1798,7 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
return ret;
old = ret;
- new = (old & ~mask) | value;
+ new = (old & ~mask) | (value & mask);
change = old != new;
if (change) {
ret = snd_soc_write(codec, reg, new);
@@ -2489,6 +1965,36 @@ int snd_soc_add_controls(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(snd_soc_add_controls);
/**
+ * snd_soc_add_platform_controls - add an array of controls to a platform.
+ * Convienience function to add a list of controls.
+ *
+ * @platform: platform to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
+ const struct snd_kcontrol_new *controls, int num_controls)
+{
+ struct snd_card *card = platform->card->snd_card;
+ int err, i;
+
+ for (i = 0; i < num_controls; i++) {
+ const struct snd_kcontrol_new *control = &controls[i];
+ err = snd_ctl_add(card, snd_soc_cnew(control, platform,
+ control->name, NULL));
+ if (err < 0) {
+ dev_err(platform->dev, "Failed to add %s %d\n",control->name, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
+
+/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
@@ -3632,6 +3138,8 @@ int snd_soc_register_platform(struct device *dev,
platform->dev = dev;
platform->driver = platform_drv;
+ platform->dapm.dev = dev;
+ platform->dapm.platform = platform;
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 32ab7fc..fbfcda0 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -124,6 +124,51 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
}
+static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg)
+{
+ if (w->codec)
+ return snd_soc_read(w->codec, reg);
+ else if (w->platform)
+ return snd_soc_platform_read(w->platform, reg);
+
+ dev_err(w->dapm->dev, "no valid widget read method\n");
+ return -1;
+}
+
+static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
+{
+ if (w->codec)
+ return snd_soc_write(w->codec, reg, val);
+ else if (w->platform)
+ return snd_soc_platform_write(w->platform, reg, val);
+
+ dev_err(w->dapm->dev, "no valid widget write method\n");
+ return -1;
+}
+
+static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
+ unsigned short reg, unsigned int mask, unsigned int value)
+{
+ int change;
+ unsigned int old, new;
+ int ret;
+
+ ret = soc_widget_read(w, reg);
+ if (ret < 0)
+ return ret;
+
+ old = ret;
+ new = (old & ~mask) | (value & mask);
+ change = old != new;
+ if (change) {
+ ret = soc_widget_write(w, reg, new);
+ if (ret < 0)
+ return ret;
+ }
+
+ return change;
+}
+
/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
* @dapm: DAPM context
@@ -139,39 +184,26 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
struct snd_soc_card *card = dapm->card;
int ret = 0;
- switch (level) {
- case SND_SOC_BIAS_ON:
- dev_dbg(dapm->dev, "Setting full bias\n");
- break;
- case SND_SOC_BIAS_PREPARE:
- dev_dbg(dapm->dev, "Setting bias prepare\n");
- break;
- case SND_SOC_BIAS_STANDBY:
- dev_dbg(dapm->dev, "Setting standby bias\n");
- break;
- case SND_SOC_BIAS_OFF:
- dev_dbg(dapm->dev, "Setting bias off\n");
- break;
- default:
- dev_err(dapm->dev, "Setting invalid bias %d\n", level);
- return -EINVAL;
- }
-
trace_snd_soc_bias_level_start(card, level);
if (card && card->set_bias_level)
- ret = card->set_bias_level(card, level);
- if (ret == 0) {
- if (dapm->codec && dapm->codec->driver->set_bias_level)
- ret = dapm->codec->driver->set_bias_level(dapm->codec, level);
+ ret = card->set_bias_level(card, dapm, level);
+ if (ret != 0)
+ goto out;
+
+ if (dapm->codec) {
+ if (dapm->codec->driver->set_bias_level)
+ ret = dapm->codec->driver->set_bias_level(dapm->codec,
+ level);
else
dapm->bias_level = level;
}
- if (ret == 0) {
- if (card && card->set_bias_level_post)
- ret = card->set_bias_level_post(card, level);
- }
+ if (ret != 0)
+ goto out;
+ if (card && card->set_bias_level_post)
+ ret = card->set_bias_level_post(card, dapm, level);
+out:
trace_snd_soc_bias_level_done(card, level);
return ret;
@@ -194,7 +226,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- val = snd_soc_read(w->codec, reg);
+ val = soc_widget_read(w, reg);
val = (val >> shift) & mask;
if ((invert && !val) || (!invert && val))
@@ -209,8 +241,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
int val, item, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
- ;
- val = snd_soc_read(w->codec, e->reg);
+ ;
+ val = soc_widget_read(w, e->reg);
item = (val >> e->shift_l) & (bitmask - 1);
p->connect = 0;
@@ -240,7 +272,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
w->kcontrol_news[i].private_value;
int val, item;
- val = snd_soc_read(w->codec, e->reg);
+ val = soc_widget_read(w, e->reg);
val = (val >> e->shift_l) & e->mask;
for (item = 0; item < e->max; item++) {
if (val == e->values[item])
@@ -606,6 +638,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
}
list_for_each_entry(path, &widget->sinks, list_source) {
+ if (path->weak)
+ continue;
+
if (path->walked)
continue;
@@ -656,6 +691,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
}
list_for_each_entry(path, &widget->sources, list_sink) {
+ if (path->weak)
+ continue;
+
if (path->walked)
continue;
@@ -681,7 +719,7 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
else
val = w->off_val;
- snd_soc_update_bits(w->codec, -(w->reg + 1),
+ soc_widget_update_bits(w, -(w->reg + 1),
w->mask << w->shift, val << w->shift);
return 0;
@@ -737,6 +775,9 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
/* Check if one of our outputs is connected */
list_for_each_entry(path, &w->sinks, list_source) {
+ if (path->weak)
+ continue;
+
if (path->connected &&
!path->connected(path->source, path->sink))
continue;
@@ -885,11 +926,17 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
}
if (reg >= 0) {
+ /* Any widget will do, they should all be updating the
+ * same register.
+ */
+ w = list_first_entry(pending, struct snd_soc_dapm_widget,
+ power_list);
+
pop_dbg(dapm->dev, card->pop_time,
"pop test : Applying 0x%x/0x%x to %x in %dms\n",
value, mask, reg, card->pop_time);
pop_wait(card->pop_time);
- snd_soc_update_bits(dapm->codec, reg, mask, value);
+ soc_widget_update_bits(w, reg, mask, value);
}
list_for_each_entry(w, pending, power_list) {
@@ -942,7 +989,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&pending);
cur_sort = -1;
- cur_subseq = -1;
+ cur_subseq = INT_MIN;
cur_reg = SND_SOC_NOPM;
cur_dapm = NULL;
}
@@ -1041,16 +1088,17 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
struct snd_soc_dapm_context *d = data;
int ret;
- if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
+ /* If we're off and we're not supposed to be go into STANDBY */
+ if (d->bias_level == SND_SOC_BIAS_OFF &&
+ d->target_bias_level != SND_SOC_BIAS_OFF) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
if (ret != 0)
dev_err(d->dev,
"Failed to turn on bias: %d\n", ret);
}
- /* If we're changing to all on or all off then prepare */
- if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
- (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
+ /* Prepare for a STADDBY->ON or ON->STANDBY transition */
+ if (d->bias_level != d->target_bias_level) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);
if (ret != 0)
dev_err(d->dev,
@@ -1067,7 +1115,9 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
int ret;
/* If we just powered the last thing off drop to standby bias */
- if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
+ if (d->bias_level == SND_SOC_BIAS_PREPARE &&
+ (d->target_bias_level == SND_SOC_BIAS_STANDBY ||
+ d->target_bias_level == SND_SOC_BIAS_OFF)) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
if (ret != 0)
dev_err(d->dev, "Failed to apply standby bias: %d\n",
@@ -1075,14 +1125,16 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
}
/* If we're in standby and can support bias off then do that */
- if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) {
+ if (d->bias_level == SND_SOC_BIAS_STANDBY &&
+ d->target_bias_level == SND_SOC_BIAS_OFF) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
if (ret != 0)
dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
}
/* If we just powered up then move to active bias */
- if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
+ if (d->bias_level == SND_SOC_BIAS_PREPARE &&
+ d->target_bias_level == SND_SOC_BIAS_ON) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);
if (ret != 0)
dev_err(d->dev, "Failed to apply active bias: %d\n",
@@ -1107,13 +1159,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
LIST_HEAD(up_list);
LIST_HEAD(down_list);
LIST_HEAD(async_domain);
+ enum snd_soc_bias_level bias;
int power;
trace_snd_soc_dapm_start(card);
- list_for_each_entry(d, &card->dapm_list, list)
- if (d->n_widgets || d->codec == NULL)
- d->dev_power = 0;
+ list_for_each_entry(d, &card->dapm_list, list) {
+ if (d->n_widgets || d->codec == NULL) {
+ if (d->idle_bias_off)
+ d->target_bias_level = SND_SOC_BIAS_OFF;
+ else
+ d->target_bias_level = SND_SOC_BIAS_STANDBY;
+ }
+ }
/* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down.
@@ -1135,8 +1193,27 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
power = w->power_check(w);
else
power = 1;
- if (power)
- w->dapm->dev_power = 1;
+
+ if (power) {
+ d = w->dapm;
+
+ /* Supplies and micbiases only bring
+ * the context up to STANDBY as unless
+ * something else is active and
+ * passing audio they generally don't
+ * require full power.
+ */
+ switch (w->id) {
+ case snd_soc_dapm_supply:
+ case snd_soc_dapm_micbias:
+ if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
+ d->target_bias_level = SND_SOC_BIAS_STANDBY;
+ break;
+ default:
+ d->target_bias_level = SND_SOC_BIAS_ON;
+ break;
+ }
+ }
if (w->power == power)
continue;
@@ -1160,24 +1237,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
switch (event) {
case SND_SOC_DAPM_STREAM_START:
case SND_SOC_DAPM_STREAM_RESUME:
- dapm->dev_power = 1;
+ dapm->target_bias_level = SND_SOC_BIAS_ON;
break;
case SND_SOC_DAPM_STREAM_STOP:
- dapm->dev_power = !!dapm->codec->active;
+ if (dapm->codec->active)
+ dapm->target_bias_level = SND_SOC_BIAS_ON;
+ else
+ dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
break;
case SND_SOC_DAPM_STREAM_SUSPEND:
- dapm->dev_power = 0;
+ dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
break;
case SND_SOC_DAPM_STREAM_NOP:
- switch (dapm->bias_level) {
- case SND_SOC_BIAS_STANDBY:
- case SND_SOC_BIAS_OFF:
- dapm->dev_power = 0;
- break;
- default:
- dapm->dev_power = 1;
- break;
- }
+ dapm->target_bias_level = dapm->bias_level;
break;
default:
break;
@@ -1185,12 +1257,12 @@ 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;
+ bias = SND_SOC_BIAS_OFF;
list_for_each_entry(d, &card->dapm_list, list)
- if (d->dev_power)
- power = 1;
+ if (d->target_bias_level > bias)
+ bias = d->target_bias_level;
list_for_each_entry(d, &card->dapm_list, list)
- d->dev_power = power;
+ d->target_bias_level = bias;
/* Run all the bias changes in parallel */
@@ -1794,6 +1866,84 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
+static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_route *route)
+{
+ struct snd_soc_dapm_widget *source = dapm_find_widget(dapm,
+ route->source,
+ true);
+ struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm,
+ route->sink,
+ true);
+ struct snd_soc_dapm_path *path;
+ int count = 0;
+
+ if (!source) {
+ dev_err(dapm->dev, "Unable to find source %s for weak route\n",
+ route->source);
+ return -ENODEV;
+ }
+
+ if (!sink) {
+ dev_err(dapm->dev, "Unable to find sink %s for weak route\n",
+ route->sink);
+ return -ENODEV;
+ }
+
+ if (route->control || route->connected)
+ dev_warn(dapm->dev, "Ignoring control for weak route %s->%s\n",
+ route->source, route->sink);
+
+ list_for_each_entry(path, &source->sinks, list_source) {
+ if (path->sink == sink) {
+ path->weak = 1;
+ count++;
+ }
+ }
+
+ if (count == 0)
+ dev_err(dapm->dev, "No path found for weak route %s->%s\n",
+ route->source, route->sink);
+ if (count > 1)
+ dev_warn(dapm->dev, "%d paths found for weak route %s->%s\n",
+ count, route->source, route->sink);
+
+ return 0;
+}
+
+/**
+ * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak
+ * @dapm: DAPM context
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Mark existing routes matching those specified in the passed array
+ * as being weak, meaning that they are ignored for the purpose of
+ * power decisions. The main intended use case is for sidetone paths
+ * which couple audio between other independent paths if they are both
+ * active in order to make the combination work better at the user
+ * level but which aren't intended to be "used".
+ *
+ * Note that CODEC drivers should not use this as sidetone type paths
+ * can frequently also be used as bypass paths.
+ */
+int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_route *route, int num)
+{
+ int i, err;
+ int ret = 0;
+
+ for (i = 0; i < num; i++) {
+ err = snd_soc_dapm_weak_route(dapm, route);
+ if (err)
+ ret = err;
+ route++;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
+
/**
* snd_soc_dapm_new_widgets - add new dapm widgets
* @dapm: DAPM context
@@ -1865,7 +2015,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
/* Read the initial power state from the device */
if (w->reg >= 0) {
- val = snd_soc_read(w->codec, w->reg);
+ val = soc_widget_read(w, w->reg);
val &= 1 << w->shift;
if (w->invert)
val = !val;
@@ -2353,6 +2503,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
dapm->n_widgets++;
w->dapm = dapm;
w->codec = dapm->codec;
+ w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c
new file mode 100644
index 0000000..cca490c
--- /dev/null
+++ b/sound/soc/soc-io.c
@@ -0,0 +1,396 @@
+/*
+ * soc-io.c -- ASoC register I/O helpers
+ *
+ * Copyright 2009-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/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include <trace/events/asoc.h>
+
+#ifdef CONFIG_SPI_MASTER
+static int do_spi_write(void *control, const char *data, int len)
+{
+ struct spi_device *spi = control;
+ int ret;
+
+ ret = spi_write(spi, data, len);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+#endif
+
+static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value, const void *data, int len)
+{
+ int ret;
+
+ 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, len);
+ if (ret == len)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static unsigned int 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;
+
+ 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;
+}
+
+static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 data;
+
+ data = cpu_to_be16((reg << 12) | (value & 0xffffff));
+
+ return do_hw_write(codec, reg, value, &data, 2);
+}
+
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 data;
+
+ data = cpu_to_be16((reg << 9) | (value & 0x1ff));
+
+ return do_hw_write(codec, reg, value, &data, 2);
+}
+
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ reg &= 0xff;
+ data[0] = reg;
+ data[1] = value & 0xff;
+
+ return do_hw_write(codec, reg, value, data, 2);
+}
+
+static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+ u16 val = cpu_to_be16(value);
+
+ data[0] = reg;
+ memcpy(&data[1], &val, sizeof(val));
+
+ return do_hw_write(codec, reg, value, data, 3);
+}
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int do_i2c_read(struct snd_soc_codec *codec,
+ void *reg, int reglen,
+ void *data, int datalen)
+{
+ struct i2c_msg xfer[2];
+ int ret;
+ struct i2c_client *client = codec->control_data;
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = reglen;
+ xfer[0].buf = reg;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = datalen;
+ xfer[1].buf = data;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ 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
+#define snd_soc_8_8_read_i2c NULL
+#endif
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
+ unsigned int r)
+{
+ u8 reg = r;
+ u16 data;
+ int ret;
+
+ ret = do_i2c_read(codec, &reg, 1, &data, 2);
+ if (ret < 0)
+ return 0;
+ return (data >> 8) | ((data & 0xff) << 8);
+}
+#else
+#define snd_soc_8_16_read_i2c NULL
+#endif
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
+ unsigned int r)
+{
+ u16 reg = r;
+ u8 data;
+ int ret;
+
+ ret = do_i2c_read(codec, &reg, 2, &data, 1);
+ if (ret < 0)
+ return 0;
+ return data;
+}
+#else
+#define snd_soc_16_8_read_i2c NULL
+#endif
+
+static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+ u16 rval = cpu_to_be16(reg);
+
+ memcpy(data, &rval, sizeof(rval));
+ data[2] = value;
+
+ return do_hw_write(codec, reg, value, data, 3);
+}
+
+#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)
+{
+ u16 reg = cpu_to_be16(r);
+ u16 data;
+ int ret;
+
+ ret = do_i2c_read(codec, &reg, 2, &data, 2);
+ if (ret < 0)
+ return 0;
+ return be16_to_cpu(data);
+}
+#else
+#define snd_soc_16_16_read_i2c NULL
+#endif
+
+static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 data[2];
+
+ data[0] = cpu_to_be16(reg);
+ data[1] = cpu_to_be16(value);
+
+ return do_hw_write(codec, reg, value, data, sizeof(data));
+}
+
+/* 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(to_i2c_client(codec->dev), data, len);
+ break;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ case SND_SOC_SPI:
+ ret = spi_write(to_spi_device(codec->dev), data, len);
+ break;
+#endif
+ default:
+ BUG();
+ }
+
+ if (ret == len)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static struct {
+ int addr_bits;
+ int data_bits;
+ int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned 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,
+ },
+ {
+ .addr_bits = 7, .data_bits = 9,
+ .write = snd_soc_7_9_write,
+ },
+ {
+ .addr_bits = 8, .data_bits = 8,
+ .write = snd_soc_8_8_write,
+ .i2c_read = snd_soc_8_8_read_i2c,
+ },
+ {
+ .addr_bits = 8, .data_bits = 16,
+ .write = snd_soc_8_16_write,
+ .i2c_read = snd_soc_8_16_read_i2c,
+ },
+ {
+ .addr_bits = 16, .data_bits = 8,
+ .write = snd_soc_16_8_write,
+ .i2c_read = snd_soc_16_8_read_i2c,
+ },
+ {
+ .addr_bits = 16, .data_bits = 16,
+ .write = snd_soc_16_16_write,
+ .i2c_read = snd_soc_16_16_read_i2c,
+ },
+};
+
+/**
+ * snd_soc_codec_set_cache_io: Set up standard I/O functions.
+ *
+ * @codec: CODEC to configure.
+ * @addr_bits: Number of bits of register address data.
+ * @data_bits: Number of bits of data per register.
+ * @control: Control bus used.
+ *
+ * Register formats are frequently shared between many I2C and SPI
+ * devices. In order to promote code reuse the ASoC core provides
+ * some standard implementations of CODEC read and write operations
+ * which can be set up using this function.
+ *
+ * The caller is responsible for allocating and initialising the
+ * actual cache.
+ *
+ * Note that at present this code cannot be used by CODECs with
+ * volatile registers.
+ */
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+ int addr_bits, int data_bits,
+ enum snd_soc_control_type control)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(io_types); i++)
+ if (io_types[i].addr_bits == addr_bits &&
+ io_types[i].data_bits == data_bits)
+ break;
+ if (i == ARRAY_SIZE(io_types)) {
+ printk(KERN_ERR
+ "No I/O functions for %d bit address %d bit data\n",
+ addr_bits, data_bits);
+ return -EINVAL;
+ }
+
+ codec->write = io_types[i].write;
+ codec->read = hw_read;
+ codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
+
+ switch (control) {
+ case SND_SOC_I2C:
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+ codec->hw_write = (hw_write_t)i2c_master_send;
+#endif
+ if (io_types[i].i2c_read)
+ codec->hw_read = io_types[i].i2c_read;
+
+ codec->control_data = container_of(codec->dev,
+ struct i2c_client,
+ dev);
+ break;
+
+ case SND_SOC_SPI:
+#ifdef CONFIG_SPI_MASTER
+ codec->hw_write = do_spi_write;
+#endif
+
+ codec->control_data = container_of(codec->dev,
+ struct spi_device,
+ dev);
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
+
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
new file mode 100644
index 0000000..b575939
--- /dev/null
+++ b/sound/soc/soc-pcm.c
@@ -0,0 +1,639 @@
+/*
+ * soc-pcm.c -- ALSA SoC PCM
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Authors: Liam Girdwood <lrg@ti.com>
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+static DEFINE_MUTEX(pcm_mutex);
+
+static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
+{
+ 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;
+
+ if (!codec_dai->driver->symmetric_rates &&
+ !cpu_dai->driver->symmetric_rates &&
+ !rtd->dai_link->symmetric_rates)
+ return 0;
+
+ /* This can happen if multiple streams are starting simultaneously -
+ * the second can need to get its constraints before the first has
+ * picked a rate. Complain and allow the application to carry on.
+ */
+ if (!rtd->rate) {
+ dev_warn(&rtd->dev,
+ "Not enforcing symmetric_rates due to race\n");
+ return 0;
+ }
+
+ dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
+
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ rtd->rate, rtd->rate);
+ if (ret < 0) {
+ dev_err(&rtd->dev,
+ "Unable to apply rate symmetry constraint: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Called by ALSA when a PCM substream is opened, the runtime->hw record is
+ * then initialized and any private data can be allocated. This also calls
+ * startup for the cpu DAI, platform, machine and codec DAI.
+ */
+static int soc_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
+ struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
+ int ret = 0;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ /* startup the audio subsystem */
+ if (cpu_dai->driver->ops->startup) {
+ ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't open interface %s\n",
+ cpu_dai->name);
+ goto out;
+ }
+ }
+
+ 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);
+ goto platform_err;
+ }
+ }
+
+ if (codec_dai->driver->ops->startup) {
+ ret = codec_dai->driver->ops->startup(substream, codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't open codec %s\n",
+ codec_dai->name);
+ goto codec_dai_err;
+ }
+ }
+
+ if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
+ ret = rtd->dai_link->ops->startup(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
+ goto machine_err;
+ }
+ }
+
+ /* Check that the codec and cpu DAIs are compatible */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw.rate_min =
+ max(codec_dai_drv->playback.rate_min,
+ cpu_dai_drv->playback.rate_min);
+ runtime->hw.rate_max =
+ min(codec_dai_drv->playback.rate_max,
+ cpu_dai_drv->playback.rate_max);
+ runtime->hw.channels_min =
+ max(codec_dai_drv->playback.channels_min,
+ cpu_dai_drv->playback.channels_min);
+ runtime->hw.channels_max =
+ min(codec_dai_drv->playback.channels_max,
+ cpu_dai_drv->playback.channels_max);
+ runtime->hw.formats =
+ codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
+ runtime->hw.rates =
+ codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
+ if (codec_dai_drv->playback.rates
+ & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+ runtime->hw.rates |= cpu_dai_drv->playback.rates;
+ if (cpu_dai_drv->playback.rates
+ & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+ runtime->hw.rates |= codec_dai_drv->playback.rates;
+ } else {
+ runtime->hw.rate_min =
+ max(codec_dai_drv->capture.rate_min,
+ cpu_dai_drv->capture.rate_min);
+ runtime->hw.rate_max =
+ min(codec_dai_drv->capture.rate_max,
+ cpu_dai_drv->capture.rate_max);
+ runtime->hw.channels_min =
+ max(codec_dai_drv->capture.channels_min,
+ cpu_dai_drv->capture.channels_min);
+ runtime->hw.channels_max =
+ min(codec_dai_drv->capture.channels_max,
+ cpu_dai_drv->capture.channels_max);
+ runtime->hw.formats =
+ codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
+ runtime->hw.rates =
+ codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
+ if (codec_dai_drv->capture.rates
+ & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+ runtime->hw.rates |= cpu_dai_drv->capture.rates;
+ if (cpu_dai_drv->capture.rates
+ & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+ runtime->hw.rates |= codec_dai_drv->capture.rates;
+ }
+
+ ret = -EINVAL;
+ snd_pcm_limit_hw_rates(runtime);
+ if (!runtime->hw.rates) {
+ printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
+ codec_dai->name, cpu_dai->name);
+ goto config_err;
+ }
+ if (!runtime->hw.formats) {
+ printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
+ codec_dai->name, cpu_dai->name);
+ goto config_err;
+ }
+ if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
+ runtime->hw.channels_min > runtime->hw.channels_max) {
+ printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
+ codec_dai->name, cpu_dai->name);
+ goto config_err;
+ }
+
+ /* Symmetry only applies if we've already got an active stream. */
+ if (cpu_dai->active || codec_dai->active) {
+ ret = soc_pcm_apply_symmetry(substream);
+ if (ret != 0)
+ goto config_err;
+ }
+
+ pr_debug("asoc: %s <-> %s info:\n",
+ codec_dai->name, cpu_dai->name);
+ pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
+ pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
+ runtime->hw.channels_max);
+ pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
+ runtime->hw.rate_max);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ cpu_dai->playback_active++;
+ codec_dai->playback_active++;
+ } else {
+ cpu_dai->capture_active++;
+ codec_dai->capture_active++;
+ }
+ cpu_dai->active++;
+ codec_dai->active++;
+ rtd->codec->active++;
+ mutex_unlock(&rtd->pcm_mutex);
+ return 0;
+
+config_err:
+ if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
+ rtd->dai_link->ops->shutdown(substream);
+
+machine_err:
+ if (codec_dai->driver->ops->shutdown)
+ codec_dai->driver->ops->shutdown(substream, codec_dai);
+
+codec_dai_err:
+ if (platform->driver->ops && platform->driver->ops->close)
+ platform->driver->ops->close(substream);
+
+platform_err:
+ if (cpu_dai->driver->ops->shutdown)
+ cpu_dai->driver->ops->shutdown(substream, cpu_dai);
+out:
+ mutex_unlock(&rtd->pcm_mutex);
+ return ret;
+}
+
+/*
+ * Power down the audio subsystem pmdown_time msecs after close is called.
+ * This is to ensure there are no pops or clicks in between any music tracks
+ * due to DAPM power cycling.
+ */
+static void close_delayed_work(struct work_struct *work)
+{
+ struct snd_soc_pcm_runtime *rtd =
+ container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ pr_debug("pop wq checking: %s status: %s waiting: %s\n",
+ codec_dai->driver->playback.stream_name,
+ codec_dai->playback_active ? "active" : "inactive",
+ codec_dai->pop_wait ? "yes" : "no");
+
+ /* are we waiting on this codec DAI stream */
+ if (codec_dai->pop_wait == 1) {
+ codec_dai->pop_wait = 0;
+ snd_soc_dapm_stream_event(rtd,
+ codec_dai->driver->playback.stream_name,
+ SND_SOC_DAPM_STREAM_STOP);
+ }
+
+ mutex_unlock(&rtd->pcm_mutex);
+}
+
+/*
+ * Called by ALSA when a PCM substream is closed. Private data can be
+ * freed here. The cpu DAI, codec DAI, machine and platform are also
+ * shutdown.
+ */
+static int soc_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ cpu_dai->playback_active--;
+ codec_dai->playback_active--;
+ } else {
+ cpu_dai->capture_active--;
+ codec_dai->capture_active--;
+ }
+
+ cpu_dai->active--;
+ codec_dai->active--;
+ codec->active--;
+
+ /* Muting the DAC suppresses artifacts caused during digital
+ * shutdown, for example from stopping clocks.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dai_digital_mute(codec_dai, 1);
+
+ if (cpu_dai->driver->ops->shutdown)
+ cpu_dai->driver->ops->shutdown(substream, cpu_dai);
+
+ if (codec_dai->driver->ops->shutdown)
+ codec_dai->driver->ops->shutdown(substream, codec_dai);
+
+ if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
+ rtd->dai_link->ops->shutdown(substream);
+
+ if (platform->driver->ops && platform->driver->ops->close)
+ platform->driver->ops->close(substream);
+ cpu_dai->runtime = NULL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* start delayed pop wq here for playback streams */
+ codec_dai->pop_wait = 1;
+ schedule_delayed_work(&rtd->delayed_work,
+ msecs_to_jiffies(rtd->pmdown_time));
+ } else {
+ /* capture streams can be powered down now */
+ snd_soc_dapm_stream_event(rtd,
+ codec_dai->driver->capture.stream_name,
+ SND_SOC_DAPM_STREAM_STOP);
+ }
+
+ mutex_unlock(&rtd->pcm_mutex);
+ return 0;
+}
+
+/*
+ * Called by ALSA when the PCM substream is prepared, can set format, sample
+ * rate, etc. This function is non atomic and can be called multiple times,
+ * it can refer to the runtime info.
+ */
+static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret = 0;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
+ ret = rtd->dai_link->ops->prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: machine prepare error\n");
+ goto out;
+ }
+ }
+
+ 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");
+ goto out;
+ }
+ }
+
+ if (codec_dai->driver->ops->prepare) {
+ ret = codec_dai->driver->ops->prepare(substream, codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: codec DAI prepare error\n");
+ goto out;
+ }
+ }
+
+ if (cpu_dai->driver->ops->prepare) {
+ ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: cpu DAI prepare error\n");
+ goto out;
+ }
+ }
+
+ /* cancel any delayed stream shutdown that is pending */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ codec_dai->pop_wait) {
+ codec_dai->pop_wait = 0;
+ cancel_delayed_work(&rtd->delayed_work);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_stream_event(rtd,
+ codec_dai->driver->playback.stream_name,
+ SND_SOC_DAPM_STREAM_START);
+ else
+ snd_soc_dapm_stream_event(rtd,
+ codec_dai->driver->capture.stream_name,
+ SND_SOC_DAPM_STREAM_START);
+
+ snd_soc_dai_digital_mute(codec_dai, 0);
+
+out:
+ mutex_unlock(&rtd->pcm_mutex);
+ return ret;
+}
+
+/*
+ * Called by ALSA when the hardware params are set by application. This
+ * function can also be called multiple times and can allocate buffers
+ * (using snd_pcm_lib_* ). It's non-atomic.
+ */
+static int soc_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_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret = 0;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
+ ret = rtd->dai_link->ops->hw_params(substream, params);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: machine hw_params failed\n");
+ goto out;
+ }
+ }
+
+ if (codec_dai->driver->ops->hw_params) {
+ ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't set codec %s hw params\n",
+ codec_dai->name);
+ goto codec_err;
+ }
+ }
+
+ if (cpu_dai->driver->ops->hw_params) {
+ ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: interface %s hw params failed\n",
+ cpu_dai->name);
+ goto interface_err;
+ }
+ }
+
+ 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",
+ platform->name);
+ goto platform_err;
+ }
+ }
+
+ rtd->rate = params_rate(params);
+
+out:
+ mutex_unlock(&rtd->pcm_mutex);
+ return ret;
+
+platform_err:
+ if (cpu_dai->driver->ops->hw_free)
+ cpu_dai->driver->ops->hw_free(substream, cpu_dai);
+
+interface_err:
+ if (codec_dai->driver->ops->hw_free)
+ codec_dai->driver->ops->hw_free(substream, codec_dai);
+
+codec_err:
+ if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
+ rtd->dai_link->ops->hw_free(substream);
+
+ mutex_unlock(&rtd->pcm_mutex);
+ return ret;
+}
+
+/*
+ * Frees resources allocated by hw_params, can be called multiple times
+ */
+static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ /* apply codec digital mute */
+ if (!codec->active)
+ snd_soc_dai_digital_mute(codec_dai, 1);
+
+ /* free any machine hw params */
+ if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
+ rtd->dai_link->ops->hw_free(substream);
+
+ /* free any DMA resources */
+ if (platform->driver->ops && platform->driver->ops->hw_free)
+ platform->driver->ops->hw_free(substream);
+
+ /* now free hw params for the DAIs */
+ if (codec_dai->driver->ops->hw_free)
+ codec_dai->driver->ops->hw_free(substream, codec_dai);
+
+ if (cpu_dai->driver->ops->hw_free)
+ cpu_dai->driver->ops->hw_free(substream, cpu_dai);
+
+ mutex_unlock(&rtd->pcm_mutex);
+ return 0;
+}
+
+static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ if (codec_dai->driver->ops->trigger) {
+ ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (platform->driver->ops && platform->driver->ops->trigger) {
+ ret = platform->driver->ops->trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (cpu_dai->driver->ops->trigger) {
+ ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * soc level wrapper for pointer callback
+ * If cpu_dai, codec_dai, platform driver has the delay callback, than
+ * the runtime->delay will be updated accordingly.
+ */
+static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t offset = 0;
+ snd_pcm_sframes_t delay = 0;
+
+ if (platform->driver->ops && platform->driver->ops->pointer)
+ offset = platform->driver->ops->pointer(substream);
+
+ if (cpu_dai->driver->ops->delay)
+ delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
+
+ if (codec_dai->driver->ops->delay)
+ delay += codec_dai->driver->ops->delay(substream, codec_dai);
+
+ if (platform->driver->delay)
+ delay += platform->driver->delay(substream, codec_dai);
+
+ runtime->delay = delay;
+
+ return offset;
+}
+
+/* ASoC PCM operations */
+static struct snd_pcm_ops soc_pcm_ops = {
+ .open = soc_pcm_open,
+ .close = soc_pcm_close,
+ .hw_params = soc_pcm_hw_params,
+ .hw_free = soc_pcm_hw_free,
+ .prepare = soc_pcm_prepare,
+ .trigger = soc_pcm_trigger,
+ .pointer = soc_pcm_pointer,
+};
+
+/* create a new pcm */
+int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_pcm *pcm;
+ char new_name[64];
+ int ret = 0, playback = 0, capture = 0;
+
+ /* check client and interface hw capabilities */
+ snprintf(new_name, sizeof(new_name), "%s %s-%d",
+ rtd->dai_link->stream_name, codec_dai->name, num);
+
+ if (codec_dai->driver->playback.channels_min)
+ playback = 1;
+ if (codec_dai->driver->capture.channels_min)
+ capture = 1;
+
+ dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
+ ret = snd_pcm_new(rtd->card->snd_card, new_name,
+ num, playback, capture, &pcm);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+ return ret;
+ }
+
+ /* DAPM dai link stream work */
+ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+
+ rtd->pcm = pcm;
+ pcm->private_data = rtd;
+ 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);
+
+ if (capture)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
+
+ if (platform->driver->pcm_new) {
+ ret = platform->driver->pcm_new(rtd);
+ if (ret < 0) {
+ pr_err("asoc: platform pcm constructor failed\n");
+ return ret;
+ }
+ }
+
+ pcm->private_free = platform->driver->pcm_free;
+ printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
+ cpu_dai->name);
+ return ret;
+}
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 035d39a..c6af1fd 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -12,6 +12,15 @@ config SND_SOC_TEGRA_I2S
Tegra I2S interface. You will also need to select the individual
machine drivers to support below.
+config SND_SOC_TEGRA_SPDIF
+ tristate
+ depends on SND_SOC_TEGRA
+ default m
+ help
+ Say Y or M if you want to add support for the SPDIF interface.
+ You will also need to select the individual machine drivers to support
+ below.
+
config MACH_HAS_SND_SOC_TEGRA_WM8903
bool
help
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index fa6574d..4d943b3 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -2,12 +2,14 @@
snd-soc-tegra-das-objs := tegra_das.o
snd-soc-tegra-pcm-objs := tegra_pcm.o
snd-soc-tegra-i2s-objs := tegra_i2s.o
+snd-soc-tegra-spdif-objs := tegra_spdif.o
snd-soc-tegra-utils-objs += tegra_asoc_utils.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
+obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o
# Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
index 6b817e2..f36b996 100644
--- a/sound/soc/tegra/tegra_i2s.c
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -222,12 +222,18 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
if (i2sclock % (2 * srate))
reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
+ if (!i2s->clk_refs)
+ clk_enable(i2s->clk_i2s);
+
tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
+ if (!i2s->clk_refs)
+ clk_disable(i2s->clk_i2s);
+
return 0;
}
@@ -348,7 +354,6 @@ struct snd_soc_dai_driver tegra_i2s_dai[] = {
static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
{
struct tegra_i2s * i2s;
- char clk_name[12]; /* tegra-i2s.0 */
struct resource *mem, *memregion, *dmareq;
int ret;
@@ -383,8 +388,7 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
}
dev_set_drvdata(&pdev->dev, i2s);
- snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id);
- i2s->clk_i2s = clk_get_sys(clk_name, NULL);
+ i2s->clk_i2s = clk_get(&pdev->dev, NULL);
if (IS_ERR(i2s->clk_i2s)) {
dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
ret = PTR_ERR(i2s->clk_i2s);
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 3c271f9..ff86e5e 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -322,9 +322,11 @@ static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
static u64 tegra_dma_mask = DMA_BIT_MASK(32);
-static int tegra_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c
new file mode 100644
index 0000000..abe606b
--- /dev/null
+++ b/sound/soc/tegra/tegra_spdif.c
@@ -0,0 +1,371 @@
+/*
+ * tegra_spdif.c - Tegra SPDIF driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 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 <linux/clk.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_spdif.h"
+
+#define DRV_NAME "tegra-spdif"
+
+static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg,
+ u32 val)
+{
+ __raw_writel(val, spdif->regs + reg);
+}
+
+static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg)
+{
+ return __raw_readl(spdif->regs + reg);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_spdif_show(struct seq_file *s, void *unused)
+{
+#define REG(r) { r, #r }
+ static const struct {
+ int offset;
+ const char *name;
+ } regs[] = {
+ REG(TEGRA_SPDIF_CTRL),
+ REG(TEGRA_SPDIF_STATUS),
+ REG(TEGRA_SPDIF_STROBE_CTRL),
+ REG(TEGRA_SPDIF_DATA_FIFO_CSR),
+ REG(TEGRA_SPDIF_CH_STA_RX_A),
+ REG(TEGRA_SPDIF_CH_STA_RX_B),
+ REG(TEGRA_SPDIF_CH_STA_RX_C),
+ REG(TEGRA_SPDIF_CH_STA_RX_D),
+ REG(TEGRA_SPDIF_CH_STA_RX_E),
+ REG(TEGRA_SPDIF_CH_STA_RX_F),
+ REG(TEGRA_SPDIF_CH_STA_TX_A),
+ REG(TEGRA_SPDIF_CH_STA_TX_B),
+ REG(TEGRA_SPDIF_CH_STA_TX_C),
+ REG(TEGRA_SPDIF_CH_STA_TX_D),
+ REG(TEGRA_SPDIF_CH_STA_TX_E),
+ REG(TEGRA_SPDIF_CH_STA_TX_F),
+ };
+#undef REG
+
+ struct tegra_spdif *spdif = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ u32 val = tegra_spdif_read(spdif, regs[i].offset);
+ seq_printf(s, "%s = %08x\n", regs[i].name, val);
+ }
+
+ return 0;
+}
+
+static int tegra_spdif_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tegra_spdif_show, inode->i_private);
+}
+
+static const struct file_operations tegra_spdif_debug_fops = {
+ .open = tegra_spdif_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void tegra_spdif_debug_add(struct tegra_spdif *spdif)
+{
+ spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
+ snd_soc_debugfs_root, spdif,
+ &tegra_spdif_debug_fops);
+}
+
+static void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
+{
+ if (spdif->debug)
+ debugfs_remove(spdif->debug);
+}
+#else
+static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif)
+{
+}
+
+static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
+{
+}
+#endif
+
+static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = substream->pcm->card->dev;
+ struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret, srate, spdifclock;
+
+ spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK;
+ spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK;
+ spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ srate = params_rate(params);
+ switch (params_rate(params)) {
+ case 32000:
+ spdifclock = 4096000;
+ break;
+ case 44100:
+ spdifclock = 5644800;
+ break;
+ case 48000:
+ spdifclock = 6144000;
+ break;
+ case 88200:
+ spdifclock = 11289600;
+ break;
+ case 96000:
+ spdifclock = 12288000;
+ break;
+ case 176400:
+ spdifclock = 22579200;
+ break;
+ case 192000:
+ spdifclock = 24576000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
+ if (ret) {
+ dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void tegra_spdif_start_playback(struct tegra_spdif *spdif)
+{
+ spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN;
+ tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
+}
+
+static void tegra_spdif_stop_playback(struct tegra_spdif *spdif)
+{
+ spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN;
+ tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
+}
+
+static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (!spdif->clk_refs)
+ clk_enable(spdif->clk_spdif_out);
+ spdif->clk_refs++;
+ tegra_spdif_start_playback(spdif);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tegra_spdif_stop_playback(spdif);
+ spdif->clk_refs--;
+ if (!spdif->clk_refs)
+ clk_disable(spdif->clk_spdif_out);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra_spdif_probe(struct snd_soc_dai *dai)
+{
+ struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ dai->capture_dma_data = NULL;
+ dai->playback_dma_data = &spdif->playback_dma_data;
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra_spdif_dai_ops = {
+ .hw_params = tegra_spdif_hw_params,
+ .trigger = tegra_spdif_trigger,
+};
+
+struct snd_soc_dai_driver tegra_spdif_dai = {
+ .name = DRV_NAME,
+ .probe = tegra_spdif_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra_spdif_dai_ops,
+};
+
+static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev)
+{
+ struct tegra_spdif *spdif;
+ struct resource *mem, *memregion, *dmareq;
+ int ret;
+
+ spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL);
+ if (!spdif) {
+ dev_err(&pdev->dev, "Can't allocate tegra_spdif\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
+ if (IS_ERR(spdif->clk_spdif_out)) {
+ pr_err("Can't retrieve spdif clock\n");
+ ret = PTR_ERR(spdif->clk_spdif_out);
+ goto err_free;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmareq) {
+ dev_err(&pdev->dev, "No DMA resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ memregion = request_mem_region(mem->start, resource_size(mem),
+ DRV_NAME);
+ if (!memregion) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ ret = -EBUSY;
+ goto err_clk_put;
+ }
+
+ spdif->regs = ioremap(mem->start, resource_size(mem));
+ if (!spdif->regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT;
+ spdif->playback_dma_data.wrap = 4;
+ spdif->playback_dma_data.width = 32;
+ spdif->playback_dma_data.req_sel = dmareq->start;
+
+ ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ tegra_spdif_debug_add(spdif);
+
+ return 0;
+
+err_unmap:
+ iounmap(spdif->regs);
+err_release:
+ release_mem_region(mem->start, resource_size(mem));
+err_clk_put:
+ clk_put(spdif->clk_spdif_out);
+err_free:
+ kfree(spdif);
+exit:
+ return ret;
+}
+
+static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev)
+{
+ struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ snd_soc_unregister_dai(&pdev->dev);
+
+ tegra_spdif_debug_remove(spdif);
+
+ iounmap(spdif->regs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ clk_put(spdif->clk_spdif_out);
+
+ kfree(spdif);
+
+ return 0;
+}
+
+static struct platform_driver tegra_spdif_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_spdif_platform_probe,
+ .remove = __devexit_p(tegra_spdif_platform_remove),
+};
+
+static int __init snd_tegra_spdif_init(void)
+{
+ return platform_driver_register(&tegra_spdif_driver);
+}
+module_init(snd_tegra_spdif_init);
+
+static void __exit snd_tegra_spdif_exit(void)
+{
+ platform_driver_unregister(&tegra_spdif_driver);
+}
+module_exit(snd_tegra_spdif_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra SPDIF ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_spdif.h b/sound/soc/tegra/tegra_spdif.h
new file mode 100644
index 0000000..2e03db4
--- /dev/null
+++ b/sound/soc/tegra/tegra_spdif.h
@@ -0,0 +1,473 @@
+/*
+ * tegra_spdif.h - Definitions for Tegra SPDIF driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2011 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ * Copyright (c) 2008-2009, NVIDIA Corporation
+ *
+ * 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
+ *
+ */
+
+#ifndef __TEGRA_SPDIF_H__
+#define __TEGRA_SPDIF_H__
+
+#include "tegra_pcm.h"
+
+/* Offsets from TEGRA_SPDIF_BASE */
+
+#define TEGRA_SPDIF_CTRL 0x0
+#define TEGRA_SPDIF_STATUS 0x4
+#define TEGRA_SPDIF_STROBE_CTRL 0x8
+#define TEGRA_SPDIF_DATA_FIFO_CSR 0x0C
+#define TEGRA_SPDIF_DATA_OUT 0x40
+#define TEGRA_SPDIF_DATA_IN 0x80
+#define TEGRA_SPDIF_CH_STA_RX_A 0x100
+#define TEGRA_SPDIF_CH_STA_RX_B 0x104
+#define TEGRA_SPDIF_CH_STA_RX_C 0x108
+#define TEGRA_SPDIF_CH_STA_RX_D 0x10C
+#define TEGRA_SPDIF_CH_STA_RX_E 0x110
+#define TEGRA_SPDIF_CH_STA_RX_F 0x114
+#define TEGRA_SPDIF_CH_STA_TX_A 0x140
+#define TEGRA_SPDIF_CH_STA_TX_B 0x144
+#define TEGRA_SPDIF_CH_STA_TX_C 0x148
+#define TEGRA_SPDIF_CH_STA_TX_D 0x14C
+#define TEGRA_SPDIF_CH_STA_TX_E 0x150
+#define TEGRA_SPDIF_CH_STA_TX_F 0x154
+#define TEGRA_SPDIF_USR_STA_RX_A 0x180
+#define TEGRA_SPDIF_USR_DAT_TX_A 0x1C0
+
+/* Fields in TEGRA_SPDIF_CTRL */
+
+/* Start capturing from 0=right, 1=left channel */
+#define TEGRA_SPDIF_CTRL_CAP_LC (1 << 30)
+
+/* SPDIF receiver(RX) enable */
+#define TEGRA_SPDIF_CTRL_RX_EN (1 << 29)
+
+/* SPDIF Transmitter(TX) enable */
+#define TEGRA_SPDIF_CTRL_TX_EN (1 << 28)
+
+/* Transmit Channel status */
+#define TEGRA_SPDIF_CTRL_TC_EN (1 << 27)
+
+/* Transmit user Data */
+#define TEGRA_SPDIF_CTRL_TU_EN (1 << 26)
+
+/* Interrupt on transmit error */
+#define TEGRA_SPDIF_CTRL_IE_TXE (1 << 25)
+
+/* Interrupt on receive error */
+#define TEGRA_SPDIF_CTRL_IE_RXE (1 << 24)
+
+/* Interrupt on invalid preamble */
+#define TEGRA_SPDIF_CTRL_IE_P (1 << 23)
+
+/* Interrupt on "B" preamble */
+#define TEGRA_SPDIF_CTRL_IE_B (1 << 22)
+
+/* Interrupt when block of channel status received */
+#define TEGRA_SPDIF_CTRL_IE_C (1 << 21)
+
+/* Interrupt when a valid information unit (IU) is received */
+#define TEGRA_SPDIF_CTRL_IE_U (1 << 20)
+
+/* Interrupt when RX user FIFO attention level is reached */
+#define TEGRA_SPDIF_CTRL_QE_RU (1 << 19)
+
+/* Interrupt when TX user FIFO attention level is reached */
+#define TEGRA_SPDIF_CTRL_QE_TU (1 << 18)
+
+/* Interrupt when RX data FIFO attention level is reached */
+#define TEGRA_SPDIF_CTRL_QE_RX (1 << 17)
+
+/* Interrupt when TX data FIFO attention level is reached */
+#define TEGRA_SPDIF_CTRL_QE_TX (1 << 16)
+
+/* Loopback test mode enable */
+#define TEGRA_SPDIF_CTRL_LBK_EN (1 << 15)
+
+/*
+ * Pack data mode:
+ * 0 = Single data (16 bit needs to be padded to match the
+ * interface data bit size).
+ * 1 = Packeted left/right channel data into a single word.
+ */
+#define TEGRA_SPDIF_CTRL_PACK (1 << 14)
+
+/*
+ * 00 = 16bit data
+ * 01 = 20bit data
+ * 10 = 24bit data
+ * 11 = raw data
+ */
+#define TEGRA_SPDIF_BIT_MODE_16BIT 0
+#define TEGRA_SPDIF_BIT_MODE_20BIT 1
+#define TEGRA_SPDIF_BIT_MODE_24BIT 2
+#define TEGRA_SPDIF_BIT_MODE_RAW 3
+
+#define TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT 12
+#define TEGRA_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA_SPDIF_BIT_MODE_16BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA_SPDIF_BIT_MODE_20BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA_SPDIF_BIT_MODE_24BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA_SPDIF_CTRL_BIT_MODE_RAW (TEGRA_SPDIF_BIT_MODE_RAW << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
+
+/* Fields in TEGRA_SPDIF_STATUS */
+
+/*
+ * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must
+ * write a 1 to the corresponding bit location to clear the status.
+ */
+
+/*
+ * Receiver(RX) shifter is busy receiving data.
+ * This bit is asserted when the receiver first locked onto the
+ * preamble of the data stream after RX_EN is asserted. This bit is
+ * deasserted when either,
+ * (a) the end of a frame is reached after RX_EN is deeasserted, or
+ * (b) the SPDIF data stream becomes inactive.
+ */
+#define TEGRA_SPDIF_STATUS_RX_BSY (1 << 29)
+
+/*
+ * Transmitter(TX) shifter is busy transmitting data.
+ * This bit is asserted when TX_EN is asserted.
+ * This bit is deasserted when the end of a frame is reached after
+ * TX_EN is deasserted.
+ */
+#define TEGRA_SPDIF_STATUS_TX_BSY (1 << 28)
+
+/*
+ * TX is busy shifting out channel status.
+ * This bit is asserted when both TX_EN and TC_EN are asserted and
+ * data from CH_STA_TX_A register is loaded into the internal shifter.
+ * This bit is deasserted when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) CH_STA_TX_F register is loaded into the internal shifter.
+ */
+#define TEGRA_SPDIF_STATUS_TC_BSY (1 << 27)
+
+/*
+ * TX User data FIFO busy.
+ * This bit is asserted when TX_EN and TXU_EN are asserted and
+ * there's data in the TX user FIFO. This bit is deassert when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) there's no data left in the TX user FIFO.
+ */
+#define TEGRA_SPDIF_STATUS_TU_BSY (1 << 26)
+
+/* TX FIFO Underrun error status */
+#define TEGRA_SPDIF_STATUS_TX_ERR (1 << 25)
+
+/* RX FIFO Overrun error status */
+#define TEGRA_SPDIF_STATUS_RX_ERR (1 << 24)
+
+/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */
+#define TEGRA_SPDIF_STATUS_IS_P (1 << 23)
+
+/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
+#define TEGRA_SPDIF_STATUS_IS_B (1 << 22)
+
+/*
+ * RX channel block data receive status:
+ * 0=entire block not recieved yet.
+ * 1=received entire block of channel status,
+ */
+#define TEGRA_SPDIF_STATUS_IS_C (1 << 21)
+
+/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */
+#define TEGRA_SPDIF_STATUS_IS_U (1 << 20)
+
+/*
+ * RX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA_SPDIF_STATUS_QS_RU (1 << 19)
+
+/*
+ * TX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA_SPDIF_STATUS_QS_TU (1 << 18)
+
+/*
+ * RX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA_SPDIF_STATUS_QS_RX (1 << 17)
+
+/*
+ * TX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA_SPDIF_STATUS_QS_TX (1 << 16)
+
+/* Fields in TEGRA_SPDIF_STROBE_CTRL */
+
+/*
+ * Indicates the approximate number of detected SPDIFIN clocks within a
+ * bi-phase period.
+ */
+#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16
+#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT)
+
+/* Data strobe mode: 0=Auto-locked 1=Manual locked */
+#define TEGRA_SPDIF_STROBE_CTRL_STROBE (1 << 15)
+
+/*
+ * Manual data strobe time within the bi-phase clock period (in terms of
+ * the number of over-sampling clocks).
+ */
+#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8
+#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT)
+
+/*
+ * Manual SPDIFIN bi-phase clock period (in terms of the number of
+ * over-sampling clocks).
+ */
+#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0
+#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT)
+
+/* Fields in SPDIF_DATA_FIFO_CSR */
+
+/* Clear Receiver User FIFO (RX USR.FIFO) */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31)
+
+#define TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0
+#define TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1
+#define TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2
+#define TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3
+
+/* RU FIFO attention level */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \
+ (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+
+/* Number of RX USR.FIFO levels with valid data. */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter User FIFO (TX USR.FIFO) */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23)
+
+/* TU FIFO attention level */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \
+ (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+
+/* Number of TX USR.FIFO levels that could be filled. */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT)
+
+/* Clear Receiver Data FIFO (RX DATA.FIFO) */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15)
+
+#define TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0
+#define TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1
+#define TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2
+#define TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3
+
+/* RU FIFO attention level */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \
+ (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+
+/* Number of RX DATA.FIFO levels with valid data. */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8
+#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7)
+
+/* TU FIFO attention level */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \
+ (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \
+ (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+
+/* Number of TX DATA.FIFO levels that could be filled. */
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0
+#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT)
+
+/* Fields in TEGRA_SPDIF_DATA_OUT */
+
+/*
+ * This register has 5 different formats:
+ * 16-bit (BIT_MODE=00, PACK=0)
+ * 20-bit (BIT_MODE=01, PACK=0)
+ * 24-bit (BIT_MODE=10, PACK=0)
+ * raw (BIT_MODE=11, PACK=0)
+ * 16-bit packed (BIT_MODE=00, PACK=1)
+ */
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT 0
+#define TEGRA_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT 0
+#define TEGRA_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT 0
+#define TEGRA_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31)
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30)
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29)
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0
+#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16
+#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT)
+
+#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0
+#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT)
+
+/* Fields in TEGRA_SPDIF_DATA_IN */
+
+/*
+ * This register has 5 different formats:
+ * 16-bit (BIT_MODE=00, PACK=0)
+ * 20-bit (BIT_MODE=01, PACK=0)
+ * 24-bit (BIT_MODE=10, PACK=0)
+ * raw (BIT_MODE=11, PACK=0)
+ * 16-bit packed (BIT_MODE=00, PACK=1)
+ *
+ * Bits 31:24 are common to all modes except 16-bit packed
+ */
+
+#define TEGRA_SPDIF_DATA_IN_DATA_P (1 << 31)
+#define TEGRA_SPDIF_DATA_IN_DATA_C (1 << 30)
+#define TEGRA_SPDIF_DATA_IN_DATA_U (1 << 29)
+#define TEGRA_SPDIF_DATA_IN_DATA_V (1 << 28)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24
+#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT 0
+#define TEGRA_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT 0
+#define TEGRA_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT 0
+#define TEGRA_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8
+#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4
+#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0
+#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16
+#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT)
+
+#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0
+#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT)
+
+/* Fields in TEGRA_SPDIF_CH_STA_RX_A */
+/* Fields in TEGRA_SPDIF_CH_STA_RX_B */
+/* Fields in TEGRA_SPDIF_CH_STA_RX_C */
+/* Fields in TEGRA_SPDIF_CH_STA_RX_D */
+/* Fields in TEGRA_SPDIF_CH_STA_RX_E */
+/* Fields in TEGRA_SPDIF_CH_STA_RX_F */
+
+/*
+ * The 6-word receive channel data page buffer holds a block (192 frames) of
+ * channel status information. The order of receive is from LSB to MSB
+ * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A.
+ */
+
+/* Fields in TEGRA_SPDIF_CH_STA_TX_A */
+/* Fields in TEGRA_SPDIF_CH_STA_TX_B */
+/* Fields in TEGRA_SPDIF_CH_STA_TX_C */
+/* Fields in TEGRA_SPDIF_CH_STA_TX_D */
+/* Fields in TEGRA_SPDIF_CH_STA_TX_E */
+/* Fields in TEGRA_SPDIF_CH_STA_TX_F */
+
+/*
+ * The 6-word transmit channel data page buffer holds a block (192 frames) of
+ * channel status information. The order of transmission is from LSB to MSB
+ * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A.
+ */
+
+/* Fields in TEGRA_SPDIF_USR_STA_RX_A */
+
+/*
+ * This 4-word deep FIFO receives user FIFO field information. The order of
+ * receive is from LSB to MSB bit.
+ */
+
+/* Fields in TEGRA_SPDIF_USR_DAT_TX_A */
+
+/*
+ * This 4-word deep FIFO transmits user FIFO field information. The order of
+ * transmission is from LSB to MSB bit.
+ */
+
+struct tegra_spdif {
+ struct clk *clk_spdif_out;
+ int clk_refs;
+ struct tegra_pcm_dma_params capture_dma_data;
+ struct tegra_pcm_dma_params playback_dma_data;
+ void __iomem *regs;
+ struct dentry *debug;
+ u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 0d6738a..a42e9ac 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -267,7 +267,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
}
machine->gpio_requested |= GPIO_HP_MUTE;
- gpio_direction_output(pdata->gpio_hp_mute, 0);
+ gpio_direction_output(pdata->gpio_hp_mute, 1);
}
if (gpio_is_valid(pdata->gpio_int_mic_en)) {
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index f4aa4e0..34aa972 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -288,9 +288,10 @@ static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
-static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
struct platform_device *pdev = to_platform_device(dai->platform->dev);
struct txx9aclc_soc_device *dev;
struct resource *r;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 220c616..781d9e6 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -433,9 +433,10 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
* only at the first time. the successive calls of this function will
* append the pcm interface to the corresponding card.
*/
-static void *snd_usb_audio_probe(struct usb_device *dev,
- struct usb_interface *intf,
- const struct usb_device_id *usb_id)
+static struct snd_usb_audio *
+snd_usb_audio_probe(struct usb_device *dev,
+ struct usb_interface *intf,
+ const struct usb_device_id *usb_id)
{
const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info;
int i, err;
@@ -540,16 +541,15 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
* we need to take care of counter, since disconnection can be called also
* many times as well as usb_audio_probe().
*/
-static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
+static void snd_usb_audio_disconnect(struct usb_device *dev,
+ struct snd_usb_audio *chip)
{
- struct snd_usb_audio *chip;
struct snd_card *card;
struct list_head *p;
- if (ptr == (void *)-1L)
+ if (chip == (void *)-1L)
return;
- chip = ptr;
card = chip->card;
mutex_lock(&register_mutex);
mutex_lock(&chip->shutdown_mutex);
@@ -585,7 +585,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
static int usb_audio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- void *chip;
+ struct snd_usb_audio *chip;
chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
if (chip) {
usb_set_intfdata(intf, chip);
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index b0ef9f5..7c0d21e 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -408,6 +408,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
/* doesn't set the sample rate attribute, but supports it */
fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
break;
+ case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */
+ case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
an older model 77d:223) */
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index fb5d68f..67bec76 100644
--- a/sound/usb/misc/ua101.c
+++ b/sound/usb/misc/ua101.c
@@ -645,7 +645,7 @@ static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream,
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
1500000 / ua->packets_per_second,
- 8192000);
+ UINT_MAX);
if (err < 0)
return err;
err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24);
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 0b2ae8e..dba0b7f 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -1677,6 +1677,36 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ USB_DEVICE(0x0582, 0x011e),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "BOSS", */
+ /* .product_name = "BR-800", */
+ .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_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 090e193..77762c9 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -369,6 +369,30 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
return 0;
}
+static int snd_usb_fasttrackpro_boot_quirk(struct usb_device *dev)
+{
+ int err;
+
+ if (dev->actconfig->desc.bConfigurationValue == 1) {
+ snd_printk(KERN_INFO "usb-audio: "
+ "Fast Track Pro switching to config #2\n");
+ /* This function has to be available by the usb core module.
+ * if it is not avialable the boot quirk has to be left out
+ * and the configuration has to be set by udev or hotplug
+ * rules
+ */
+ err = usb_driver_set_configuration(dev, 2);
+ if (err < 0) {
+ snd_printdd("error usb_driver_set_configuration: %d\n",
+ err);
+ return -ENODEV;
+ }
+ } else
+ snd_printk(KERN_INFO "usb-audio: Fast Track Pro config OK\n");
+
+ return 0;
+}
+
/*
* C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
* documented in the device's data sheet.
@@ -471,16 +495,49 @@ static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev)
/*
* Setup quirks
*/
-#define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */
-#define AUDIOPHILE_SET_DTS 0x02 /* if set, enable DTS Digital Output */
-#define AUDIOPHILE_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
-#define AUDIOPHILE_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */
-#define AUDIOPHILE_SET_DI 0x10 /* if set, enable Digital Input */
-#define AUDIOPHILE_SET_MASK 0x1F /* bit mask for setup value */
-#define AUDIOPHILE_SET_24B_48K_DI 0x19 /* value for 24bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_24B_48K_NOTDI 0x09 /* value for 24bits+48KHz+No Digital Input */
-#define AUDIOPHILE_SET_16B_48K_DI 0x11 /* value for 16bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_16B_48K_NOTDI 0x01 /* value for 16bits+48KHz+No Digital Input */
+#define MAUDIO_SET 0x01 /* parse device_setup */
+#define MAUDIO_SET_COMPATIBLE 0x80 /* use only "win-compatible" interfaces */
+#define MAUDIO_SET_DTS 0x02 /* enable DTS Digital Output */
+#define MAUDIO_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
+#define MAUDIO_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */
+#define MAUDIO_SET_DI 0x10 /* enable Digital Input */
+#define MAUDIO_SET_MASK 0x1f /* bit mask for setup value */
+#define MAUDIO_SET_24B_48K_DI 0x19 /* 24bits+48KHz+Digital Input */
+#define MAUDIO_SET_24B_48K_NOTDI 0x09 /* 24bits+48KHz+No Digital Input */
+#define MAUDIO_SET_16B_48K_DI 0x11 /* 16bits+48KHz+Digital Input */
+#define MAUDIO_SET_16B_48K_NOTDI 0x01 /* 16bits+48KHz+No Digital Input */
+
+static int quattro_skip_setting_quirk(struct snd_usb_audio *chip,
+ int iface, int altno)
+{
+ /* Reset ALL ifaces to 0 altsetting.
+ * Call it for every possible altsetting of every interface.
+ */
+ usb_set_interface(chip->dev, iface, 0);
+ if (chip->setup & MAUDIO_SET) {
+ if (chip->setup & MAUDIO_SET_COMPATIBLE) {
+ if (iface != 1 && iface != 2)
+ return 1; /* skip all interfaces but 1 and 2 */
+ } else {
+ unsigned int mask;
+ if (iface == 1 || iface == 2)
+ return 1; /* skip interfaces 1 and 2 */
+ if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
+ return 1; /* skip this altsetting */
+ mask = chip->setup & MAUDIO_SET_MASK;
+ if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
+ return 1; /* skip this altsetting */
+ if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
+ return 1; /* skip this altsetting */
+ if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 4)
+ return 1; /* skip this altsetting */
+ }
+ }
+ snd_printdd(KERN_INFO
+ "using altsetting %d for interface %d config %d\n",
+ altno, iface, chip->setup);
+ return 0; /* keep this altsetting */
+}
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
int iface,
@@ -491,30 +548,65 @@ static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
*/
usb_set_interface(chip->dev, iface, 0);
- if (chip->setup & AUDIOPHILE_SET) {
- if ((chip->setup & AUDIOPHILE_SET_DTS)
- && altno != 6)
+ if (chip->setup & MAUDIO_SET) {
+ unsigned int mask;
+ if ((chip->setup & MAUDIO_SET_DTS) && altno != 6)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_96K)
- && altno != 1)
+ if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_24B_48K_DI && altno != 2)
+ mask = chip->setup & MAUDIO_SET_MASK;
+ if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3)
+ if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_16B_48K_DI && altno != 4)
+ if (mask == MAUDIO_SET_16B_48K_DI && altno != 4)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5)
+ if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 5)
return 1; /* skip this altsetting */
}
return 0; /* keep this altsetting */
}
+
+static int fasttrackpro_skip_setting_quirk(struct snd_usb_audio *chip,
+ int iface, int altno)
+{
+ /* Reset ALL ifaces to 0 altsetting.
+ * Call it for every possible altsetting of every interface.
+ */
+ usb_set_interface(chip->dev, iface, 0);
+
+ /* possible configuration where both inputs and only one output is
+ *used is not supported by the current setup
+ */
+ if (chip->setup & (MAUDIO_SET | MAUDIO_SET_24B)) {
+ if (chip->setup & MAUDIO_SET_96K) {
+ if (altno != 3 && altno != 6)
+ return 1;
+ } else if (chip->setup & MAUDIO_SET_DI) {
+ if (iface == 4)
+ return 1; /* no analog input */
+ if (altno != 2 && altno != 5)
+ return 1; /* enable only altsets 2 and 5 */
+ } else {
+ if (iface == 5)
+ return 1; /* disable digialt input */
+ if (altno != 2 && altno != 5)
+ return 1; /* enalbe only altsets 2 and 5 */
+ }
+ } else {
+ /* keep only 16-Bit mode */
+ if (altno != 1)
+ return 1;
+ }
+
+ snd_printdd(KERN_INFO
+ "using altsetting %d for interface %d config %d\n",
+ altno, iface, chip->setup);
+ return 0; /* keep this altsetting */
+}
+
int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int iface,
int altno)
@@ -522,6 +614,12 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
/* audiophile usb: skip altsets incompatible with device_setup */
if (chip->usb_id == USB_ID(0x0763, 0x2003))
return audiophile_skip_setting_quirk(chip, iface, altno);
+ /* quattro usb: skip altsets incompatible with device_setup */
+ if (chip->usb_id == USB_ID(0x0763, 0x2001))
+ return quattro_skip_setting_quirk(chip, iface, altno);
+ /* fasttrackpro usb: skip altsets incompatible with device_setup */
+ if (chip->usb_id == USB_ID(0x0763, 0x2012))
+ return fasttrackpro_skip_setting_quirk(chip, iface, altno);
return 0;
}
@@ -560,6 +658,8 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
return snd_usb_nativeinstruments_boot_quirk(dev);
+ case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
+ return snd_usb_fasttrackpro_boot_quirk(dev);
}
return 0;
@@ -570,15 +670,24 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
*/
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
{
+ /* it depends on altsetting wether the device is big-endian or not */
switch (chip->usb_id) {
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
- if (fp->endpoint & USB_DIR_IN)
+ if (fp->altsetting == 2 || fp->altsetting == 3 ||
+ fp->altsetting == 5 || fp->altsetting == 6)
return 1;
break;
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
if (chip->setup == 0x00 ||
- fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
+ fp->altsetting == 1 || fp->altsetting == 2 ||
+ fp->altsetting == 3)
+ return 1;
+ break;
+ case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro */
+ if (fp->altsetting == 2 || fp->altsetting == 3 ||
+ fp->altsetting == 5 || fp->altsetting == 6)
return 1;
+ break;
}
return 0;
}
OpenPOWER on IntegriCloud