summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/arm/pxa2xx-ac97.c26
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c52
-rw-r--r--sound/arm/pxa2xx-pcm.c5
-rw-r--r--sound/arm/pxa2xx-pcm.h6
-rw-r--r--sound/core/Kconfig3
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/pcm_dmaengine.c (renamed from sound/soc/soc-dmaengine-pcm.c)0
-rw-r--r--sound/core/pcm_lib.c4
-rw-r--r--sound/drivers/dummy.c2
-rw-r--r--sound/firewire/speakers.c4
-rw-r--r--sound/isa/gus/interwave.c3
-rw-r--r--sound/oss/dmabuf.c3
-rw-r--r--sound/pci/hda/Kconfig9
-rw-r--r--sound/pci/hda/hda_codec.c64
-rw-r--r--sound/pci/hda/hda_codec.h21
-rw-r--r--sound/pci/hda/hda_generic.c79
-rw-r--r--sound/pci/hda/hda_generic.h1
-rw-r--r--sound/pci/hda/hda_hwdep.c6
-rw-r--r--sound/pci/hda/hda_intel.c34
-rw-r--r--sound/pci/hda/hda_jack.c22
-rw-r--r--sound/pci/hda/hda_jack.h13
-rw-r--r--sound/pci/hda/hda_proc.c33
-rw-r--r--sound/pci/hda/patch_analog.c4528
-rw-r--r--sound/pci/hda/patch_conexant.c79
-rw-r--r--sound/pci/hda/patch_hdmi.c9
-rw-r--r--sound/pci/hda/patch_realtek.c190
-rw-r--r--sound/pci/hda/patch_sigmatel.c14
-rw-r--r--sound/pci/hda/patch_via.c2
-rw-r--r--sound/pci/rme96.c307
-rw-r--r--sound/pci/rme9652/hdspm.c779
-rw-r--r--sound/soc/Kconfig5
-rw-r--r--sound/soc/Makefile4
-rw-r--r--sound/soc/atmel/Kconfig21
-rw-r--r--sound/soc/atmel/Makefile4
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c118
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c36
-rw-r--r--sound/soc/atmel/atmel_wm8904.c254
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c208
-rw-r--r--sound/soc/au1x/db1200.c4
-rw-r--r--sound/soc/au1x/psc-ac97.c3
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h1
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c3
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c5
-rw-r--r--sound/soc/codecs/Kconfig19
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/ac97.c15
-rw-r--r--sound/soc/codecs/ad1980.c43
-rw-r--r--sound/soc/codecs/ad73311.c22
-rw-r--r--sound/soc/codecs/adau1701.c25
-rw-r--r--sound/soc/codecs/adav80x.c13
-rw-r--r--sound/soc/codecs/ads117x.c29
-rw-r--r--sound/soc/codecs/ak4104.c34
-rw-r--r--sound/soc/codecs/ak4554.c106
-rw-r--r--sound/soc/codecs/ak5386.c17
-rw-r--r--sound/soc/codecs/arizona.c69
-rw-r--r--sound/soc/codecs/arizona.h5
-rw-r--r--sound/soc/codecs/bt-sco.c22
-rw-r--r--sound/soc/codecs/cs4270.c20
-rw-r--r--sound/soc/codecs/cs4271.c30
-rw-r--r--sound/soc/codecs/dmic.c17
-rw-r--r--sound/soc/codecs/hdmi.c30
-rw-r--r--sound/soc/codecs/lm4857.c107
-rw-r--r--sound/soc/codecs/max9768.c16
-rw-r--r--sound/soc/codecs/max98090.c10
-rw-r--r--sound/soc/codecs/max9877.c294
-rw-r--r--sound/soc/codecs/mc13783.c1
-rw-r--r--sound/soc/codecs/pcm1681.c339
-rw-r--r--sound/soc/codecs/pcm1792a.c257
-rw-r--r--sound/soc/codecs/pcm1792a.h26
-rw-r--r--sound/soc/codecs/pcm3008.c150
-rw-r--r--sound/soc/codecs/rt5640.c235
-rw-r--r--sound/soc/codecs/rt5640.h12
-rw-r--r--sound/soc/codecs/sgtl5000.c10
-rw-r--r--sound/soc/codecs/si476x.c14
-rw-r--r--sound/soc/codecs/spdif_receiver.c17
-rw-r--r--sound/soc/codecs/spdif_transmitter.c18
-rw-r--r--sound/soc/codecs/sta32x.c10
-rw-r--r--sound/soc/codecs/tlv320aic26.c51
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c22
-rw-r--r--sound/soc/codecs/tlv320aic3x.c56
-rw-r--r--sound/soc/codecs/twl4030.c2
-rw-r--r--sound/soc/codecs/twl6040.c7
-rw-r--r--sound/soc/codecs/uda134x.c88
-rw-r--r--sound/soc/codecs/wl1273.c17
-rw-r--r--sound/soc/codecs/wm0010.c12
-rw-r--r--sound/soc/codecs/wm5102.c53
-rw-r--r--sound/soc/codecs/wm5110.c35
-rw-r--r--sound/soc/codecs/wm8350.c6
-rw-r--r--sound/soc/codecs/wm8727.c17
-rw-r--r--sound/soc/codecs/wm8731.c60
-rw-r--r--sound/soc/codecs/wm8753.c5
-rw-r--r--sound/soc/codecs/wm8782.c17
-rw-r--r--sound/soc/codecs/wm8903.c4
-rw-r--r--sound/soc/codecs/wm8904.c3
-rw-r--r--sound/soc/codecs/wm8960.c10
-rw-r--r--sound/soc/codecs/wm8962.c9
-rw-r--r--sound/soc/codecs/wm8994.c39
-rw-r--r--sound/soc/codecs/wm8995.c5
-rw-r--r--sound/soc/codecs/wm8997.c1175
-rw-r--r--sound/soc/codecs/wm8997.h23
-rw-r--r--sound/soc/codecs/wm_adsp.c124
-rw-r--r--sound/soc/codecs/wm_adsp.h3
-rw-r--r--sound/soc/codecs/wm_hubs.c8
-rw-r--r--sound/soc/dwc/designware_i2s.c5
-rw-r--r--sound/soc/fsl/Kconfig23
-rw-r--r--sound/soc/fsl/Makefile4
-rw-r--r--sound/soc/fsl/fsl_spdif.c1225
-rw-r--r--sound/soc/fsl/fsl_spdif.h191
-rw-r--r--sound/soc/fsl/fsl_ssi.c501
-rw-r--r--sound/soc/fsl/imx-audmux.c78
-rw-r--r--sound/soc/fsl/imx-audmux.h52
-rw-r--r--sound/soc/fsl/imx-mc13783.c1
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c4
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c20
-rw-r--r--sound/soc/fsl/imx-pcm.h26
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c4
-rw-r--r--sound/soc/fsl/imx-spdif.c148
-rw-r--r--sound/soc/fsl/imx-ssi.c11
-rw-r--r--sound/soc/fsl/imx-ssi.h1
-rw-r--r--sound/soc/fsl/imx-wm8962.c3
-rw-r--r--sound/soc/generic/simple-card.c2
-rw-r--r--sound/soc/kirkwood/Kconfig13
-rw-r--r--sound/soc/kirkwood/Makefile4
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c108
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c93
-rw-r--r--sound/soc/kirkwood/kirkwood-openrd.c6
-rw-r--r--sound/soc/kirkwood/kirkwood-t5325.c6
-rw-r--r--sound/soc/kirkwood/kirkwood.h11
-rw-r--r--sound/soc/mxs/Kconfig3
-rw-r--r--sound/soc/mxs/mxs-saif.c1
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c32
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c3
-rw-r--r--sound/soc/omap/Kconfig8
-rw-r--r--sound/soc/omap/mcbsp.c2
-rw-r--r--sound/soc/omap/omap-abe-twl6040.c133
-rw-r--r--sound/soc/omap/omap-dmic.c9
-rw-r--r--sound/soc/omap/omap-mcbsp.c5
-rw-r--r--sound/soc/omap/omap-mcpdm.c3
-rw-r--r--sound/soc/pxa/Kconfig2
-rw-r--r--sound/soc/pxa/brownstone.c1
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c7
-rw-r--r--sound/soc/pxa/mmp-pcm.c7
-rw-r--r--sound/soc/pxa/mmp-sspa.c15
-rw-r--r--sound/soc/pxa/pxa-ssp.c76
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c67
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c28
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c21
-rw-r--r--sound/soc/pxa/ttc-dkb.c1
-rw-r--r--sound/soc/s6000/s6105-ipcam.c2
-rw-r--r--sound/soc/samsung/ac97.c11
-rw-r--r--sound/soc/samsung/dma.c19
-rw-r--r--sound/soc/samsung/dma.h4
-rw-r--r--sound/soc/samsung/i2s-regs.h51
-rw-r--r--sound/soc/samsung/i2s.c193
-rw-r--r--sound/soc/samsung/pcm.c4
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c4
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c4
-rw-r--r--sound/soc/samsung/smdk_wm8994.c58
-rw-r--r--sound/soc/samsung/spdif.c12
-rw-r--r--sound/soc/sh/Kconfig7
-rw-r--r--sound/soc/sh/Makefile3
-rw-r--r--sound/soc/sh/fsi.c51
-rw-r--r--sound/soc/sh/rcar/Makefile2
-rw-r--r--sound/soc/sh/rcar/adg.c234
-rw-r--r--sound/soc/sh/rcar/core.c861
-rw-r--r--sound/soc/sh/rcar/gen.c280
-rw-r--r--sound/soc/sh/rcar/rsnd.h302
-rw-r--r--sound/soc/sh/rcar/scu.c236
-rw-r--r--sound/soc/sh/rcar/ssi.c728
-rw-r--r--sound/soc/soc-compress.c13
-rw-r--r--sound/soc/soc-core.c253
-rw-r--r--sound/soc/soc-dapm.c819
-rw-r--r--sound/soc/soc-jack.c4
-rw-r--r--sound/soc/soc-pcm.c25
-rw-r--r--sound/soc/spear/Kconfig2
-rw-r--r--sound/soc/tegra/Kconfig14
-rw-r--r--sound/soc/tegra/tegra20_ac97.c8
-rw-r--r--sound/soc/tegra/tegra_alc5632.c2
-rw-r--r--sound/soc/tegra/tegra_rt5640.c1
-rw-r--r--sound/soc/tegra/tegra_wm8753.c2
-rw-r--r--sound/soc/tegra/trimslice.c2
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c3
-rw-r--r--sound/soc/ux500/mop500.c1
-rw-r--r--sound/usb/6fire/firmware.c4
-rw-r--r--sound/usb/endpoint.c3
-rw-r--r--sound/usb/pcm.c243
-rw-r--r--sound/usb/usx2y/usbusx2y.c8
187 files changed, 11305 insertions, 6990 deletions
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index ce431e6e07cf..5066a3768b28 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -14,12 +14,14 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include <mach/regs-ac97.h>
#include <mach/audio.h>
@@ -41,20 +43,20 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_reset,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {
- .name = "AC97 PCM out",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(12),
- .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
- DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_out_req = 12;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
+ .addr = __PREG(PCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .maxburst = 32,
+ .filter_data = &pxa2xx_ac97_pcm_out_req,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = {
- .name = "AC97 PCM in",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(11),
- .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
- DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_in_req = 11;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
+ .addr = __PREG(PCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .maxburst = 32,
+ .filter_data = &pxa2xx_ac97_pcm_in_req,
};
static struct snd_pcm *pxa2xx_ac97_pcm;
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 823359ed95e1..a61d7a9a995e 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -7,11 +7,13 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include <mach/dma.h>
@@ -43,6 +45,35 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
size_t period = params_period_bytes(params);
pxa_dma_desc *dma_desc;
dma_addr_t dma_buff_phys, next_desc_phys;
+ u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
+
+ /* temporary transition hack */
+ switch (rtd->params->addr_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ dcmd |= DCMD_WIDTH1;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ dcmd |= DCMD_WIDTH2;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ dcmd |= DCMD_WIDTH4;
+ break;
+ default:
+ /* can't happen */
+ break;
+ }
+
+ switch (rtd->params->maxburst) {
+ case 8:
+ dcmd |= DCMD_BURST8;
+ break;
+ case 16:
+ dcmd |= DCMD_BURST16;
+ break;
+ case 32:
+ dcmd |= DCMD_BURST32;
+ break;
+ }
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = totsize;
@@ -55,14 +86,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
dma_desc->ddadr = next_desc_phys;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dma_desc->dsadr = dma_buff_phys;
- dma_desc->dtadr = rtd->params->dev_addr;
+ dma_desc->dtadr = rtd->params->addr;
} else {
- dma_desc->dsadr = rtd->params->dev_addr;
+ dma_desc->dsadr = rtd->params->addr;
dma_desc->dtadr = dma_buff_phys;
}
if (period > totsize)
period = totsize;
- dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
+ dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
dma_desc++;
dma_buff_phys += period;
} while (totsize -= period);
@@ -76,8 +107,10 @@ int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- if (rtd && rtd->params && rtd->params->drcmr)
- *rtd->params->drcmr = 0;
+ if (rtd && rtd->params && rtd->params->filter_data) {
+ unsigned long req = *(unsigned long *) rtd->params->filter_data;
+ DRCMR(req) = 0;
+ }
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
@@ -136,6 +169,7 @@ EXPORT_SYMBOL(pxa2xx_pcm_pointer);
int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+ unsigned long req;
if (!prtd || !prtd->params)
return 0;
@@ -146,7 +180,8 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
DCSR(prtd->dma_ch) &= ~DCSR_RUN;
DCSR(prtd->dma_ch) = 0;
DCMD(prtd->dma_ch) = 0;
- *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
+ req = *(unsigned long *) prtd->params->filter_data;
+ DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
return 0;
}
@@ -155,7 +190,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
{
struct snd_pcm_substream *substream = dev_id;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
int dcsr;
dcsr = DCSR(dma_ch);
@@ -164,8 +198,8 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
if (dcsr & DCSR_ENDINTR) {
snd_pcm_period_elapsed(substream);
} else {
- printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
- rtd->params->name, dma_ch, dcsr);
+ printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
+ dma_ch, dcsr);
snd_pcm_stream_lock(substream);
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
snd_pcm_stream_unlock(substream);
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
index 26422a3584ea..69a2455b4472 100644
--- a/sound/arm/pxa2xx-pcm.c
+++ b/sound/arm/pxa2xx-pcm.c
@@ -11,8 +11,11 @@
*/
#include <linux/module.h>
+#include <linux/dmaengine.h>
+
#include <sound/core.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include "pxa2xx-pcm.h"
@@ -40,7 +43,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
client->playback_params : client->capture_params;
- ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW,
+ ret = pxa_request_dma("dma", DMA_PRIO_LOW,
pxa2xx_pcm_dma_irq, substream);
if (ret < 0)
goto err2;
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
index 65f86b56ba42..2a8fc08d52a1 100644
--- a/sound/arm/pxa2xx-pcm.h
+++ b/sound/arm/pxa2xx-pcm.h
@@ -13,14 +13,14 @@
struct pxa2xx_runtime_data {
int dma_ch;
- struct pxa2xx_pcm_dma_params *params;
+ struct snd_dmaengine_dai_dma_data *params;
pxa_dma_desc *dma_desc_array;
dma_addr_t dma_desc_array_phys;
};
struct pxa2xx_pcm_client {
- struct pxa2xx_pcm_dma_params *playback_params;
- struct pxa2xx_pcm_dma_params *capture_params;
+ struct snd_dmaengine_dai_dma_data *playback_params;
+ struct snd_dmaengine_dai_dma_data *capture_params;
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index c0c2f57a0d6f..313f22e9d929 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -6,6 +6,9 @@ config SND_PCM
tristate
select SND_TIMER
+config SND_DMAENGINE_PCM
+ tristate
+
config SND_HWDEP
tristate
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 43d4117428ac..5e890cfed423 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -13,6 +13,8 @@ snd-$(CONFIG_SND_JACK) += jack.o
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o
+snd-pcm-dmaengine-objs := pcm_dmaengine.o
+
snd-page-alloc-y := memalloc.o
snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
@@ -30,6 +32,7 @@ obj-$(CONFIG_SND_TIMER) += snd-timer.o
obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
+obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
obj-$(CONFIG_SND_OSSEMUL) += oss/
diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/core/pcm_dmaengine.c
index aa924d9b7986..aa924d9b7986 100644
--- a/sound/soc/soc-dmaengine-pcm.c
+++ b/sound/core/pcm_dmaengine.c
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 82bb029d4414..6e03b465e44e 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -184,7 +184,7 @@ static void xrun(struct snd_pcm_substream *substream)
do { \
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
xrun_log_show(substream); \
- if (printk_ratelimit()) { \
+ if (snd_printd_ratelimit()) { \
snd_printd("PCM: " fmt, ##args); \
} \
dump_stack_on_xrun(substream); \
@@ -342,7 +342,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
return -EPIPE;
}
if (pos >= runtime->buffer_size) {
- if (printk_ratelimit()) {
+ if (snd_printd_ratelimit()) {
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
xrun_log_show(substream);
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 11048cc744d0..915b4d7fbb23 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -1022,7 +1022,7 @@ static void dummy_proc_write(struct snd_info_entry *entry,
if (i >= ARRAY_SIZE(fields))
continue;
snd_info_get_str(item, ptr, sizeof(item));
- if (strict_strtoull(item, 0, &val))
+ if (kstrtoull(item, 0, &val))
continue;
if (fields[i].size == sizeof(int))
*get_dummy_int_ptr(dummy, fields[i].offset) = val;
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 2c6386503940..fe9e6e2f2c5b 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -49,7 +49,6 @@ struct fwspk {
struct snd_card *card;
struct fw_unit *unit;
const struct device_info *device_info;
- struct snd_pcm_substream *pcm;
struct mutex mutex;
struct cmp_connection connection;
struct amdtp_out_stream stream;
@@ -363,8 +362,7 @@ static int fwspk_create_pcm(struct fwspk *fwspk)
return err;
pcm->private_data = fwspk;
strcpy(pcm->name, fwspk->device_info->short_name);
- fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- fwspk->pcm->ops = &ops;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
return 0;
}
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index 9942691cc0ca..afef0d738078 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -443,8 +443,7 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
for (i = 0; i < 8; ++i)
iwave[i] = snd_gf1_peek(gus, bank_pos + i);
#ifdef CONFIG_SND_DEBUG_ROM
- printk(KERN_DEBUG "ROM at 0x%06x = %*phC\n", bank_pos,
- 8, iwave);
+ printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave);
#endif
if (strncmp(iwave, "INTRWAVE", 8))
continue; /* first check */
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
index a59c88818f48..461d94cfecbe 100644
--- a/sound/oss/dmabuf.c
+++ b/sound/oss/dmabuf.c
@@ -557,7 +557,6 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
unsigned long flags;
int err = 0, n = 0;
struct dma_buffparms *dmap = adev->dmap_in;
- int go;
if (!(adev->open_mode & OPEN_READ))
return -EIO;
@@ -584,7 +583,7 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
spin_unlock_irqrestore(&dmap->lock,flags);
return -EAGAIN;
}
- if ((go = adev->go))
+ if (adev->go)
timeout = dmabuf_timeout(dmap);
spin_unlock_irqrestore(&dmap->lock,flags);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 59c5e9c03d53..8de66ccd7279 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -152,14 +152,9 @@ config SND_HDA_CODEC_HDMI
This module is automatically loaded at probing.
config SND_HDA_I915
- bool "Build Display HD-audio controller/codec power well support for i915 cards"
+ bool
+ default y
depends on DRM_I915
- help
- Say Y here to include full HDMI and DisplayPort HD-audio controller/codec
- power-well support for Intel Haswell graphics cards based on the i915 driver.
-
- Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise
- the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode.
config SND_HDA_CODEC_CIRRUS
bool "Build Cirrus Logic codec support"
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8a005f0e5ca4..5b6c4e3c92ca 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -666,6 +666,64 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
}
EXPORT_SYMBOL_HDA(snd_hda_get_conn_index);
+
+/* return DEVLIST_LEN parameter of the given widget */
+static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int parm;
+
+ if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
+ get_wcaps_type(wcaps) != AC_WID_PIN)
+ return 0;
+
+ parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN);
+ if (parm == -1 && codec->bus->rirb_error)
+ parm = 0;
+ return parm & AC_DEV_LIST_LEN_MASK;
+}
+
+/**
+ * snd_hda_get_devices - copy device list without cache
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ * @dev_list: device list array
+ * @max_devices: max. number of devices to store
+ *
+ * Copy the device list. This info is dynamic and so not cached.
+ * Currently called only from hda_proc.c, so not exported.
+ */
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+ u8 *dev_list, int max_devices)
+{
+ unsigned int parm;
+ int i, dev_len, devices;
+
+ parm = get_num_devices(codec, nid);
+ if (!parm) /* not multi-stream capable */
+ return 0;
+
+ dev_len = parm + 1;
+ dev_len = dev_len < max_devices ? dev_len : max_devices;
+
+ devices = 0;
+ while (devices < dev_len) {
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DEVICE_LIST, devices);
+ if (parm == -1 && codec->bus->rirb_error)
+ break;
+
+ for (i = 0; i < 8; i++) {
+ dev_list[devices] = (u8)parm;
+ parm >>= 4;
+ devices++;
+ if (devices >= dev_len)
+ break;
+ }
+ }
+ return devices;
+}
+
/**
* snd_hda_queue_unsol_event - add an unsolicited event to queue
* @bus: the BUS
@@ -1216,11 +1274,13 @@ static void hda_jackpoll_work(struct work_struct *work)
{
struct hda_codec *codec =
container_of(work, struct hda_codec, jackpoll_work.work);
- if (!codec->jackpoll_interval)
- return;
snd_hda_jack_set_dirty_all(codec);
snd_hda_jack_poll_all(codec);
+
+ if (!codec->jackpoll_interval)
+ return;
+
queue_delayed_work(codec->bus->workq, &codec->jackpoll_work,
codec->jackpoll_interval);
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 701c2e069b10..7aa9870040c1 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -94,6 +94,8 @@ enum {
#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
+#define AC_VERB_GET_DEVICE_SEL 0xf35
+#define AC_VERB_GET_DEVICE_LIST 0xf36
/*
* SET verbs
@@ -133,6 +135,7 @@ enum {
#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
#define AC_VERB_SET_HDMI_CP_CTRL 0x733
#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
+#define AC_VERB_SET_DEVICE_SEL 0x735
/*
* Parameter IDs
@@ -154,6 +157,7 @@ enum {
#define AC_PAR_GPIO_CAP 0x11
#define AC_PAR_AMP_OUT_CAP 0x12
#define AC_PAR_VOL_KNB_CAP 0x13
+#define AC_PAR_DEVLIST_LEN 0x15
#define AC_PAR_HDMI_LPCM_CAP 0x20
/*
@@ -251,6 +255,11 @@ enum {
#define AC_UNSOL_RES_TAG_SHIFT 26
#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
#define AC_UNSOL_RES_SUBTAG_SHIFT 21
+#define AC_UNSOL_RES_DE (0x3f<<15) /* Device Entry
+ * (for DP1.2 MST)
+ */
+#define AC_UNSOL_RES_DE_SHIFT 15
+#define AC_UNSOL_RES_IA (1<<2) /* Inactive (for DP1.2 MST) */
#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
@@ -352,6 +361,10 @@ enum {
#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
+/* Display pin's device list length */
+#define AC_DEV_LIST_LEN_MASK 0x3f
+#define AC_MAX_DEV_LIST_LEN 64
+
/*
* Control Parameters
*/
@@ -460,6 +473,11 @@ enum {
#define AC_DEFCFG_PORT_CONN (0x3<<30)
#define AC_DEFCFG_PORT_CONN_SHIFT 30
+/* Display pin's device list entry */
+#define AC_DE_PD (1<<0)
+#define AC_DE_ELDV (1<<1)
+#define AC_DE_IA (1<<2)
+
/* device device types (0x0-0xf) */
enum {
AC_JACK_LINE_OUT,
@@ -885,6 +903,7 @@ struct hda_codec {
unsigned int pcm_format_first:1; /* PCM format must be set first */
unsigned int epss:1; /* supporting EPSS? */
unsigned int cached_write:1; /* write only to caches */
+ unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
#ifdef CONFIG_PM
unsigned int power_on :1; /* current (global) power-state */
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
@@ -972,6 +991,8 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive);
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+ u8 *dev_list, int max_devices);
int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index e3c7ba8d7582..ac41e9cdc976 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -142,6 +142,9 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "primary_hp");
if (val >= 0)
spec->no_primary_hp = !val;
+ val = snd_hda_get_bool_hint(codec, "multi_io");
+ if (val >= 0)
+ spec->no_multi_io = !val;
val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
if (val >= 0)
spec->multi_cap_vol = !!val;
@@ -813,6 +816,8 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
enum {
HDA_CTL_WIDGET_VOL,
@@ -830,7 +835,13 @@ static const struct snd_kcontrol_new control_templates[] = {
.put = hda_gen_mixer_mute_put, /* replaced */
.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
},
- HDA_BIND_MUTE(NULL, 0, 0, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_bind_switch_get,
+ .put = hda_gen_bind_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
};
/* add dynamic controls from template */
@@ -937,8 +948,8 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
}
/* playback mute control with the software mute bit check */
-static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
@@ -949,10 +960,22 @@ static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] &= enabled;
ucontrol->value.integer.value[1] &= enabled;
}
+}
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ sync_auto_mute_bits(kcontrol, ucontrol);
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
}
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ sync_auto_mute_bits(kcontrol, ucontrol);
+ return snd_hda_mixer_bind_switch_put(kcontrol, ucontrol);
+}
+
/* any ctl assigned to the path with the given index? */
static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
{
@@ -1541,7 +1564,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
cfg->speaker_pins,
spec->multiout.extra_out_nid,
spec->speaker_paths);
- if (fill_mio_first && cfg->line_outs == 1 &&
+ if (!spec->no_multi_io &&
+ fill_mio_first && cfg->line_outs == 1 &&
cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
if (!err)
@@ -1554,7 +1578,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
spec->private_dac_nids, spec->out_paths,
spec->main_out_badness);
- if (fill_mio_first &&
+ if (!spec->no_multi_io && fill_mio_first &&
cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
/* try to fill multi-io first */
err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
@@ -1582,7 +1606,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
return err;
badness += err;
}
- if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ if (!spec->no_multi_io &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
if (err < 0)
return err;
@@ -1600,7 +1625,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
check_aamix_out_path(codec, spec->speaker_paths[0]);
}
- if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ if (!spec->no_multi_io &&
+ cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
spec->multi_ios = 1; /* give badness */
@@ -3724,7 +3750,8 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
/* check each pin in the given array; returns true if any of them is plugged */
static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
{
- int i, present = 0;
+ int i;
+ bool present = false;
for (i = 0; i < num_pins; i++) {
hda_nid_t nid = pins[i];
@@ -3733,14 +3760,15 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
/* don't detect pins retasked as inputs */
if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
continue;
- present |= snd_hda_jack_detect(codec, nid);
+ if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT)
+ present = true;
}
return present;
}
/* standard HP/line-out auto-mute helper */
static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
- bool mute)
+ int *paths, bool mute)
{
struct hda_gen_spec *spec = codec->spec;
int i;
@@ -3752,10 +3780,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
break;
if (spec->auto_mute_via_amp) {
+ struct nid_path *path;
+ hda_nid_t mute_nid;
+
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (!path)
+ continue;
+ mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]);
+ if (!mute_nid)
+ continue;
if (mute)
- spec->mute_bits |= (1ULL << nid);
+ spec->mute_bits |= (1ULL << mute_nid);
else
- spec->mute_bits &= ~(1ULL << nid);
+ spec->mute_bits &= ~(1ULL << mute_nid);
set_pin_eapd(codec, nid, !mute);
continue;
}
@@ -3786,14 +3823,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
void snd_hda_gen_update_outputs(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
+ int *paths;
int on;
/* Control HP pins/amps depending on master_mute state;
* in general, HP pins/amps control should be enabled in all cases,
* but currently set only for master_mute, just to be safe
*/
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->hp_paths;
do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
- spec->autocfg.hp_pins, spec->master_mute);
+ spec->autocfg.hp_pins, paths, spec->master_mute);
if (!spec->automute_speaker)
on = 0;
@@ -3801,8 +3843,12 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
on = spec->hp_jack_present | spec->line_jack_present;
on |= spec->master_mute;
spec->speaker_muted = on;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->speaker_paths;
do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
- spec->autocfg.speaker_pins, on);
+ spec->autocfg.speaker_pins, paths, on);
/* toggle line-out mutes if needed, too */
/* if LO is a copy of either HP or Speaker, don't need to handle it */
@@ -3815,8 +3861,9 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
on = spec->hp_jack_present;
on |= spec->master_mute;
spec->line_out_muted = on;
+ paths = spec->out_paths;
do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
- spec->autocfg.line_out_pins, on);
+ spec->autocfg.line_out_pins, paths, on);
}
EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
@@ -3887,7 +3934,7 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja
/* don't detect pins retasked as outputs */
if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
continue;
- if (snd_hda_jack_detect(codec, pin)) {
+ if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) {
mux_select(codec, 0, spec->am_entry[i].idx);
return;
}
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index e199a852388b..48d44026705b 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -220,6 +220,7 @@ struct hda_gen_spec {
unsigned int hp_mic:1; /* Allow HP as a mic-in */
unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+ unsigned int no_multi_io:1; /* Don't try multi I/O config */
unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
unsigned int own_eapd_ctl:1; /* set EAPD by own function */
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index ce67608734b5..fe0bda19de15 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -295,7 +295,7 @@ static ssize_t type##_store(struct device *dev, \
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
struct hda_codec *codec = hwdep->private_data; \
unsigned long val; \
- int err = strict_strtoul(buf, 0, &val); \
+ int err = kstrtoul(buf, 0, &val); \
if (err < 0) \
return err; \
codec->type = val; \
@@ -654,7 +654,7 @@ int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
p = snd_hda_get_hint(codec, key);
if (!p)
ret = -ENOENT;
- else if (strict_strtoul(p, 0, &val))
+ else if (kstrtoul(p, 0, &val))
ret = -EINVAL;
else {
*valp = val;
@@ -751,7 +751,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
struct hda_codec **codecp) \
{ \
unsigned long val; \
- if (!strict_strtoul(buf, 0, &val)) \
+ if (!kstrtoul(buf, 0, &val)) \
(*codecp)->name = val; \
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 8860dd529520..c6c98298ac39 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1160,7 +1160,7 @@ static int azx_reset(struct azx *chip, int full_reset)
goto __skip;
/* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+ azx_writew(chip, STATESTS, STATESTS_INT_MASK);
/* reset controller */
azx_enter_link_reset(chip);
@@ -1242,7 +1242,7 @@ static void azx_int_clear(struct azx *chip)
}
/* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+ azx_writew(chip, STATESTS, STATESTS_INT_MASK);
/* clear rirb status */
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
@@ -1451,8 +1451,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
#if 0
/* clear state status int */
- if (azx_readb(chip, STATESTS) & 0x04)
- azx_writeb(chip, STATESTS, 0x04);
+ if (azx_readw(chip, STATESTS) & 0x04)
+ azx_writew(chip, STATESTS, 0x04);
#endif
spin_unlock(&chip->reg_lock);
@@ -2971,6 +2971,10 @@ static int azx_runtime_suspend(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
+ /* enable controller wake up event */
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+ STATESTS_INT_MASK);
+
azx_stop_chip(chip);
azx_enter_link_reset(chip);
azx_clear_irq_pending(chip);
@@ -2983,11 +2987,31 @@ static int azx_runtime_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
+ struct hda_bus *bus;
+ struct hda_codec *codec;
+ int status;
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
hda_display_power(true);
+
+ /* Read STATESTS before controller reset */
+ status = azx_readw(chip, STATESTS);
+
azx_init_pci(chip);
azx_init_chip(chip, 1);
+
+ bus = chip->bus;
+ if (status && bus) {
+ list_for_each_entry(codec, &bus->codec_list, list)
+ if (status & (1 << codec->addr))
+ queue_delayed_work(codec->bus->workq,
+ &codec->jackpoll_work, codec->jackpoll_interval);
+ }
+
+ /* disable controller Wake Up event*/
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+ ~STATESTS_INT_MASK);
+
return 0;
}
@@ -3831,11 +3855,13 @@ static int azx_probe_continue(struct azx *chip)
/* Request power well for Haswell HDA controller and codec */
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+#ifdef CONFIG_SND_HDA_I915
err = hda_i915_init();
if (err < 0) {
snd_printk(KERN_ERR SFX "Error request power-well from i915\n");
goto out_free;
}
+#endif
hda_display_power(true);
}
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 3fd2973183e2..05b3e3e9108f 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -194,18 +194,24 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
/**
- * snd_hda_jack_detect - query pin Presence Detect status
+ * snd_hda_jack_detect_state - query pin Presence Detect status
* @codec: the CODEC to sense
* @nid: the pin NID to sense
*
- * Query and return the pin's Presence Detect status.
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
*/
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
{
- u32 sense = snd_hda_pin_sense(codec, nid);
- return get_jack_plug_state(sense);
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
+ if (jack && jack->phantom_jack)
+ return HDA_JACK_PHANTOM;
+ else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE)
+ return HDA_JACK_PRESENT;
+ else
+ return HDA_JACK_NOT_PRESENT;
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect_state);
/**
* snd_hda_jack_detect_enable - enable the jack-detection
@@ -247,8 +253,8 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid)
{
- struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid);
- struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid);
+ struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid);
+ struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid);
if (!gated || !gating)
return -EINVAL;
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index ec12abd45263..379420c44eef 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -75,7 +75,18 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
+
+/* the jack state returned from snd_hda_jack_detect_state() */
+enum {
+ HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
+};
+
+int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid);
+
+static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT;
+}
bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 9760f001916d..a8cb22eec89e 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -582,6 +582,36 @@ static void print_gpio(struct snd_info_buffer *buffer,
print_nid_array(buffer, codec, nid, &codec->nids);
}
+static void print_device_list(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i, curr = -1;
+ u8 dev_list[AC_MAX_DEV_LIST_LEN];
+ int devlist_len;
+
+ devlist_len = snd_hda_get_devices(codec, nid, dev_list,
+ AC_MAX_DEV_LIST_LEN);
+ snd_iprintf(buffer, " Devices: %d\n", devlist_len);
+ if (devlist_len <= 0)
+ return;
+
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DEVICE_SEL, 0);
+
+ for (i = 0; i < devlist_len; i++) {
+ if (i == curr)
+ snd_iprintf(buffer, " *");
+ else
+ snd_iprintf(buffer, " ");
+
+ snd_iprintf(buffer,
+ "Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i,
+ !!(dev_list[i] & AC_DE_PD),
+ !!(dev_list[i] & AC_DE_ELDV),
+ !!(dev_list[i] & AC_DE_IA));
+ }
+}
+
static void print_codec_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -751,6 +781,9 @@ static void print_codec_info(struct snd_info_entry *entry,
(wid_caps & AC_WCAP_DELAY) >>
AC_WCAP_DELAY_SHIFT);
+ if (wid_type == AC_WID_PIN && codec->dp_mst)
+ print_device_list(buffer, codec, nid);
+
if (wid_caps & AC_WCAP_CONN_LIST)
print_conn_list(buffer, codec, nid, wid_type,
conn, conn_len);
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index d97f0d61a15b..0cbdd87dde6d 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -32,7 +32,6 @@
#include "hda_jack.h"
#include "hda_generic.h"
-#define ENABLE_AD_STATIC_QUIRKS
struct ad198x_spec {
struct hda_gen_spec gen;
@@ -43,114 +42,8 @@ struct ad198x_spec {
hda_nid_t eapd_nid;
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
- const struct snd_kcontrol_new *mixers[6];
- int num_mixers;
- const struct hda_verb *init_verbs[6]; /* initialization verbs
- * don't forget NULL termination!
- */
- unsigned int num_init_verbs;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- unsigned int cur_eapd;
- unsigned int need_dac_fix;
-
- /* capture */
- unsigned int num_adc_nids;
- const hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- const hda_nid_t *capsrc_nids;
- unsigned int cur_mux[3];
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
-
- unsigned int spdif_route;
-
- unsigned int jack_present: 1;
- unsigned int inv_jack_detect: 1;/* inverted jack-detection */
- unsigned int analog_beep: 1; /* analog beep input present */
- unsigned int avoid_init_slave_vol:1;
-
-#ifdef CONFIG_PM
- struct hda_loopback_check loopback;
-#endif
- /* for virtual master */
- hda_nid_t vmaster_nid;
- const char * const *slave_vols;
- const char * const *slave_sws;
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-};
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-/*
- * input MUX handling (common part)
- */
-static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->capsrc_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-/*
- * initialization (common callbacks)
- */
-static int ad198x_init(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
- return 0;
-}
-
-static const char * const ad_slave_pfxs[] = {
- "Front", "Surround", "Center", "LFE", "Side",
- "Headphone", "Mono", "Speaker", "IEC958",
- NULL
};
-static const char * const ad1988_6stack_fp_slave_pfxs[] = {
- "Front", "Surround", "Center", "LFE", "Side", "IEC958",
- NULL
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
@@ -160,12 +53,6 @@ static const struct snd_kcontrol_new ad_beep_mixer[] = {
{ } /* end */
};
-static const struct snd_kcontrol_new ad_beep2_mixer[] = {
- HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
- { } /* end */
-};
-
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
#else
@@ -181,8 +68,7 @@ static int create_beep_ctls(struct hda_codec *codec)
if (!spec->beep_amp)
return 0;
- knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
- for ( ; knew->name; knew++) {
+ for (knew = ad_beep_mixer ; knew->name; knew++) {
int err;
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(knew, codec);
@@ -199,268 +85,6 @@ static int create_beep_ctls(struct hda_codec *codec)
#define create_beep_ctls(codec) 0
#endif
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int ad198x_build_controls(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- unsigned int i;
- int err;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* create beep controls if needed */
- err = create_beep_ctls(codec);
- if (err < 0)
- return err;
-
- /* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = __snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv,
- (spec->slave_vols ?
- spec->slave_vols : ad_slave_pfxs),
- "Playback Volume",
- !spec->avoid_init_slave_vol, NULL);
- if (err < 0)
- return err;
- }
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL,
- (spec->slave_sws ?
- spec->slave_sws : ad_slave_pfxs),
- "Playback Switch");
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- if (!kctl)
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
- if (err < 0)
- return err;
- }
-
- /* assign IEC958 enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec,
- SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
- if (kctl) {
- err = snd_hda_add_nid(codec, kctl, 0,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-/*
- * Analog playback callbacks
- */
-static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-/*
- */
-static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6, /* changed later */
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_playback_pcm_open,
- .prepare = ad198x_playback_pcm_prepare,
- .cleanup = ad198x_playback_pcm_cleanup,
- },
-};
-
-static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = ad198x_capture_pcm_prepare,
- .cleanup = ad198x_capture_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_dig_playback_pcm_open,
- .close = ad198x_dig_playback_pcm_close,
- .prepare = ad198x_dig_playback_pcm_prepare,
- .cleanup = ad198x_dig_playback_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static int ad198x_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "AD198x Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- if (spec->multiout.dig_out_nid) {
- info++;
- codec->num_pcms++;
- codec->spdif_status_reset = 1;
- info->name = "AD198x Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-#endif /* ENABLE_AD_STATIC_QUIRKS */
static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
hda_nid_t hp)
@@ -507,18 +131,6 @@ static void ad198x_shutup(struct hda_codec *codec)
ad198x_power_eapd(codec);
}
-static void ad198x_free(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- snd_hda_gen_spec_free(&spec->gen);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
#ifdef CONFIG_PM
static int ad198x_suspend(struct hda_codec *codec)
{
@@ -527,65 +139,6 @@ static int ad198x_suspend(struct hda_codec *codec)
}
#endif
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const struct hda_codec_ops ad198x_patch_ops = {
- .build_controls = ad198x_build_controls,
- .build_pcms = ad198x_build_pcms,
- .init = ad198x_init,
- .free = ad198x_free,
-#ifdef CONFIG_PM
- .check_power_status = ad198x_check_power_status,
- .suspend = ad198x_suspend,
-#endif
- .reboot_notify = ad198x_shutup,
-};
-
-
-/*
- * EAPD control
- * the private value = nid
- */
-#define ad198x_eapd_info snd_ctl_boolean_mono_info
-
-static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- if (codec->inv_eapd)
- ucontrol->value.integer.value[0] = ! spec->cur_eapd;
- else
- ucontrol->value.integer.value[0] = spec->cur_eapd;
- return 0;
-}
-
-static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int eapd;
- eapd = !!ucontrol->value.integer.value[0];
- if (codec->inv_eapd)
- eapd = !eapd;
- if (eapd == spec->cur_eapd)
- return 0;
- spec->cur_eapd = eapd;
- snd_hda_codec_write_cache(codec, nid,
- 0, AC_VERB_SET_EAPD_BTLENABLE,
- eapd ? 0x02 : 0x00);
- return 1;
-}
-
-static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo);
-static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
/*
* Automatic parse of I/O pins from the BIOS configuration
@@ -646,537 +199,6 @@ static int ad198x_parse_auto_config(struct hda_codec *codec)
* AD1986A specific
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
-#define AD1986A_SPDIF_OUT 0x02
-#define AD1986A_FRONT_DAC 0x03
-#define AD1986A_SURR_DAC 0x04
-#define AD1986A_CLFE_DAC 0x05
-#define AD1986A_ADC 0x06
-
-static const hda_nid_t ad1986a_dac_nids[3] = {
- AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
-};
-static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
-static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
-
-static const struct hda_input_mux ad1986a_capture_source = {
- .num_items = 7,
- .items = {
- { "Mic", 0x0 },
- { "CD", 0x1 },
- { "Aux", 0x3 },
- { "Line", 0x4 },
- { "Mix", 0x5 },
- { "Mono", 0x6 },
- { "Phone", 0x7 },
- },
-};
-
-
-static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/*
- * mixers
- */
-static const struct snd_kcontrol_new ad1986a_mixers[] = {
- /*
- * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
- */
- HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
- HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
- HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* additional mixers for 3stack mode */
-static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-/* laptop model - 2ch only */
-static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
-
-/* master controls both pins 0x1a and 0x1b */
-static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
-
-static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
-
-static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
- /*
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* laptop-eapd model - 2ch only */
-
-static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x4 },
- { "Mix", 0x5 },
- },
-};
-
-static const struct hda_input_mux ad1986a_automic_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x5 },
- },
-};
-
-static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x1b, /* port-D */
- },
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* re-connect the mic boost input according to the jack sensing */
-static void ad1986a_automic(struct hda_codec *codec)
-{
- unsigned int present;
- present = snd_hda_jack_detect(codec, 0x1f);
- /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 2);
-}
-
-#define AD1986A_MIC_EVENT 0x36
-
-static void ad1986a_automic_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1986A_MIC_EVENT)
- return;
- ad1986a_automic(codec);
-}
-
-static int ad1986a_automic_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_automic(codec);
- return 0;
-}
-
-/* laptop-automute - 2ch only */
-
-static void ad1986a_update_hp(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- unsigned int mute;
-
- if (spec->jack_present)
- mute = HDA_AMP_MUTE; /* mute internal speaker */
- else
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
-}
-
-static void ad1986a_hp_automute(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
- if (spec->inv_jack_detect)
- spec->jack_present = !spec->jack_present;
- ad1986a_update_hp(codec);
-}
-
-#define AD1986A_HP_EVENT 0x37
-
-static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1986A_HP_EVENT)
- return;
- ad1986a_hp_automute(codec);
-}
-
-static int ad1986a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- return 0;
-}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- if (change)
- ad1986a_update_hp(codec);
- return change;
-}
-
-static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1986a_hp_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- },
- { } /* end */
-};
-
-
-/*
- * initialization verbs
- */
-static const struct hda_verb ad1986a_init_verbs[] = {
- /* Front, Surround, CLFE DAC; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Downmix - off */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP, Line-Out, Surround, CLFE selectors */
- {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic selector: Mic 1/2 pin */
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic 1/2 swap */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Record selector: mic */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* PC beep */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP Pin */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Front, Surround, CLFE Pins */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mono Pin */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line, Aux, CD, Beep-In Pin */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
-
-static const struct hda_verb ad1986a_ch2_init[] = {
- /* Surround out -> Line In */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* Line-in selectors */
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static const struct hda_verb ad1986a_ch4_init[] = {
- /* Surround out -> Surround */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static const struct hda_verb ad1986a_ch6_init[] = {
- /* Surround out -> Surround out */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> CLFE */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { } /* end */
-};
-
-static const struct hda_channel_mode ad1986a_modes[3] = {
- { 2, ad1986a_ch2_init },
- { 4, ad1986a_ch4_init },
- { 6, ad1986a_ch6_init },
-};
-
-/* eapd initialization */
-static const struct hda_verb ad1986a_eapd_init_verbs[] = {
- {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- {}
-};
-
-static const struct hda_verb ad1986a_automic_verbs[] = {
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
- {}
-};
-
-/* Ultra initialization */
-static const struct hda_verb ad1986a_ultra_init[] = {
- /* eapd initialization */
- { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- /* CLFE -> Mic in */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
- { } /* end */
-};
-
-/* pin sensing on HP jack */
-static const struct hda_verb ad1986a_hp_init_verbs[] = {
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
- {}
-};
-
-static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1986A_HP_EVENT:
- ad1986a_hp_automute(codec);
- break;
- case AD1986A_MIC_EVENT:
- ad1986a_automic(codec);
- break;
- }
-}
-
-static int ad1986a_samsung_p50_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- ad1986a_automic(codec);
- return 0;
-}
-
-
-/* models */
-enum {
- AD1986A_AUTO,
- AD1986A_6STACK,
- AD1986A_3STACK,
- AD1986A_LAPTOP,
- AD1986A_LAPTOP_EAPD,
- AD1986A_LAPTOP_AUTOMUTE,
- AD1986A_ULTRA,
- AD1986A_SAMSUNG,
- AD1986A_SAMSUNG_P50,
- AD1986A_MODELS
-};
-
-static const char * const ad1986a_models[AD1986A_MODELS] = {
- [AD1986A_AUTO] = "auto",
- [AD1986A_6STACK] = "6stack",
- [AD1986A_3STACK] = "3stack",
- [AD1986A_LAPTOP] = "laptop",
- [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
- [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
- [AD1986A_ULTRA] = "ultra",
- [AD1986A_SAMSUNG] = "samsung",
- [AD1986A_SAMSUNG_P50] = "samsung-p50",
-};
-
-static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
- SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
- SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
- SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
- SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
- {}
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1986a_loopbacks[] = {
- { 0x13, HDA_OUTPUT, 0 }, /* Mic */
- { 0x14, HDA_OUTPUT, 0 }, /* Phone */
- { 0x15, HDA_OUTPUT, 0 }, /* CD */
- { 0x16, HDA_OUTPUT, 0 }, /* Aux */
- { 0x17, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
-};
-#endif
-
-static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
- return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
-}
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
static int alloc_ad_spec(struct hda_codec *codec)
{
struct ad198x_spec *spec;
@@ -1203,6 +225,11 @@ static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
enum {
AD1986A_FIXUP_INV_JACK_DETECT,
+ AD1986A_FIXUP_ULTRA,
+ AD1986A_FIXUP_SAMSUNG,
+ AD1986A_FIXUP_3STACK,
+ AD1986A_FIXUP_LAPTOP,
+ AD1986A_FIXUP_LAPTOP_IMIC,
};
static const struct hda_fixup ad1986a_fixups[] = {
@@ -1210,16 +237,86 @@ static const struct hda_fixup ad1986a_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = ad_fixup_inv_jack_detect,
},
+ [AD1986A_FIXUP_ULTRA] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_SAMSUNG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ { 0x24, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_3STACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x01014011 }, /* front */
+ { 0x1c, 0x01013012 }, /* surround */
+ { 0x1d, 0x01019015 }, /* clfe */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a190f0 }, /* mic */
+ { 0x20, 0x018130f0 }, /* line-in */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a191f0 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP_IMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ .chained_before = 1,
+ .chain_id = AD1986A_FIXUP_LAPTOP,
+ },
};
static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG),
+ SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA),
SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
+ SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK),
+ {}
+};
+
+static const struct hda_model_fixup ad1986a_fixup_models[] = {
+ { .id = AD1986A_FIXUP_3STACK, .name = "3stack" },
+ { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */
{}
};
/*
*/
-static int ad1986a_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1986a(struct hda_codec *codec)
{
int err;
struct ad198x_spec *spec;
@@ -1244,7 +341,8 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec)
*/
spec->gen.multiout.no_share_stream = 1;
- snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups);
+ snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl,
+ ad1986a_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = ad198x_parse_auto_config(codec);
@@ -1258,330 +356,11 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec)
return 0;
}
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1986a(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
- ad1986a_models,
- ad1986a_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1986A_AUTO;
- }
-
- if (board_config == AD1986A_AUTO)
- return ad1986a_parse_auto_config(codec);
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- err = snd_hda_attach_beep_device(codec, 0x19);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
- spec->multiout.dac_nids = ad1986a_dac_nids;
- spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1986a_adc_nids;
- spec->capsrc_nids = ad1986a_capsrc_nids;
- spec->input_mux = &ad1986a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1986a_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1986a_init_verbs;
-#ifdef CONFIG_PM
- spec->loopback.amplist = ad1986a_loopbacks;
-#endif
- spec->vmaster_nid = 0x1b;
- codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- switch (board_config) {
- case AD1986A_3STACK:
- spec->num_mixers = 2;
- spec->mixers[1] = ad1986a_3st_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ch2_init;
- spec->channel_mode = ad1986a_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
- spec->need_dac_fix = 1;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- break;
- case AD1986A_LAPTOP:
- spec->mixers[0] = ad1986a_laptop_mixers;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- break;
- case AD1986A_LAPTOP_EAPD:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- break;
- case AD1986A_SAMSUNG:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
- codec->patch_ops.init = ad1986a_automic_init;
- break;
- case AD1986A_SAMSUNG_P50:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 4;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->init_verbs[3] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
- codec->patch_ops.init = ad1986a_samsung_p50_init;
- break;
- case AD1986A_LAPTOP_AUTOMUTE:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
- codec->patch_ops.init = ad1986a_hp_init;
- /* Lenovo N100 seems to report the reversed bit
- * for HP jack-sensing
- */
- spec->inv_jack_detect = 1;
- break;
- case AD1986A_ULTRA:
- spec->mixers[0] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ultra_init;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
- break;
- }
-
- /* AD1986A has a hardware problem that it can't share a stream
- * with multiple output pins. The copy of front to surrounds
- * causes noisy or silent outputs at a certain timing, e.g.
- * changing the volume.
- * So, let's disable the shared stream.
- */
- spec->multiout.no_share_stream = 1;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1986a ad1986a_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
* AD1983 specific
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
-#define AD1983_SPDIF_OUT 0x02
-#define AD1983_DAC 0x03
-#define AD1983_ADC 0x04
-
-static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
-static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
-static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
-
-static const struct hda_input_mux ad1983_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- },
-};
-
-/*
- * SPDIF playback route
- */
-static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- static const char * const texts[] = { "PCM", "ADC" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->spdif_route;
- return 0;
-}
-
-static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- if (ucontrol->value.enumerated.item[0] > 1)
- return -EINVAL;
- if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
- spec->spdif_route = ucontrol->value.enumerated.item[0];
- snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->spdif_route);
- return 1;
- }
- return 0;
-}
-
-static const struct snd_kcontrol_new ad1983_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static const struct hda_verb ad1983_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Mic, Line-In: mute */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic selector; Mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic boost: 0dB */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* Record selector: mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1983_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
-};
-#endif
-
-/* models */
-enum {
- AD1983_AUTO,
- AD1983_BASIC,
- AD1983_MODELS
-};
-
-static const char * const ad1983_models[AD1983_MODELS] = {
- [AD1983_AUTO] = "auto",
- [AD1983_BASIC] = "basic",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
/*
* SPDIF mux control for AD1983 auto-parser
*/
@@ -1656,7 +435,7 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
return 0;
}
-static int ad1983_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1983(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err;
@@ -1681,432 +460,11 @@ static int ad1983_parse_auto_config(struct hda_codec *codec)
return err;
}
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1983(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int board_config;
- int err;
-
- board_config = snd_hda_check_board_config(codec, AD1983_MODELS,
- ad1983_models, NULL);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1983_AUTO;
- }
-
- if (board_config == AD1983_AUTO)
- return ad1983_parse_auto_config(codec);
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
- spec->multiout.dac_nids = ad1983_dac_nids;
- spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1983_adc_nids;
- spec->capsrc_nids = ad1983_capsrc_nids;
- spec->input_mux = &ad1983_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1983_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1983_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_PM
- spec->loopback.amplist = ad1983_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
-
- codec->patch_ops = ad198x_patch_ops;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1983 ad1983_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
/*
* AD1981 HD specific
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
-#define AD1981_SPDIF_OUT 0x02
-#define AD1981_DAC 0x03
-#define AD1981_ADC 0x04
-
-static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
-static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
-static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
-
-/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
-static const struct hda_input_mux ad1981_capture_source = {
- .num_items = 7,
- .items = {
- { "Front Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- { "CD", 0x4 },
- { "Mic", 0x6 },
- { "Aux", 0x7 },
- },
-};
-
-static const struct snd_kcontrol_new ad1981_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static const struct hda_verb ad1981_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic Mixer; select Front Mic */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Mic boost: 0dB */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Record selector: Front mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Front & Rear Mic Pins */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* Digital Beep */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Line-Out as Input: disabled */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1981_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
- { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
- { 0x1d, HDA_OUTPUT, 0 }, /* CD */
- { } /* end */
-};
-#endif
-
-/*
- * Patch for HP nx6320
- *
- * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
- * speaker output enabled _and_ mute-LED off.
- */
-
-#define AD1981_HP_EVENT 0x37
-#define AD1981_MIC_EVENT 0x38
-
-static const struct hda_verb ad1981_hp_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- if (! ad198x_eapd_put(kcontrol, ucontrol))
- return 0;
- /* change speaker pin appropriately */
- snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
- /* toggle HP mute appropriately */
- snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- spec->cur_eapd ? 0 : HDA_AMP_MUTE);
- return 1;
-}
-
-/* bind volumes of both NID 0x05 and 0x06 */
-static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1981_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x06);
- snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void ad1981_hp_automic(struct hda_codec *codec)
-{
- static const struct hda_verb mic_jack_on[] = {
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- static const struct hda_verb mic_jack_off[] = {
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x08);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1981_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- switch (res) {
- case AD1981_HP_EVENT:
- ad1981_hp_automute(codec);
- break;
- case AD1981_MIC_EVENT:
- ad1981_hp_automic(codec);
- break;
- }
-}
-
-static const struct hda_input_mux ad1981_hp_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Dock Mic", 0x1 },
- { "Mix", 0x2 },
- },
-};
-
-static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
- .name = "Master Playback Switch",
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad1981_hp_master_sw_put,
- .private_value = 0x05,
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-#if 0
- /* FIXME: analog mic/line loopback doesn't work with my tests...
- * (although recording is OK)
- */
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- /* FIXME: does this laptop have analog CD connection? */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-#endif
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int ad1981_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1981_hp_automute(codec);
- ad1981_hp_automic(codec);
- return 0;
-}
-
-/* configuration for Toshiba Laptops */
-static const struct hda_verb ad1981_toshiba_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
- HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
- { }
-};
-
-/* configuration for Lenovo Thinkpad T60 */
-static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static const struct hda_input_mux ad1981_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* models */
-enum {
- AD1981_AUTO,
- AD1981_BASIC,
- AD1981_HP,
- AD1981_THINKPAD,
- AD1981_TOSHIBA,
- AD1981_MODELS
-};
-
-static const char * const ad1981_models[AD1981_MODELS] = {
- [AD1981_AUTO] = "auto",
- [AD1981_HP] = "hp",
- [AD1981_THINKPAD] = "thinkpad",
- [AD1981_BASIC] = "basic",
- [AD1981_TOSHIBA] = "toshiba"
-};
-
-static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
- SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
- /* All HP models */
- SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
- /* Lenovo Thinkpad T60/X60/Z6xx */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
- /* HP nx6320 (reversed SSID, H/W bug) */
- SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
- {}
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
/* follow EAPD via vmaster hook */
static void ad_vmaster_eapd_hook(void *private_data, int enabled)
{
@@ -2172,7 +530,7 @@ static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
{}
};
-static int ad1981_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1981(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err;
@@ -2205,110 +563,6 @@ static int ad1981_parse_auto_config(struct hda_codec *codec)
return err;
}
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1981(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
- ad1981_models,
- ad1981_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1981_AUTO;
- }
-
- if (board_config == AD1981_AUTO)
- return ad1981_parse_auto_config(codec);
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return -ENOMEM;
- spec = codec->spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
- spec->multiout.dac_nids = ad1981_dac_nids;
- spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1981_adc_nids;
- spec->capsrc_nids = ad1981_capsrc_nids;
- spec->input_mux = &ad1981_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1981_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1981_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_PM
- spec->loopback.amplist = ad1981_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- switch (board_config) {
- case AD1981_HP:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_hp_init_verbs;
- if (!is_jack_available(codec, 0x0a))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
-
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_THINKPAD:
- spec->mixers[0] = ad1981_thinkpad_mixers;
- spec->input_mux = &ad1981_thinkpad_capture_source;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_TOSHIBA:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->mixers[1] = ad1981_toshiba_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_toshiba_init_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1981 ad1981_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
/*
* AD1988
@@ -2395,90 +649,7 @@ static int patch_ad1981(struct hda_codec *codec)
* E/F quad mic array
*/
-
#ifdef ENABLE_AD_STATIC_QUIRKS
-/* models */
-enum {
- AD1988_AUTO,
- AD1988_6STACK,
- AD1988_6STACK_DIG,
- AD1988_3STACK,
- AD1988_3STACK_DIG,
- AD1988_LAPTOP,
- AD1988_LAPTOP_DIG,
- AD1988_MODEL_LAST,
-};
-
-/* reivision id to check workarounds */
-#define AD1988A_REV2 0x100200
-
-#define is_rev2(codec) \
- ((codec)->vendor_id == 0x11d41988 && \
- (codec)->revision_id == AD1988A_REV2)
-
-/*
- * mixers
- */
-
-static const hda_nid_t ad1988_6stack_dac_nids[4] = {
- 0x04, 0x06, 0x05, 0x0a
-};
-
-static const hda_nid_t ad1988_3stack_dac_nids[3] = {
- 0x04, 0x05, 0x0a
-};
-
-/* for AD1988A revision-2, DAC2-4 are swapped */
-static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
- 0x04, 0x05, 0x0a, 0x06
-};
-
-static const hda_nid_t ad1988_alt_dac_nid[1] = {
- 0x03
-};
-
-static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
- 0x04, 0x0a, 0x06
-};
-
-static const hda_nid_t ad1988_adc_nids[3] = {
- 0x08, 0x09, 0x0f
-};
-
-static const hda_nid_t ad1988_capsrc_nids[3] = {
- 0x0c, 0x0d, 0x0e
-};
-
-#define AD1988_SPDIF_OUT 0x02
-#define AD1988_SPDIF_OUT_HDMI 0x0b
-#define AD1988_SPDIF_IN 0x07
-
-static const hda_nid_t ad1989b_slave_dig_outs[] = {
- AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
-};
-
-static const struct hda_input_mux ad1988_6stack_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 }, /* port-B */
- { "Line", 0x2 }, /* port-C */
- { "Mic", 0x4 }, /* port-E */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-static const struct hda_input_mux ad1988_laptop_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x1 }, /* port-B */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-/*
- */
static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -2509,569 +680,6 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
spec->multiout.num_dacs = spec->multiout.max_channels / 2;
return err;
}
-
-/* 6-stack mode */
-static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* 3-stack mode */
-static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
-
- { } /* end */
-};
-
-/* laptop mode */
-static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x12, /* port-D */
- },
-
- { } /* end */
-};
-
-/* capture */
-static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static const char * const texts[] = {
- "PCM", "ADC1", "ADC2", "ADC3"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- if (!(sel & 0x80))
- ucontrol->value.enumerated.item[0] = 0;
- else {
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- if (sel < 3)
- sel++;
- else
- sel = 0;
- ucontrol->value.enumerated.item[0] = sel;
- }
- return 0;
-}
-
-static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int val, sel;
- int change;
-
- val = ucontrol->value.enumerated.item[0];
- if (val > 3)
- return -EINVAL;
- if (!val) {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(1));
- }
- } else {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT | 0x01);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- }
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0) + 1;
- change |= sel != val;
- if (change)
- snd_hda_codec_write_cache(codec, 0x0b, 0,
- AC_VERB_SET_CONNECT_SEL,
- val - 1);
- }
- return change;
-}
-
-static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad1988_spdif_playback_source_info,
- .get = ad1988_spdif_playback_source_get,
- .put = ad1988_spdif_playback_source_put,
- },
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-
-/*
- * for 6-stack (+dig)
- */
-static const struct hda_verb ad1988_6stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-F surround path */
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-G CLFE path */
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-H side path */
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in path */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in path */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Analog CD Input */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-
- { }
-};
-
-static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
- /* Headphone; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
- { }
-};
-
-static const struct hda_verb ad1988_capture_init_verbs[] = {
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- { }
-};
-
-static const struct hda_verb ad1988_spdif_init_verbs[] = {
- /* SPDIF out sel */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* SPDIF out pin */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-
- { }
-};
-
-static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
- /* unmute SPDIF input pin */
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-/* AD1989 has no ADC -> SPDIF route */
-static const struct hda_verb ad1989_spdif_init_verbs[] = {
- /* SPDIF-1 out pin */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- /* SPDIF-2/HDMI out pin */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { }
-};
-
-/*
- * verbs for 3stack (+dig)
- */
-static const struct hda_verb ad1988_3stack_ch2_init[] = {
- /* set port-C to line-in */
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* set port-E to mic-in */
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { } /* end */
-};
-
-static const struct hda_verb ad1988_3stack_ch6_init[] = {
- /* set port-C to surround out */
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- /* set port-E to CLFE out */
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static const struct hda_channel_mode ad1988_3stack_modes[2] = {
- { 2, ad1988_3stack_ch2_init },
- { 6, ad1988_3stack_ch6_init },
-};
-
-static const struct hda_verb ad1988_3stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in/surround path - 6ch mode as default */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in/CLFE path - 6ch mode as default */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-/*
- * verbs for laptop mode (+dig)
- */
-static const struct hda_verb ad1988_laptop_hp_on[] = {
- /* unmute port-A and mute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-static const struct hda_verb ad1988_laptop_hp_off[] = {
- /* mute port-A and unmute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-#define AD1988_HP_EVENT 0x01
-
-static const struct hda_verb ad1988_laptop_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
- /* Port-D line-out path + EAPD */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C docking station - try to output */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1988_HP_EVENT)
- return;
- if (snd_hda_jack_detect(codec, 0x11))
- snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
- else
- snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
-}
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1988_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Line */
- { 0x20, HDA_INPUT, 4 }, /* Mic */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
#endif /* ENABLE_AD_STATIC_QUIRKS */
static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
@@ -3220,7 +828,34 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
/*
*/
-static int ad1988_parse_auto_config(struct hda_codec *codec)
+enum {
+ AD1988_FIXUP_6STACK_DIG,
+};
+
+static const struct hda_fixup ad1988_fixups[] = {
+ [AD1988_FIXUP_6STACK_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x11, 0x02214130 }, /* front-hp */
+ { 0x12, 0x01014010 }, /* line-out */
+ { 0x14, 0x02a19122 }, /* front-mic */
+ { 0x15, 0x01813021 }, /* line-in */
+ { 0x16, 0x01011012 }, /* line-out */
+ { 0x17, 0x01a19020 }, /* mic */
+ { 0x1b, 0x0145f1f0 }, /* SPDIF */
+ { 0x24, 0x01016011 }, /* line-out */
+ { 0x25, 0x01012013 }, /* line-out */
+ { }
+ }
+ },
+};
+
+static const struct hda_model_fixup ad1988_fixup_models[] = {
+ { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" },
+ {}
+};
+
+static int patch_ad1988(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err;
@@ -3234,12 +869,19 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
spec->gen.mixer_merge_nid = 0x21;
spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
+ snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
err = ad198x_parse_auto_config(codec);
if (err < 0)
goto error;
err = ad1988_add_spdif_mux_ctl(codec);
if (err < 0)
goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
@@ -3247,169 +889,6 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
return err;
}
-/*
- */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const char * const ad1988_models[AD1988_MODEL_LAST] = {
- [AD1988_6STACK] = "6stack",
- [AD1988_6STACK_DIG] = "6stack-dig",
- [AD1988_3STACK] = "3stack",
- [AD1988_3STACK_DIG] = "3stack-dig",
- [AD1988_LAPTOP] = "laptop",
- [AD1988_LAPTOP_DIG] = "laptop-dig",
- [AD1988_AUTO] = "auto",
-};
-
-static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
- {}
-};
-
-static int patch_ad1988(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
- ad1988_models, ad1988_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1988_AUTO;
- }
-
- if (board_config == AD1988_AUTO)
- return ad1988_parse_auto_config(codec);
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- if (is_rev2(codec))
- snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
- switch (board_config) {
- case AD1988_6STACK:
- case AD1988_6STACK_DIG:
- spec->multiout.max_channels = 8;
- spec->multiout.num_dacs = 4;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_6stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_6stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_6stack_mixers1;
- spec->mixers[1] = ad1988_6stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_6stack_init_verbs;
- if (board_config == AD1988_6STACK_DIG) {
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- spec->dig_in_nid = AD1988_SPDIF_IN;
- }
- break;
- case AD1988_3STACK:
- case AD1988_3STACK_DIG:
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->channel_mode = ad1988_3stack_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_3stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_3stack_mixers1;
- spec->mixers[1] = ad1988_3stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_3stack_init_verbs;
- if (board_config == AD1988_3STACK_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_laptop_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1988_laptop_mixers;
- codec->inv_eapd = 1; /* inverted EAPD */
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_laptop_init_verbs;
- if (board_config == AD1988_LAPTOP_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- }
-
- spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
- spec->adc_nids = ad1988_adc_nids;
- spec->capsrc_nids = ad1988_capsrc_nids;
- spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
- if (spec->multiout.dig_out_nid) {
- if (codec->vendor_id >= 0x11d4989a) {
- spec->mixers[spec->num_mixers++] =
- ad1989_spdif_out_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1989_spdif_init_verbs;
- codec->slave_dig_outs = ad1989b_slave_dig_outs;
- } else {
- spec->mixers[spec->num_mixers++] =
- ad1988_spdif_out_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_init_verbs;
- }
- }
- if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
- spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_in_init_verbs;
- }
-
- codec->patch_ops = ad198x_patch_ops;
- switch (board_config) {
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
- break;
- }
-#ifdef CONFIG_PM
- spec->loopback.amplist = ad1988_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1988 ad1988_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
/*
* AD1884 / AD1984
@@ -3423,167 +902,19 @@ static int patch_ad1988(struct hda_codec *codec)
*
* AD1984 = AD1884 + two digital mic-ins
*
- * FIXME:
- * For simplicity, we share the single DAC for both HP and line-outs
- * right now. The inidividual playbacks could be easily implemented,
- * but no build-up framework is given, so far.
- */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const hda_nid_t ad1884_dac_nids[1] = {
- 0x04,
-};
-
-static const hda_nid_t ad1884_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static const hda_nid_t ad1884_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1884_SPDIF_OUT 0x02
-
-static const struct hda_input_mux ad1884_capture_source = {
- .num_items = 4,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static const struct snd_kcontrol_new ad1884_base_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
- HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
- HDA_INPUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
+ * AD1883 / AD1884A / AD1984A / AD1984B
+ *
+ * port-B (0x14) - front mic-in
+ * port-E (0x1c) - rear mic-in
+ * port-F (0x16) - CD / ext out
+ * port-C (0x15) - rear line-in
+ * port-D (0x12) - rear line-out
+ * port-A (0x11) - front hp-out
+ *
+ * AD1984A = AD1884A + digital-mic
+ * AD1883 = equivalent with AD1984A
+ * AD1984B = AD1984A + extra SPDIF-out
*/
-static const struct hda_verb ad1884_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1884_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-
-static const char * const ad1884_slave_vols[] = {
- "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD",
- "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958",
- NULL
-};
-
-enum {
- AD1884_AUTO,
- AD1884_BASIC,
- AD1884_MODELS
-};
-
-static const char * const ad1884_models[AD1884_MODELS] = {
- [AD1884_AUTO] = "auto",
- [AD1884_BASIC] = "basic",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
/* set the upper-limit for mixer amp to 0dB for avoiding the possible
* damage by overloading
@@ -3599,14 +930,34 @@ static void ad1884_fixup_amp_override(struct hda_codec *codec,
(1 << AC_AMPCAP_MUTE_SHIFT));
}
+/* toggle GPIO1 according to the mute state */
+static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct ad198x_spec *spec = codec->spec;
+
+ if (spec->eapd_nid)
+ ad_vmaster_eapd_hook(private_data, enabled);
+ snd_hda_codec_update_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA,
+ enabled ? 0x00 : 0x02);
+}
+
static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct ad198x_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init_verbs[] = {
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
+ {},
+ };
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
- spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
+ snd_hda_sequence_write_cache(codec, gpio_init_verbs);
break;
case HDA_FIXUP_ACT_PROBE:
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
@@ -3617,9 +968,18 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
}
}
+/* set magic COEFs for dmic */
+static const struct hda_verb ad1884_dmic_init_verbs[] = {
+ {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
+ {0x01, AC_VERB_SET_PROC_COEF, 0x08},
+ {}
+};
+
enum {
AD1884_FIXUP_AMP_OVERRIDE,
AD1884_FIXUP_HP_EAPD,
+ AD1884_FIXUP_DMIC_COEF,
+ AD1884_FIXUP_HP_TOUCHSMART,
};
static const struct hda_fixup ad1884_fixups[] = {
@@ -3633,15 +993,27 @@ static const struct hda_fixup ad1884_fixups[] = {
.chained = true,
.chain_id = AD1884_FIXUP_AMP_OVERRIDE,
},
+ [AD1884_FIXUP_DMIC_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ },
+ [AD1884_FIXUP_HP_TOUCHSMART] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_HP_EAPD,
+ },
};
static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF),
{}
};
-static int ad1884_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err;
@@ -3674,1170 +1046,6 @@ static int ad1884_parse_auto_config(struct hda_codec *codec)
return err;
}
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1884_basic(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
- spec->multiout.dac_nids = ad1884_dac_nids;
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
- spec->adc_nids = ad1884_adc_nids;
- spec->capsrc_nids = ad1884_capsrc_nids;
- spec->input_mux = &ad1884_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_PM
- spec->loopback.amplist = ad1884_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
- /* we need to cover all playback volumes */
- spec->slave_vols = ad1884_slave_vols;
- /* slaves may contain input volumes, so we can't raise to 0dB blindly */
- spec->avoid_init_slave_vol = 1;
-
- codec->patch_ops = ad198x_patch_ops;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-static int patch_ad1884(struct hda_codec *codec)
-{
- int board_config;
-
- board_config = snd_hda_check_board_config(codec, AD1884_MODELS,
- ad1884_models, NULL);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1884_AUTO;
- }
-
- if (board_config == AD1884_AUTO)
- return ad1884_parse_auto_config(codec);
- else
- return patch_ad1884_basic(codec);
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1884 ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-/*
- * Lenovo Thinkpad T61/X61
- */
-static const struct hda_input_mux ad1984_thinkpad_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Mix", 0x3 },
- { "Dock Mic", 0x4 },
- },
-};
-
-
-/*
- * Dell Precision T3400
- */
-static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Line-In", 0x1 },
- { "Mix", 0x3 },
- },
-};
-
-
-static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/* additional verbs */
-static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
- /* Port-E (docking station mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* docking mic boost */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Analog PC Beeper - allow firmware/ACPI beeps */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
- /* Analog mixer - docking mic; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* enable EAPD bit */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- { } /* end */
-};
-
-/*
- * Dell Precision T3400
- */
-static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* Digial MIC ADC NID 0x05 + 0x06 */
-static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
- return 0;
-}
-
-static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x05,
- .ops = {
- .prepare = ad1984_pcm_dmic_prepare,
- .cleanup = ad1984_pcm_dmic_cleanup
- },
-};
-
-static int ad1984_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info;
- int err;
-
- err = ad198x_build_pcms(codec);
- if (err < 0)
- return err;
-
- info = spec->pcm_rec + codec->num_pcms;
- codec->num_pcms++;
- info->name = "AD1984 Digital Mic";
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
- return 0;
-}
-
-/* models */
-enum {
- AD1984_AUTO,
- AD1984_BASIC,
- AD1984_THINKPAD,
- AD1984_DELL_DESKTOP,
- AD1984_MODELS
-};
-
-static const char * const ad1984_models[AD1984_MODELS] = {
- [AD1984_AUTO] = "auto",
- [AD1984_BASIC] = "basic",
- [AD1984_THINKPAD] = "thinkpad",
- [AD1984_DELL_DESKTOP] = "dell_desktop",
-};
-
-static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
- /* Lenovo Thinkpad T61/X61 */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
- SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
- SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
- {}
-};
-
-static int patch_ad1984(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int board_config, err;
-
- board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
- ad1984_models, ad1984_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1984_AUTO;
- }
-
- if (board_config == AD1984_AUTO)
- return ad1884_parse_auto_config(codec);
-
- err = patch_ad1884_basic(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- switch (board_config) {
- case AD1984_BASIC:
- /* additional digital mics */
- spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
- codec->patch_ops.build_pcms = ad1984_build_pcms;
- break;
- case AD1984_THINKPAD:
- if (codec->subsystem_id == 0x17aa20fb) {
- /* Thinpad X300 does not have the ability to do SPDIF,
- or attach to docking station to use SPDIF */
- spec->multiout.dig_out_nid = 0;
- } else
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->input_mux = &ad1984_thinkpad_capture_source;
- spec->mixers[0] = ad1984_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
- spec->analog_beep = 1;
- break;
- case AD1984_DELL_DESKTOP:
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984_dell_desktop_capture_source;
- spec->mixers[0] = ad1984_dell_desktop_mixers;
- break;
- }
- return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1984 ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
-/*
- * AD1883 / AD1884A / AD1984A / AD1984B
- *
- * port-B (0x14) - front mic-in
- * port-E (0x1c) - rear mic-in
- * port-F (0x16) - CD / ext out
- * port-C (0x15) - rear line-in
- * port-D (0x12) - rear line-out
- * port-A (0x11) - front hp-out
- *
- * AD1984A = AD1884A + digital-mic
- * AD1883 = equivalent with AD1984A
- * AD1984B = AD1984A + extra SPDIF-out
- *
- * FIXME:
- * We share the single DAC for both HP and line-outs (see AD1884/1984).
- */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const hda_nid_t ad1884a_dac_nids[1] = {
- 0x03,
-};
-
-#define ad1884a_adc_nids ad1884_adc_nids
-#define ad1884a_capsrc_nids ad1884_capsrc_nids
-
-#define AD1884A_SPDIF_OUT 0x02
-
-static const struct hda_input_mux ad1884a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x4 },
- { "Line", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static const struct hda_verb ad1884a_init_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-D (Line-out) mixer - route only from analog mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer - route only from analog mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-E (rear mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
- /* Port-F (CD) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* SPDIF output amp */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1884a_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-
-/*
- * Laptop model
- *
- * Port A: Headphone jack
- * Port B: MIC jack
- * Port C: Internal MIC
- * Port D: Dock Line Out (if enabled)
- * Port E: Dock Line In (if enabled)
- * Port F: Internal speakers
- */
-
-static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- int mute = (!ucontrol->value.integer.value[0] &&
- !ucontrol->value.integer.value[1]);
- /* toggle GPIO1 according to the mute state */
- snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- mute ? 0x02 : 0x0);
- return ret;
-}
-
-static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1884a_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_hp_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x14);
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 1);
-}
-
-#define AD1884A_HP_EVENT 0x37
-#define AD1884A_MIC_EVENT 0x36
-
-/* unsolicited event for HP jack sensing */
-static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1884a_hp_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1884a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1884a_hp_automic(codec);
- return 0;
-}
-
-/* mute internal speaker if HP or docking HP is plugged */
-static void ad1884a_laptop_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- if (!present)
- present = snd_hda_jack_detect(codec, 0x12);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_laptop_automic(struct hda_codec *codec)
-{
- unsigned int idx;
-
- if (snd_hda_jack_detect(codec, 0x14))
- idx = 0;
- else if (snd_hda_jack_detect(codec, 0x1c))
- idx = 4;
- else
- idx = 1;
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_laptop_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1884a_laptop_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1884a_laptop_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_laptop_automute(codec);
- ad1884a_laptop_automic(codec);
- return 0;
-}
-
-/* additional verbs for laptop model */
-static const struct hda_verb ad1884a_laptop_verbs[] = {
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F (int speaker) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* required for compaq 6530s/6531s speaker output */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-C pin - internal mic-in */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-D (docking line-out) pin - default unmuted */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-static const struct hda_verb ad1884a_mobile_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-B (mic jack) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-C (int mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-/*
- * Thinkpad X300
- * 0x11 - HP
- * 0x12 - speaker
- * 0x14 - mic-in
- * 0x17 - built-in mic
- */
-
-static const struct hda_verb ad1984a_thinkpad_verbs[] = {
- /* HP unmute */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* turn on EAPD */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
- {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
- {0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x5 },
- { "Mix", 0x3 },
- },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_thinkpad_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1884A_HP_EVENT)
- return;
- ad1984a_thinkpad_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_thinkpad_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1984a_thinkpad_automute(codec);
- return 0;
-}
-
-/*
- * Precision R5500
- * 0x12 - HP/line-out
- * 0x13 - speaker (mono)
- * 0x15 - mic-in
- */
-
-static const struct hda_verb ad1984a_precision_verbs[] = {
- /* Unmute main output path */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Select mic as input */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
- /* Configure as mic */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* HP unmute */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* turn on EAPD */
- {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- /* unsolicited event for pin-sense */
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_precision_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x12);
- snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_precision_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1884A_HP_EVENT)
- return;
- ad1984a_precision_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_precision_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1984a_precision_automute(codec);
- return 0;
-}
-
-
-/*
- * HP Touchsmart
- * port-A (0x11) - front hp-out
- * port-B (0x14) - unused
- * port-C (0x15) - unused
- * port-D (0x12) - rear line out
- * port-E (0x1c) - front mic-in
- * port-F (0x16) - Internal speakers
- * digital-mic (0x17) - Internal mic
- */
-
-static const struct hda_verb ad1984a_touchsmart_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-E (int speaker) mixer - route only from analog mixer */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
- /* Port-E pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
- {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
- {0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .name = "Master Playback Switch",
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* switch to external mic if plugged */
-static void ad1984a_touchsmart_automic(struct hda_codec *codec)
-{
- if (snd_hda_jack_detect(codec, 0x1c))
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x4);
- else
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x5);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1984a_touchsmart_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_touchsmart_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1984a_touchsmart_automic(codec);
- return 0;
-}
-
-
-/*
- */
-
-enum {
- AD1884A_AUTO,
- AD1884A_DESKTOP,
- AD1884A_LAPTOP,
- AD1884A_MOBILE,
- AD1884A_THINKPAD,
- AD1984A_TOUCHSMART,
- AD1984A_PRECISION,
- AD1884A_MODELS
-};
-
-static const char * const ad1884a_models[AD1884A_MODELS] = {
- [AD1884A_AUTO] = "auto",
- [AD1884A_DESKTOP] = "desktop",
- [AD1884A_LAPTOP] = "laptop",
- [AD1884A_MOBILE] = "mobile",
- [AD1884A_THINKPAD] = "thinkpad",
- [AD1984A_TOUCHSMART] = "touchsmart",
- [AD1984A_PRECISION] = "precision",
-};
-
-static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
- SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
- SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
- {}
-};
-
-static int patch_ad1884a(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
- ad1884a_models,
- ad1884a_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1884A_AUTO;
- }
-
- if (board_config == AD1884A_AUTO)
- return ad1884_parse_auto_config(codec);
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
- spec->multiout.dac_nids = ad1884a_dac_nids;
- spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
- spec->adc_nids = ad1884a_adc_nids;
- spec->capsrc_nids = ad1884a_capsrc_nids;
- spec->input_mux = &ad1884a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884a_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884a_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_PM
- spec->loopback.amplist = ad1884a_loopbacks;
-#endif
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- switch (board_config) {
- case AD1884A_LAPTOP:
- spec->mixers[0] = ad1884a_laptop_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
- codec->patch_ops.init = ad1884a_laptop_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_MOBILE:
- spec->mixers[0] = ad1884a_mobile_mixers;
- spec->init_verbs[0] = ad1884a_mobile_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
- codec->patch_ops.init = ad1884a_hp_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_THINKPAD:
- spec->mixers[0] = ad1984a_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1984a_thinkpad_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984a_thinkpad_capture_source;
- codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
- codec->patch_ops.init = ad1984a_thinkpad_init;
- break;
- case AD1984A_PRECISION:
- spec->mixers[0] = ad1984a_precision_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1984a_precision_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
- codec->patch_ops.init = ad1984a_precision_init;
- break;
- case AD1984A_TOUCHSMART:
- spec->mixers[0] = ad1984a_touchsmart_mixers;
- spec->init_verbs[0] = ad1984a_touchsmart_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
- codec->patch_ops.init = ad1984a_touchsmart_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1884a ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
/*
* AD1882 / AD1882A
*
@@ -4850,299 +1058,7 @@ static int patch_ad1884a(struct hda_codec *codec)
* port-G - rear clfe-out (6stack)
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const hda_nid_t ad1882_dac_nids[3] = {
- 0x04, 0x03, 0x05
-};
-
-static const hda_nid_t ad1882_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static const hda_nid_t ad1882_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1882_SPDIF_OUT 0x02
-
-/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
-static const struct hda_input_mux ad1882_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4 },
- { "Line", 0x2 },
- { "CD", 0x3 },
- { "Mix", 0x7 },
- },
-};
-
-/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
-static const struct hda_input_mux ad1882a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4},
- { "Line", 0x2 },
- { "Digital Mic", 0x06 },
- { "Mix", 0x7 },
- },
-};
-
-static const struct snd_kcontrol_new ad1882_base_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
- HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-/* simple auto-mute control for AD1882 3-stack board */
-#define AD1882_HP_EVENT 0x01
-
-static void ad1882_3stack_automute(struct hda_codec *codec)
-{
- bool mute = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- mute ? 0 : PIN_OUT);
-}
-
-static int ad1882_3stack_automute_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1882_3stack_automute(codec);
- return 0;
-}
-
-static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (res >> 26) {
- case AD1882_HP_EVENT:
- ad1882_3stack_automute(codec);
- break;
- }
-}
-
-static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
- HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static const struct hda_verb ad1882_ch2_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static const struct hda_verb ad1882_ch4_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static const struct hda_verb ad1882_ch6_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { } /* end */
-};
-
-static const struct hda_channel_mode ad1882_modes[3] = {
- { 2, ad1882_ch2_init },
- { 4, ad1882_ch4_init },
- { 6, ad1882_ch6_init },
-};
-
-/*
- * initialization verbs
- */
-static const struct hda_verb ad1882_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C (line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C mixer - mute as input */
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-E (mic-in) pin */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-E mixer - mute as input */
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-F (surround) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-G (CLFE) */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-static const struct hda_verb ad1882_3stack_automute_verbs[] = {
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT},
- { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1882_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 4 }, /* Line */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
-
-/* models */
-enum {
- AD1882_AUTO,
- AD1882_3STACK,
- AD1882_6STACK,
- AD1882_3STACK_AUTOMUTE,
- AD1882_MODELS
-};
-
-static const char * const ad1882_models[AD1986A_MODELS] = {
- [AD1882_AUTO] = "auto",
- [AD1882_3STACK] = "3stack",
- [AD1882_6STACK] = "6stack",
- [AD1882_3STACK_AUTOMUTE] = "3stack-automute",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-static int ad1882_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1882(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err;
@@ -5169,110 +1085,20 @@ static int ad1882_parse_auto_config(struct hda_codec *codec)
return err;
}
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1882(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
- ad1882_models, NULL);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1882_AUTO;
- }
-
- if (board_config == AD1882_AUTO)
- return ad1882_parse_auto_config(codec);
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- spec->multiout.dac_nids = ad1882_dac_nids;
- spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
- spec->adc_nids = ad1882_adc_nids;
- spec->capsrc_nids = ad1882_capsrc_nids;
- if (codec->vendor_id == 0x11d41882)
- spec->input_mux = &ad1882_capture_source;
- else
- spec->input_mux = &ad1882a_capture_source;
- spec->num_mixers = 2;
- spec->mixers[0] = ad1882_base_mixers;
- if (codec->vendor_id == 0x11d41882)
- spec->mixers[1] = ad1882_loopback_mixers;
- else
- spec->mixers[1] = ad1882a_loopback_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1882_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_PM
- spec->loopback.amplist = ad1882_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- switch (board_config) {
- default:
- case AD1882_3STACK:
- case AD1882_3STACK_AUTOMUTE:
- spec->num_mixers = 3;
- spec->mixers[2] = ad1882_3stack_mixers;
- spec->channel_mode = ad1882_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
- spec->need_dac_fix = 1;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- if (board_config != AD1882_3STACK) {
- spec->init_verbs[spec->num_init_verbs++] =
- ad1882_3stack_automute_verbs;
- codec->patch_ops.unsol_event = ad1882_3stack_unsol_event;
- codec->patch_ops.init = ad1882_3stack_automute_init;
- }
- break;
- case AD1882_6STACK:
- spec->num_mixers = 3;
- spec->mixers[2] = ad1882_6stack_mixers;
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1882 ad1882_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
/*
* patch entries
*/
static const struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
+ { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
- { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
+ { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
- { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
- { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
+ { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
+ { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
- { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
+ { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index de00ce166470..4edd2d0f9a3c 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -66,6 +66,8 @@ struct conexant_spec {
hda_nid_t eapds[4];
bool dynamic_eapd;
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
#ifdef ENABLE_CXT_STATIC_QUIRKS
const struct snd_kcontrol_new *mixers[5];
int num_mixers;
@@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec)
snd_hda_gen_init(codec);
if (!spec->dynamic_eapd)
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
return 0;
}
@@ -3224,6 +3229,8 @@ enum {
CXT_PINCFG_LEMOTE_A1205,
CXT_FIXUP_STEREO_DMIC,
CXT_FIXUP_INC_MIC_BOOST,
+ CXT_FIXUP_HEADPHONE_MIC_PIN,
+ CXT_FIXUP_HEADPHONE_MIC,
};
static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
@@ -3246,6 +3253,59 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec,
(0 << AC_AMPCAP_MUTE_SHIFT));
}
+static void cxt_update_headset_mode(struct hda_codec *codec)
+{
+ /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
+ int i;
+ bool mic_mode = false;
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].pin == mux_pin) {
+ mic_mode = !!cfg->inputs[i].is_headphone_mic;
+ break;
+ }
+
+ if (mic_mode) {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = false;
+ } else {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
+ }
+
+ snd_hda_gen_update_outputs(codec);
+}
+
+static void cxt_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ cxt_update_headset_mode(codec);
+}
+
+static void cxt_fixup_headphone_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
+ spec->gen.automute_hook = cxt_update_headset_mode;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cxt_update_headset_mode(codec);
+ break;
+ }
+}
+
+
/* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
@@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt5066_increase_mic_boost,
},
+ [CXT_FIXUP_HEADPHONE_MIC_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_HEADPHONE_MIC,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ }
+ },
+ [CXT_FIXUP_HEADPHONE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_headphone_mic,
+ },
};
static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
@@ -3395,7 +3469,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
if (err < 0)
goto error;
@@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
codec->bus->allow_bus_reset = 1;
}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 030ca8652a1c..895a0d3320b4 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -959,6 +959,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
int pin_nid;
int pin_idx;
struct hda_jack_tbl *jack;
+ int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
if (!jack)
@@ -967,8 +968,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
jack->jack_dirty = 1;
_snd_printd(SND_PR_VERBOSE,
- "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- codec->addr, pin_nid,
+ "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, pin_nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
!!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
pin_idx = pin_nid_to_pin_index(spec, pin_nid);
@@ -1989,8 +1990,10 @@ static int patch_generic_hdmi(struct hda_codec *codec)
return -EINVAL;
}
codec->patch_ops = generic_hdmi_patch_ops;
- if (codec->vendor_id == 0x80862807)
+ if (codec->vendor_id == 0x80862807) {
codec->patch_ops.set_power_state = haswell_set_power_state;
+ codec->dp_mst = true;
+ }
generic_hdmi_init_per_pins(codec);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f303cd898515..4a909170b59e 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -282,6 +282,7 @@ static void alc_eapd_shutup(struct hda_codec *codec)
{
alc_auto_setup_eapd(codec, false);
msleep(200);
+ snd_hda_shutup_pins(codec);
}
/* generic EAPD initialization */
@@ -826,7 +827,8 @@ static inline void alc_shutup(struct hda_codec *codec)
if (spec && spec->shutup)
spec->shutup(codec);
- snd_hda_shutup_pins(codec);
+ else
+ snd_hda_shutup_pins(codec);
}
#define alc_free snd_hda_gen_free
@@ -1853,8 +1855,10 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->gen.no_primary_hp = 1;
+ spec->gen.no_multi_io = 1;
+ }
}
static const struct hda_fixup alc882_fixups[] = {
@@ -2533,6 +2537,7 @@ enum {
ALC269_TYPE_ALC269VD,
ALC269_TYPE_ALC280,
ALC269_TYPE_ALC282,
+ ALC269_TYPE_ALC283,
ALC269_TYPE_ALC284,
ALC269_TYPE_ALC286,
};
@@ -2558,6 +2563,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC269VB:
case ALC269_TYPE_ALC269VD:
case ALC269_TYPE_ALC282:
+ case ALC269_TYPE_ALC283:
case ALC269_TYPE_ALC286:
ssids = alc269_ssids;
break;
@@ -2583,15 +2589,81 @@ static void alc269_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- if (spec->codec_variant != ALC269_TYPE_ALC269VB)
- return;
-
if (spec->codec_variant == ALC269_TYPE_ALC269VB)
alc269vb_toggle_power_output(codec, 0);
if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
(alc_get_coef0(codec) & 0x00ff) == 0x018) {
msleep(150);
}
+ snd_hda_shutup_pins(codec);
+}
+
+static void alc283_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_pin_sense;
+ int val;
+
+ if (!hp_pin)
+ return;
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ if (hp_pin_sense)
+ msleep(85);
+ /* Index 0x46 Combo jack auto switch control 2 */
+ /* 3k pull low control for Headset jack. */
+ val = alc_read_coef_idx(codec, 0x46);
+ alc_write_coef_idx(codec, 0x46, val & ~(3 << 12));
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
+
+static void alc283_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_pin_sense;
+ int val;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ val = alc_read_coef_idx(codec, 0x46);
+ alc_write_coef_idx(codec, 0x46, val | (3 << 12));
+
+ if (hp_pin_sense)
+ msleep(85);
+ snd_hda_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x43, 0x9614);
}
static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
@@ -2722,6 +2794,7 @@ static int alc269_resume(struct hda_codec *codec)
hda_call_check_power_status(codec, 0x01);
if (spec->has_alc5505_dsp)
alc5505_dsp_resume(codec);
+
return 0;
}
#endif /* CONFIG_PM */
@@ -3261,6 +3334,28 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
alc_fixup_headset_mode(codec, fix, action);
}
+/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */
+static int find_ext_mic_pin(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ hda_nid_t nid;
+ unsigned int defcfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ nid = cfg->inputs[i].pin;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+ continue;
+ return nid;
+ }
+
+ return 0;
+}
+
static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
@@ -3268,11 +3363,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PROBE) {
- if (snd_BUG_ON(!spec->gen.am_entry[1].pin ||
- !spec->gen.autocfg.hp_pins[0]))
+ int mic_pin = find_ext_mic_pin(codec);
+ int hp_pin = spec->gen.autocfg.hp_pins[0];
+
+ if (snd_BUG_ON(!mic_pin || !hp_pin))
return;
- snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin,
- spec->gen.autocfg.hp_pins[0]);
+ snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
}
}
@@ -3308,6 +3404,45 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
}
}
+static void alc283_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+
+ msleep(600);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
+
+static void alc283_chromebook_caps(struct hda_codec *codec)
+{
+ snd_hda_override_wcaps(codec, 0x03, 0);
+}
+
+static void alc283_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ int val;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ alc283_chromebook_caps(codec);
+ spec->gen.hp_automute_hook = alc283_hp_automute_hook;
+ /* MIC2-VREF control */
+ /* Set to manual mode */
+ val = alc_read_coef_idx(codec, 0x06);
+ alc_write_coef_idx(codec, 0x06, val & ~0x000c);
+ break;
+ }
+}
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -3344,6 +3479,7 @@ enum {
ALC269_FIXUP_ACER_AC700,
ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
ALC269VB_FIXUP_ORDISSIMO_EVE2,
+ ALC283_FIXUP_CHROME_BOOK,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -3595,11 +3731,20 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
},
},
+ [ALC283_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_chromebook,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
+ SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -3637,6 +3782,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -3655,11 +3801,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
- SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
- SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
- SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
- SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
- SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
@@ -3670,8 +3811,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
@@ -3840,11 +3989,15 @@ static int patch_alc269(struct hda_codec *codec)
case 0x10ec0290:
spec->codec_variant = ALC269_TYPE_ALC280;
break;
- case 0x10ec0233:
case 0x10ec0282:
- case 0x10ec0283:
spec->codec_variant = ALC269_TYPE_ALC282;
break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ spec->codec_variant = ALC269_TYPE_ALC283;
+ spec->shutup = alc283_shutup;
+ spec->init_hook = alc283_init;
+ break;
case 0x10ec0284:
case 0x10ec0292:
spec->codec_variant = ALC269_TYPE_ALC284;
@@ -3872,7 +4025,8 @@ static int patch_alc269(struct hda_codec *codec)
codec->patch_ops.suspend = alc269_suspend;
codec->patch_ops.resume = alc269_resume;
#endif
- spec->shutup = alc269_shutup;
+ if (!spec->shutup)
+ spec->shutup = alc269_shutup;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 6d1924c19abf..fba0cef1c47f 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -158,6 +158,7 @@ enum {
STAC_D965_VERBS,
STAC_DELL_3ST,
STAC_DELL_BIOS,
+ STAC_DELL_BIOS_AMIC,
STAC_DELL_BIOS_SPDIF,
STAC_927X_DELL_DMIC,
STAC_927X_VOLKNOB,
@@ -3231,8 +3232,6 @@ static const struct hda_fixup stac927x_fixups[] = {
[STAC_DELL_BIOS] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
- /* configure the analog microphone on some laptops */
- { 0x0c, 0x90a79130 },
/* correct the front output jack as a hp out */
{ 0x0f, 0x0221101f },
/* correct the front input jack as a mic */
@@ -3242,6 +3241,16 @@ static const struct hda_fixup stac927x_fixups[] = {
.chained = true,
.chain_id = STAC_927X_DELL_DMIC,
},
+ [STAC_DELL_BIOS_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* configure the analog microphone on some laptops */
+ { 0x0c, 0x90a79130 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_DELL_BIOS,
+ },
[STAC_DELL_BIOS_SPDIF] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -3270,6 +3279,7 @@ static const struct hda_model_fixup stac927x_models[] = {
{ .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" },
{ .id = STAC_DELL_3ST, .name = "dell-3stack" },
{ .id = STAC_DELL_BIOS, .name = "dell-bios" },
+ { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" },
{ .id = STAC_927X_VOLKNOB, .name = "volknob" },
{}
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index e2481baddc70..0bc20ef5687a 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -207,9 +207,9 @@ static void vt1708_stop_hp_work(struct hda_codec *codec)
return;
if (spec->hp_work_active) {
snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
+ codec->jackpoll_interval = 0;
cancel_delayed_work_sync(&codec->jackpoll_work);
spec->hp_work_active = false;
- codec->jackpoll_interval = 0;
}
}
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 2a8ad9d1a2ae..bb9ebc5543d7 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/module.h>
+#include <linux/vmalloc.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -198,6 +199,31 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");
#define RME96_AD1852_VOL_BITS 14
#define RME96_AD1855_VOL_BITS 10
+/* Defines for snd_rme96_trigger */
+#define RME96_TB_START_PLAYBACK 1
+#define RME96_TB_START_CAPTURE 2
+#define RME96_TB_STOP_PLAYBACK 4
+#define RME96_TB_STOP_CAPTURE 8
+#define RME96_TB_RESET_PLAYPOS 16
+#define RME96_TB_RESET_CAPTUREPOS 32
+#define RME96_TB_CLEAR_PLAYBACK_IRQ 64
+#define RME96_TB_CLEAR_CAPTURE_IRQ 128
+#define RME96_RESUME_PLAYBACK (RME96_TB_START_PLAYBACK)
+#define RME96_RESUME_CAPTURE (RME96_TB_START_CAPTURE)
+#define RME96_RESUME_BOTH (RME96_RESUME_PLAYBACK \
+ | RME96_RESUME_CAPTURE)
+#define RME96_START_PLAYBACK (RME96_TB_START_PLAYBACK \
+ | RME96_TB_RESET_PLAYPOS)
+#define RME96_START_CAPTURE (RME96_TB_START_CAPTURE \
+ | RME96_TB_RESET_CAPTUREPOS)
+#define RME96_START_BOTH (RME96_START_PLAYBACK \
+ | RME96_START_CAPTURE)
+#define RME96_STOP_PLAYBACK (RME96_TB_STOP_PLAYBACK \
+ | RME96_TB_CLEAR_PLAYBACK_IRQ)
+#define RME96_STOP_CAPTURE (RME96_TB_STOP_CAPTURE \
+ | RME96_TB_CLEAR_CAPTURE_IRQ)
+#define RME96_STOP_BOTH (RME96_STOP_PLAYBACK \
+ | RME96_STOP_CAPTURE)
struct rme96 {
spinlock_t lock;
@@ -214,6 +240,13 @@ struct rme96 {
u8 rev; /* card revision number */
+#ifdef CONFIG_PM
+ u32 playback_pointer;
+ u32 capture_pointer;
+ void *playback_suspend_buffer;
+ void *capture_suspend_buffer;
+#endif
+
struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream;
@@ -344,6 +377,8 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -373,6 +408,8 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -402,6 +439,8 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -427,6 +466,8 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -1045,54 +1086,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
}
static void
-snd_rme96_playback_start(struct rme96 *rme96,
- int from_pause)
+snd_rme96_trigger(struct rme96 *rme96,
+ int op)
{
- if (!from_pause) {
+ if (op & RME96_TB_RESET_PLAYPOS)
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
- }
-
- rme96->wcreg |= RME96_WCR_START;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
-
-static void
-snd_rme96_capture_start(struct rme96 *rme96,
- int from_pause)
-{
- if (!from_pause) {
+ if (op & RME96_TB_RESET_CAPTUREPOS)
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
- }
-
- rme96->wcreg |= RME96_WCR_START_2;
+ if (op & RME96_TB_CLEAR_PLAYBACK_IRQ) {
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ if (rme96->rcreg & RME96_RCR_IRQ)
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+ }
+ if (op & RME96_TB_CLEAR_CAPTURE_IRQ) {
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ if (rme96->rcreg & RME96_RCR_IRQ_2)
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+ }
+ if (op & RME96_TB_START_PLAYBACK)
+ rme96->wcreg |= RME96_WCR_START;
+ if (op & RME96_TB_STOP_PLAYBACK)
+ rme96->wcreg &= ~RME96_WCR_START;
+ if (op & RME96_TB_START_CAPTURE)
+ rme96->wcreg |= RME96_WCR_START_2;
+ if (op & RME96_TB_STOP_CAPTURE)
+ rme96->wcreg &= ~RME96_WCR_START_2;
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
-static void
-snd_rme96_playback_stop(struct rme96 *rme96)
-{
- /*
- * Check if there is an unconfirmed IRQ, if so confirm it, or else
- * the hardware will not stop generating interrupts
- */
- rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
- if (rme96->rcreg & RME96_RCR_IRQ) {
- writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
- }
- rme96->wcreg &= ~RME96_WCR_START;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
-static void
-snd_rme96_capture_stop(struct rme96 *rme96)
-{
- rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
- if (rme96->rcreg & RME96_RCR_IRQ_2) {
- writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
- }
- rme96->wcreg &= ~RME96_WCR_START_2;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
static irqreturn_t
snd_rme96_interrupt(int irq,
@@ -1155,6 +1177,7 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
spin_lock_irq(&rme96->lock);
if (rme96->playback_substream != NULL) {
spin_unlock_irq(&rme96->lock);
@@ -1191,6 +1214,7 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
runtime->hw = snd_rme96_capture_spdif_info;
if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
(rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0)
@@ -1222,6 +1246,7 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
spin_lock_irq(&rme96->lock);
if (rme96->playback_substream != NULL) {
spin_unlock_irq(&rme96->lock);
@@ -1253,6 +1278,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
runtime->hw = snd_rme96_capture_adat_info;
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
/* makes no sense to use analog input. Note that analog
@@ -1288,7 +1314,7 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
}
rme96->playback_substream = NULL;
rme96->playback_periodsize = 0;
@@ -1309,7 +1335,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
}
rme96->capture_substream = NULL;
rme96->capture_periodsize = 0;
@@ -1324,7 +1350,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
}
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
spin_unlock_irq(&rme96->lock);
@@ -1338,7 +1364,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
}
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
spin_unlock_irq(&rme96->lock);
@@ -1350,41 +1376,55 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *s;
+ bool sync;
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (snd_pcm_substream_chip(s) == rme96)
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ sync = (rme96->playback_substream && rme96->capture_substream) &&
+ (rme96->playback_substream->group ==
+ rme96->capture_substream->group);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!RME96_ISPLAYING(rme96)) {
- if (substream != rme96->playback_substream) {
+ if (substream != rme96->playback_substream)
return -EBUSY;
- }
- snd_rme96_playback_start(rme96, 0);
+ snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
+ : RME96_START_PLAYBACK);
}
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
if (RME96_ISPLAYING(rme96)) {
- if (substream != rme96->playback_substream) {
+ if (substream != rme96->playback_substream)
return -EBUSY;
- }
- snd_rme96_playback_stop(rme96);
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_PLAYBACK);
}
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_stop(rme96);
- }
+ if (RME96_ISPLAYING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_PLAYBACK);
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_start(rme96, 1);
- }
+ if (!RME96_ISPLAYING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
+ : RME96_RESUME_PLAYBACK);
break;
-
+
default:
return -EINVAL;
}
+
return 0;
}
@@ -1393,38 +1433,51 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *s;
+ bool sync;
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (snd_pcm_substream_chip(s) == rme96)
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ sync = (rme96->playback_substream && rme96->capture_substream) &&
+ (rme96->playback_substream->group ==
+ rme96->capture_substream->group);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!RME96_ISRECORDING(rme96)) {
- if (substream != rme96->capture_substream) {
+ if (substream != rme96->capture_substream)
return -EBUSY;
- }
- snd_rme96_capture_start(rme96, 0);
+ snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
+ : RME96_START_CAPTURE);
}
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
if (RME96_ISRECORDING(rme96)) {
- if (substream != rme96->capture_substream) {
+ if (substream != rme96->capture_substream)
return -EBUSY;
- }
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_CAPTURE);
}
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_stop(rme96);
- }
+ if (RME96_ISRECORDING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_CAPTURE);
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_start(rme96, 1);
- }
+ if (!RME96_ISRECORDING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
+ : RME96_RESUME_CAPTURE);
break;
-
+
default:
return -EINVAL;
}
@@ -1505,8 +1558,7 @@ snd_rme96_free(void *private_data)
return;
}
if (rme96->irq >= 0) {
- snd_rme96_playback_stop(rme96);
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_BOTH);
rme96->areg &= ~RME96_AR_DAC_EN;
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
free_irq(rme96->irq, (void *)rme96);
@@ -1520,6 +1572,10 @@ snd_rme96_free(void *private_data)
pci_release_regions(rme96->pci);
rme96->port = 0;
}
+#ifdef CONFIG_PM
+ vfree(rme96->playback_suspend_buffer);
+ vfree(rme96->capture_suspend_buffer);
+#endif
pci_disable_device(rme96->pci);
}
@@ -1606,8 +1662,7 @@ snd_rme96_create(struct rme96 *rme96)
rme96->capture_periodsize = 0;
/* make sure playback/capture is stopped, if by some reason active */
- snd_rme96_playback_stop(rme96);
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_BOTH);
/* set default values in registers */
rme96->wcreg =
@@ -2319,6 +2374,87 @@ snd_rme96_create_switches(struct snd_card *card,
* Card initialisation
*/
+#ifdef CONFIG_PM
+
+static int
+snd_rme96_suspend(struct pci_dev *pci,
+ pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct rme96 *rme96 = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend(rme96->playback_substream);
+ snd_pcm_suspend(rme96->capture_substream);
+
+ /* save capture & playback pointers */
+ rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
+ & RME96_RCR_AUDIO_ADDR_MASK;
+ rme96->capture_pointer = readl(rme96->iobase + RME96_IO_GET_REC_POS)
+ & RME96_RCR_AUDIO_ADDR_MASK;
+
+ /* save playback and capture buffers */
+ memcpy_fromio(rme96->playback_suspend_buffer,
+ rme96->iobase + RME96_IO_PLAY_BUFFER, RME96_BUFFER_SIZE);
+ memcpy_fromio(rme96->capture_suspend_buffer,
+ rme96->iobase + RME96_IO_REC_BUFFER, RME96_BUFFER_SIZE);
+
+ /* disable the DAC */
+ rme96->areg &= ~RME96_AR_DAC_EN;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+ pci_disable_device(pci);
+ pci_save_state(pci);
+
+ return 0;
+}
+
+static int
+snd_rme96_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct rme96 *rme96 = card->private_data;
+
+ pci_restore_state(pci);
+ if (pci_enable_device(pci) < 0) {
+ printk(KERN_ERR "rme96: pci_enable_device failed, disabling device\n");
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+
+ /* reset playback and record buffer pointers */
+ writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS
+ + rme96->playback_pointer);
+ writel(0, rme96->iobase + RME96_IO_SET_REC_POS
+ + rme96->capture_pointer);
+
+ /* restore playback and capture buffers */
+ memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER,
+ rme96->playback_suspend_buffer, RME96_BUFFER_SIZE);
+ memcpy_toio(rme96->iobase + RME96_IO_REC_BUFFER,
+ rme96->capture_suspend_buffer, RME96_BUFFER_SIZE);
+
+ /* reset the ADC */
+ writel(rme96->areg | RME96_AR_PD2,
+ rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+ /* reset and enable DAC, restore analog volume */
+ snd_rme96_reset_dac(rme96);
+ rme96->areg |= RME96_AR_DAC_EN;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ if (RME96_HAS_ANALOG_OUT(rme96)) {
+ usleep_range(3000, 10000);
+ snd_rme96_apply_dac_volume(rme96);
+ }
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+
+#endif
+
static void snd_rme96_card_free(struct snd_card *card)
{
snd_rme96_free(card->private_data);
@@ -2355,6 +2491,23 @@ snd_rme96_probe(struct pci_dev *pci,
return err;
}
+#ifdef CONFIG_PM
+ rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+ if (!rme96->playback_suspend_buffer) {
+ snd_printk(KERN_ERR
+ "Failed to allocate playback suspend buffer!\n");
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+ rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+ if (!rme96->capture_suspend_buffer) {
+ snd_printk(KERN_ERR
+ "Failed to allocate capture suspend buffer!\n");
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+#endif
+
strcpy(card->driver, "Digi96");
switch (rme96->pci->device) {
case PCI_DEVICE_ID_RME_DIGI96:
@@ -2397,6 +2550,10 @@ static struct pci_driver rme96_driver = {
.id_table = snd_rme96_ids,
.probe = snd_rme96_probe,
.remove = snd_rme96_remove,
+#ifdef CONFIG_PM
+ .suspend = snd_rme96_suspend,
+ .resume = snd_rme96_resume,
+#endif
};
module_pci_driver(rme96_driver);
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index bd501931ee23..3cde55b753e2 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -38,6 +38,97 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
+
+/* ************* Register Documentation *******************************************************
+ *
+ * Work in progress! Documentation is based on the code in this file.
+ *
+ * --------- HDSPM_controlRegister ---------
+ * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
+ * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ * : . : . : . : x . : HDSPM_AudioInterruptEnable \_ setting both bits
+ * : . : . : . : . x: HDSPM_Start / enables audio IO
+ * : . : . : . : x. : HDSPM_ClockModeMaster - 1: Master, 0: Slave
+ * : . : . : . : .210 : HDSPM_LatencyMask - 3 Bit value for latency
+ * : . : . : . : . : 0:64, 1:128, 2:256, 3:512,
+ * : . : . : . : . : 4:1024, 5:2048, 6:4096, 7:8192
+ * :x . : . : . x:xx . : HDSPM_FrequencyMask
+ * : . : . : . :10 . : HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=??
+ * : . : . : . x: . : <MADI> HDSPM_DoubleSpeed
+ * :x . : . : . : . : <MADI> HDSPM_QuadSpeed
+ * : . 3 : . 10: 2 . : . : HDSPM_SyncRefMask :
+ * : . : . x: . : . : HDSPM_SyncRef0
+ * : . : . x : . : . : HDSPM_SyncRef1
+ * : . : . : x . : . : <AES32> HDSPM_SyncRef2
+ * : . x : . : . : . : <AES32> HDSPM_SyncRef3
+ * : . : . 10: . : . : <MADI> sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn
+ * : . 3 : . 10: 2 . : . : <AES32> 0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn?
+ * : . x : . : . : . : <MADIe> HDSPe_FLOAT_FORMAT
+ * : . : . : x . : . : <MADI> HDSPM_InputSelect0 : 0=optical,1=coax
+ * : . : . :x . : . : <MADI> HDSPM_InputSelect1
+ * : . : .x : . : . : <MADI> HDSPM_clr_tms
+ * : . : . : . x : . : <MADI> HDSPM_TX_64ch
+ * : . : . : . x : . : <AES32> HDSPM_Emphasis
+ * : . : . : .x : . : <MADI> HDSPM_AutoInp
+ * : . : . x : . : . : <MADI> HDSPM_SMUX
+ * : . : .x : . : . : <MADI> HDSPM_clr_tms
+ * : . : x. : . : . : <MADI> HDSPM_taxi_reset
+ * : . x: . : . : . : <MADI> HDSPM_LineOut
+ * : . x: . : . : . : <AES32> ??????????????????
+ * : . : x. : . : . : <AES32> HDSPM_WCK48
+ * : . : . : .x : . : <AES32> HDSPM_Dolby
+ * : . : x . : . : . : HDSPM_Midi0InterruptEnable
+ * : . :x . : . : . : HDSPM_Midi1InterruptEnable
+ * : . : x . : . : . : HDSPM_Midi2InterruptEnable
+ * : . x : . : . : . : <MADI> HDSPM_Midi3InterruptEnable
+ * : . x : . : . : . : <AES32> HDSPM_DS_DoubleWire
+ * : .x : . : . : . : <AES32> HDSPM_QS_DoubleWire
+ * : x. : . : . : . : <AES32> HDSPM_QS_QuadWire
+ * : . : . : . x : . : <AES32> HDSPM_Professional
+ * : x . : . : . : . : HDSPM_wclk_sel
+ * : . : . : . : . :
+ * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
+ * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit
+ *
+ *
+ *
+ * AIO / RayDAT only
+ *
+ * ------------ HDSPM_WR_SETTINGS ----------
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
+ * :1098.7654:3210.9876:5432.1098:7654.3210:
+ * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
+ * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ * : . : . : . : . x: HDSPM_c0Master 1: Master, 0: Slave
+ * : . : . : . : . x : HDSPM_c0_SyncRef0
+ * : . : . : . : . x : HDSPM_c0_SyncRef1
+ * : . : . : . : .x : HDSPM_c0_SyncRef2
+ * : . : . : . : x. : HDSPM_c0_SyncRef3
+ * : . : . : . : 3.210 : HDSPM_c0_SyncRefMask:
+ * : . : . : . : . : RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4,
+ * : . : . : . : . : 9:TCO, 10:SyncIn
+ * : . : . : . : . : AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT,
+ * : . : . : . : . : 9:TCO, 10:SyncIn
+ * : . : . : . : . :
+ * : . : . : . : . :
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
+ * :1098.7654:3210.9876:5432.1098:7654.3210:
+ * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
+ * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ *
+ */
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -95,7 +186,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_controlRegister 64
#define HDSPM_interruptConfirmation 96
#define HDSPM_control2Reg 256 /* not in specs ???????? */
-#define HDSPM_freqReg 256 /* for AES32 */
+#define HDSPM_freqReg 256 /* for setting arbitrary clock values (DDS feature) */
#define HDSPM_midiDataOut0 352 /* just believe in old code */
#define HDSPM_midiDataOut1 356
#define HDSPM_eeprom_wr 384 /* for AES32 */
@@ -258,6 +349,25 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_wclk_sel (1<<30)
+/* additional control register bits for AIO*/
+#define HDSPM_c0_Wck48 0x20 /* also RayDAT */
+#define HDSPM_c0_Input0 0x1000
+#define HDSPM_c0_Input1 0x2000
+#define HDSPM_c0_Spdif_Opt 0x4000
+#define HDSPM_c0_Pro 0x8000
+#define HDSPM_c0_clr_tms 0x10000
+#define HDSPM_c0_AEB1 0x20000
+#define HDSPM_c0_AEB2 0x40000
+#define HDSPM_c0_LineOut 0x80000
+#define HDSPM_c0_AD_GAIN0 0x100000
+#define HDSPM_c0_AD_GAIN1 0x200000
+#define HDSPM_c0_DA_GAIN0 0x400000
+#define HDSPM_c0_DA_GAIN1 0x800000
+#define HDSPM_c0_PH_GAIN0 0x1000000
+#define HDSPM_c0_PH_GAIN1 0x2000000
+#define HDSPM_c0_Sym6db 0x4000000
+
+
/* --- bit helper defines */
#define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|\
@@ -341,11 +451,11 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */
#define HDSPM_madiSync (1<<18) /* MADI is in sync */
-#define HDSPM_tcoLock 0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */
-#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status */
+#define HDSPM_tcoLockMadi 0x00000020 /* Optional TCO locked status for HDSPe MADI*/
+#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/
-#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */
-#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */
+#define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */
+#define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */
#define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
/* since 64byte accurate, last 6 bits are not used */
@@ -363,7 +473,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
* Interrupt
*/
#define HDSPM_tco_detect 0x08000000
-#define HDSPM_tco_lock 0x20000000
+#define HDSPM_tcoLockAes 0x20000000 /* Optional TCO locked status for HDSPe AES */
#define HDSPM_s2_tco_detect 0x00000040
#define HDSPM_s2_AEBO_D 0x00000080
@@ -461,7 +571,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
#define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
#define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
-#define HDSPM_AES32_AUTOSYNC_FROM_NONE 9
+#define HDSPM_AES32_AUTOSYNC_FROM_TCO 9
+#define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10
+#define HDSPM_AES32_AUTOSYNC_FROM_NONE 11
/* status2 */
/* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
@@ -537,36 +649,39 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
/* names for speed modes */
static char *hdspm_speed_names[] = { "single", "double", "quad" };
-static char *texts_autosync_aes_tco[] = { "Word Clock",
+static const char *const texts_autosync_aes_tco[] = { "Word Clock",
"AES1", "AES2", "AES3", "AES4",
"AES5", "AES6", "AES7", "AES8",
- "TCO" };
-static char *texts_autosync_aes[] = { "Word Clock",
+ "TCO", "Sync In"
+};
+static const char *const texts_autosync_aes[] = { "Word Clock",
"AES1", "AES2", "AES3", "AES4",
- "AES5", "AES6", "AES7", "AES8" };
-static char *texts_autosync_madi_tco[] = { "Word Clock",
+ "AES5", "AES6", "AES7", "AES8",
+ "Sync In"
+};
+static const char *const texts_autosync_madi_tco[] = { "Word Clock",
"MADI", "TCO", "Sync In" };
-static char *texts_autosync_madi[] = { "Word Clock",
+static const char *const texts_autosync_madi[] = { "Word Clock",
"MADI", "Sync In" };
-static char *texts_autosync_raydat_tco[] = {
+static const char *const texts_autosync_raydat_tco[] = {
"Word Clock",
"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
"AES", "SPDIF", "TCO", "Sync In"
};
-static char *texts_autosync_raydat[] = {
+static const char *const texts_autosync_raydat[] = {
"Word Clock",
"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
"AES", "SPDIF", "Sync In"
};
-static char *texts_autosync_aio_tco[] = {
+static const char *const texts_autosync_aio_tco[] = {
"Word Clock",
"ADAT", "AES", "SPDIF", "TCO", "Sync In"
};
-static char *texts_autosync_aio[] = { "Word Clock",
+static const char *const texts_autosync_aio[] = { "Word Clock",
"ADAT", "AES", "SPDIF", "Sync In" };
-static char *texts_freq[] = {
+static const char *const texts_freq[] = {
"No Lock",
"32 kHz",
"44.1 kHz",
@@ -629,7 +744,8 @@ static char *texts_ports_aio_in_ss[] = {
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
- "ADAT.7", "ADAT.8"
+ "ADAT.7", "ADAT.8",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
static char *texts_ports_aio_out_ss[] = {
@@ -638,14 +754,16 @@ static char *texts_ports_aio_out_ss[] = {
"SPDIF.L", "SPDIF.R",
"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
"ADAT.7", "ADAT.8",
- "Phone.L", "Phone.R"
+ "Phone.L", "Phone.R",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
static char *texts_ports_aio_in_ds[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
- "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
static char *texts_ports_aio_out_ds[] = {
@@ -653,14 +771,16 @@ static char *texts_ports_aio_out_ds[] = {
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
- "Phone.L", "Phone.R"
+ "Phone.L", "Phone.R",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
static char *texts_ports_aio_in_qs[] = {
"Analogue.L", "Analogue.R",
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
- "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
static char *texts_ports_aio_out_qs[] = {
@@ -668,7 +788,8 @@ static char *texts_ports_aio_out_qs[] = {
"AES.L", "AES.R",
"SPDIF.L", "SPDIF.R",
"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
- "Phone.L", "Phone.R"
+ "Phone.L", "Phone.R",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
};
static char *texts_ports_aes32[] = {
@@ -745,8 +866,8 @@ static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
8, 9, /* aes in, */
10, 11, /* spdif in */
12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */
- -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 3, 4, 5, /* AEB */
+ -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -760,7 +881,8 @@ static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
10, 11, /* spdif out */
12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */
6, 7, /* phone out */
- -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 3, 4, 5, /* AEB */
+ -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -773,7 +895,8 @@ static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
8, 9, /* aes in */
10, 11, /* spdif in */
12, 14, 16, 18, /* adat in */
- -1, -1, -1, -1, -1, -1,
+ 2, 3, 4, 5, /* AEB */
+ -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -788,7 +911,7 @@ static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
10, 11, /* spdif out */
12, 14, 16, 18, /* adat out */
6, 7, /* phone out */
- -1, -1, -1, -1,
+ 2, 3, 4, 5, /* AEB */
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -802,7 +925,8 @@ static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
8, 9, /* aes in */
10, 11, /* spdif in */
12, 16, /* adat in */
- -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 3, 4, 5, /* AEB */
+ -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -817,7 +941,8 @@ static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
10, 11, /* spdif out */
12, 16, /* adat out */
6, 7, /* phone out */
- -1, -1, -1, -1, -1, -1,
+ 2, 3, 4, 5, /* AEB */
+ -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -856,11 +981,11 @@ struct hdspm_midi {
};
struct hdspm_tco {
- int input;
- int framerate;
- int wordclock;
- int samplerate;
- int pull;
+ int input; /* 0: LTC, 1:Video, 2: WC*/
+ int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */
+ int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */
+ int samplerate; /* 0=44.1, 1=48, 2= freq from app */
+ int pull; /* 0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/
int term; /* 0 = off, 1 = on */
};
@@ -879,7 +1004,7 @@ struct hdspm {
u32 control_register; /* cached value */
u32 control2_register; /* cached value */
- u32 settings_register;
+ u32 settings_register; /* cached value for AIO / RayDat (sync reference, master/slave) */
struct hdspm_midi midi[4];
struct tasklet_struct midi_tasklet;
@@ -941,7 +1066,7 @@ struct hdspm {
struct hdspm_tco *tco; /* NULL if no TCO detected */
- char **texts_autosync;
+ const char *const *texts_autosync;
int texts_autosync_items;
cycles_t last_interrupt;
@@ -976,12 +1101,24 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
static inline int hdspm_get_pll_freq(struct hdspm *hdspm);
static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
static int hdspm_autosync_ref(struct hdspm *hdspm);
+static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out);
static int snd_hdspm_set_defaults(struct hdspm *hdspm);
static int hdspm_system_clock_mode(struct hdspm *hdspm);
static void hdspm_set_sgbuf(struct hdspm *hdspm,
struct snd_pcm_substream *substream,
unsigned int reg, int channels);
+static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx);
+static int hdspm_wc_sync_check(struct hdspm *hdspm);
+static int hdspm_tco_sync_check(struct hdspm *hdspm);
+static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
+
+static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index);
+static int hdspm_get_tco_sample_rate(struct hdspm *hdspm);
+static int hdspm_get_wc_sample_rate(struct hdspm *hdspm);
+
+
+
static inline int HDSPM_bit2freq(int n)
{
static const int bit2freq_tab[] = {
@@ -992,6 +1129,12 @@ static inline int HDSPM_bit2freq(int n)
return bit2freq_tab[n];
}
+static bool hdspm_is_raydat_or_aio(struct hdspm *hdspm)
+{
+ return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type));
+}
+
+
/* Write/read to/from HDSPM with Adresses in Bytes
not words but only 32Bit writes are allowed */
@@ -1107,14 +1250,11 @@ static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate)
else if (hdspm->control_register &
HDSPM_DoubleSpeed)
return rate * 2;
- };
+ }
return rate;
}
-static int hdspm_tco_sync_check(struct hdspm *hdspm);
-static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
-
-/* check for external sample rate */
+/* check for external sample rate, returns the sample rate in Hz*/
static int hdspm_external_sample_rate(struct hdspm *hdspm)
{
unsigned int status, status2, timecode;
@@ -1127,17 +1267,36 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
syncref = hdspm_autosync_ref(hdspm);
+ switch (syncref) {
+ case HDSPM_AES32_AUTOSYNC_FROM_WORD:
+ /* Check WC sync and get sample rate */
+ if (hdspm_wc_sync_check(hdspm))
+ return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm));
+ break;
- if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD &&
- status & HDSPM_AES32_wcLock)
- return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF);
+ case HDSPM_AES32_AUTOSYNC_FROM_AES1:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES2:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES3:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES4:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES5:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES6:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES7:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES8:
+ /* Check AES sync and get sample rate */
+ if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))
+ return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm,
+ syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1));
+ break;
- if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 &&
- syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
- status2 & (HDSPM_LockAES >>
- (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
- return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
- return 0;
+
+ case HDSPM_AES32_AUTOSYNC_FROM_TCO:
+ /* Check TCO sync and get sample rate */
+ if (hdspm_tco_sync_check(hdspm))
+ return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm));
+ break;
+ default:
+ return 0;
+ } /* end switch(syncref) */
break;
case MADIface:
@@ -2129,6 +2288,9 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
return (status >> 16) & 0xF;
break;
+ case AES32:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ return (status >> HDSPM_AES32_wcFreq_bit) & 0xF;
default:
break;
}
@@ -2152,6 +2314,9 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
return (status >> 20) & 0xF;
break;
+ case AES32:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ return (status >> 1) & 0xF;
default:
break;
}
@@ -2183,6 +2348,23 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
return 0;
}
+/**
+ * Returns the AES sample rate class for the given card.
+ **/
+static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index)
+{
+ int timecode;
+
+ switch (hdspm->io_type) {
+ case AES32:
+ timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
+ return (timecode >> (4*index)) & 0xF;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
/**
* Returns the sample rate class for input source <idx> for
@@ -2196,15 +2378,23 @@ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
}
#define ENUMERATED_CTL_INFO(info, texts) \
-{ \
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; \
- uinfo->count = 1; \
- uinfo->value.enumerated.items = ARRAY_SIZE(texts); \
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) \
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; \
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); \
-}
+ snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts)
+
+/* Helper function to query the external sample rate and return the
+ * corresponding enum to be returned to userspace.
+ */
+static int hdspm_external_rate_to_enum(struct hdspm *hdspm)
+{
+ int rate = hdspm_external_sample_rate(hdspm);
+ int i, selected_rate = 0;
+ for (i = 1; i < 10; i++)
+ if (HDSPM_bit2freq(i) == rate) {
+ selected_rate = i;
+ break;
+ }
+ return selected_rate;
+}
#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
@@ -2270,7 +2460,7 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
default:
ucontrol->value.enumerated.item[0] =
hdspm_get_s1_sample_rate(hdspm,
- ucontrol->id.index-1);
+ kcontrol->private_value-1);
}
break;
@@ -2289,28 +2479,24 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
ucontrol->value.enumerated.item[0] =
hdspm_get_sync_in_sample_rate(hdspm);
break;
+ case 11: /* External Rate */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_external_rate_to_enum(hdspm);
+ break;
default: /* AES1 to AES8 */
ucontrol->value.enumerated.item[0] =
- hdspm_get_s1_sample_rate(hdspm,
- kcontrol->private_value-1);
+ hdspm_get_aes_sample_rate(hdspm,
+ kcontrol->private_value -
+ HDSPM_AES32_AUTOSYNC_FROM_AES1);
break;
}
break;
case MADI:
case MADIface:
- {
- int rate = hdspm_external_sample_rate(hdspm);
- int i, selected_rate = 0;
- for (i = 1; i < 10; i++)
- if (HDSPM_bit2freq(i) == rate) {
- selected_rate = i;
- break;
- }
- ucontrol->value.enumerated.item[0] = selected_rate;
- }
+ ucontrol->value.enumerated.item[0] =
+ hdspm_external_rate_to_enum(hdspm);
break;
-
default:
break;
}
@@ -2359,33 +2545,17 @@ static int hdspm_system_clock_mode(struct hdspm *hdspm)
**/
static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
{
- switch (hdspm->io_type) {
- case AIO:
- case RayDAT:
- if (0 == mode)
- hdspm->settings_register |= HDSPM_c0Master;
- else
- hdspm->settings_register &= ~HDSPM_c0Master;
-
- hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
- break;
-
- default:
- if (0 == mode)
- hdspm->control_register |= HDSPM_ClockModeMaster;
- else
- hdspm->control_register &= ~HDSPM_ClockModeMaster;
-
- hdspm_write(hdspm, HDSPM_controlRegister,
- hdspm->control_register);
- }
+ hdspm_set_toggle_setting(hdspm,
+ (hdspm_is_raydat_or_aio(hdspm)) ?
+ HDSPM_c0Master : HDSPM_ClockModeMaster,
+ (0 == mode));
}
static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Master", "AutoSync" };
+ static const char *const texts[] = { "Master", "AutoSync" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -2809,16 +2979,7 @@ static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = hdspm->texts_autosync_items;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- hdspm->texts_autosync[uinfo->value.enumerated.item]);
+ snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync);
return 0;
}
@@ -2873,19 +3034,20 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
static int hdspm_autosync_ref(struct hdspm *hdspm)
{
+ /* This looks at the autosync selected sync reference */
if (AES32 == hdspm->io_type) {
+
unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
- unsigned int syncref =
- (status >> HDSPM_AES32_syncref_bit) & 0xF;
- if (syncref == 0)
- return HDSPM_AES32_AUTOSYNC_FROM_WORD;
- if (syncref <= 8)
+ unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
+ if ((syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD) &&
+ (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN)) {
return syncref;
+ }
return HDSPM_AES32_AUTOSYNC_FROM_NONE;
+
} else if (MADI == hdspm->io_type) {
- /* This looks at the autosync selected sync reference */
- unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
switch (status2 & HDSPM_SelSyncRefMask) {
case HDSPM_SelSyncRef_WORD:
return HDSPM_AUTOSYNC_FROM_WORD;
@@ -2898,7 +3060,7 @@ static int hdspm_autosync_ref(struct hdspm *hdspm)
case HDSPM_SelSyncRef_NVALID:
return HDSPM_AUTOSYNC_FROM_NONE;
default:
- return 0;
+ return HDSPM_AUTOSYNC_FROM_NONE;
}
}
@@ -2912,31 +3074,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
if (AES32 == hdspm->io_type) {
- static char *texts[] = { "WordClock", "AES1", "AES2", "AES3",
- "AES4", "AES5", "AES6", "AES7", "AES8", "None"};
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 10;
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3",
+ "AES4", "AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"};
+
+ ENUMERATED_CTL_INFO(uinfo, texts);
} else if (MADI == hdspm->io_type) {
- static char *texts[] = {"Word Clock", "MADI", "TCO",
+ static const char *const texts[] = {"Word Clock", "MADI", "TCO",
"Sync In", "None" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts);
}
return 0;
}
@@ -2964,7 +3110,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"No video", "NTSC", "PAL"};
+ static const char *const texts[] = {"No video", "NTSC", "PAL"};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3010,7 +3156,7 @@ static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
+ static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
"30 fps"};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
@@ -3027,19 +3173,19 @@ static int hdspm_tco_ltc_frames(struct hdspm *hdspm)
HDSPM_TCO1_LTC_Format_MSB)) {
case 0:
/* 24 fps */
- ret = 1;
+ ret = fps_24;
break;
case HDSPM_TCO1_LTC_Format_LSB:
/* 25 fps */
- ret = 2;
+ ret = fps_25;
break;
case HDSPM_TCO1_LTC_Format_MSB:
- /* 25 fps */
- ret = 3;
+ /* 29.97 fps */
+ ret = fps_2997;
break;
default:
/* 30 fps */
- ret = 4;
+ ret = fps_30;
break;
}
}
@@ -3067,16 +3213,35 @@ static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
static int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask)
{
- return (hdspm->control_register & regmask) ? 1 : 0;
+ u32 reg;
+
+ if (hdspm_is_raydat_or_aio(hdspm))
+ reg = hdspm->settings_register;
+ else
+ reg = hdspm->control_register;
+
+ return (reg & regmask) ? 1 : 0;
}
static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out)
{
+ u32 *reg;
+ u32 target_reg;
+
+ if (hdspm_is_raydat_or_aio(hdspm)) {
+ reg = &(hdspm->settings_register);
+ target_reg = HDSPM_WR_SETTINGS;
+ } else {
+ reg = &(hdspm->control_register);
+ target_reg = HDSPM_controlRegister;
+ }
+
if (out)
- hdspm->control_register |= regmask;
+ *reg |= regmask;
else
- hdspm->control_register &= ~regmask;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+ *reg &= ~regmask;
+
+ hdspm_write(hdspm, target_reg, *reg);
return 0;
}
@@ -3141,7 +3306,7 @@ static int hdspm_set_input_select(struct hdspm * hdspm, int out)
static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "optical", "coaxial" };
+ static const char *const texts[] = { "optical", "coaxial" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3203,7 +3368,7 @@ static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Single", "Double" };
+ static const char *const texts[] = { "Single", "Double" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3276,7 +3441,7 @@ static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Single", "Double", "Quad" };
+ static const char *const texts[] = { "Single", "Double", "Quad" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3313,6 +3478,84 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
return change;
}
+#define HDSPM_CONTROL_TRISTATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .info = snd_hdspm_info_tristate, \
+ .get = snd_hdspm_get_tristate, \
+ .put = snd_hdspm_put_tristate \
+}
+
+static int hdspm_tristate(struct hdspm *hdspm, u32 regmask)
+{
+ u32 reg = hdspm->settings_register & (regmask * 3);
+ return reg / regmask;
+}
+
+static int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask)
+{
+ hdspm->settings_register &= ~(regmask * 3);
+ hdspm->settings_register |= (regmask * mode);
+ hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
+
+ return 0;
+}
+
+static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ u32 regmask = kcontrol->private_value;
+
+ static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" };
+ static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" };
+
+ switch (regmask) {
+ case HDSPM_c0_Input0:
+ ENUMERATED_CTL_INFO(uinfo, texts_spdif);
+ break;
+ default:
+ ENUMERATED_CTL_INFO(uinfo, texts_levels);
+ break;
+ }
+ return 0;
+}
+
+static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ u32 regmask = kcontrol->private_value;
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ u32 regmask = kcontrol->private_value;
+ int change;
+ int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ val = 0;
+ if (val > 2)
+ val = 2;
+
+ spin_lock_irq(&hdspm->lock);
+ change = val != hdspm_tristate(hdspm, regmask);
+ hdspm_set_tristate(hdspm, val, regmask);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
#define HDSPM_MADI_SPEEDMODE(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
@@ -3352,7 +3595,7 @@ static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Single", "Double", "Quad" };
+ static const char *const texts[] = { "Single", "Double", "Quad" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3587,7 +3830,7 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" };
+ static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3595,7 +3838,7 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "No Lock", "Lock" };
+ static const char *const texts[] = { "No Lock", "Lock" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3745,9 +3988,18 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm)
if (hdspm->tco) {
switch (hdspm->io_type) {
case MADI:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ if (status & HDSPM_tcoLockMadi) {
+ if (status & HDSPM_tcoSync)
+ return 2;
+ else
+ return 1;
+ }
+ return 0;
+ break;
case AES32:
status = hdspm_read(hdspm, HDSPM_statusRegister);
- if (status & HDSPM_tcoLock) {
+ if (status & HDSPM_tcoLockAes) {
if (status & HDSPM_tcoSync)
return 2;
else
@@ -3807,7 +4059,8 @@ static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
case 5: /* SYNC IN */
val = hdspm_sync_in_sync_check(hdspm); break;
default:
- val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1);
+ val = hdspm_s1_sync_check(hdspm,
+ kcontrol->private_value-1);
}
break;
@@ -3975,7 +4228,8 @@ static void hdspm_tco_write(struct hdspm *hdspm)
static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "44.1 kHz", "48 kHz" };
+ /* TODO freq from app could be supported here, see tco->samplerate */
+ static const char *const texts[] = { "44.1 kHz", "48 kHz" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -4021,7 +4275,8 @@ static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" };
+ static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %",
+ "+ 4 %", "- 4 %" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -4066,7 +4321,7 @@ static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
+ static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -4112,7 +4367,7 @@ static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "24 fps", "25 fps", "29.97fps",
+ static const char *const texts[] = { "24 fps", "25 fps", "29.97fps",
"29.97 dfps", "30 fps", "30 dfps" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
@@ -4159,7 +4414,7 @@ static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "LTC", "Video", "WCK" };
+ static const char *const texts[] = { "LTC", "Video", "WCK" };
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -4284,7 +4539,6 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
- HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
HDSPM_SYNC_CHECK("WC SyncCheck", 0),
@@ -4298,7 +4552,16 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
- HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5)
+ HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5),
+ HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0),
+ HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt),
+ HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
+ HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1),
+ HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db),
+ HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48),
+ HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0),
+ HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0),
+ HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0)
/*
HDSPM_INPUT_SELECT("Input Select", 0),
@@ -4335,7 +4598,9 @@ static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
- HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8)
+ HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8),
+ HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
+ HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48)
};
static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
@@ -4345,7 +4610,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
- HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11),
HDSPM_SYNC_CHECK("WC Sync Check", 0),
HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
@@ -4501,77 +4766,22 @@ static int snd_hdspm_create_controls(struct snd_card *card,
------------------------------------------------------------*/
static void
-snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
- struct snd_info_buffer *buffer)
+snd_hdspm_proc_read_tco(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
{
struct hdspm *hdspm = entry->private_data;
- unsigned int status, status2, control, freq;
-
- char *pref_sync_ref;
- char *autosync_ref;
- char *system_clock_mode;
- char *insel;
- int x, x2;
-
- /* TCO stuff */
+ unsigned int status, control;
int a, ltc, frames, seconds, minutes, hours;
unsigned int period;
u64 freq_const = 0;
u32 rate;
+ snd_iprintf(buffer, "--- TCO ---\n");
+
status = hdspm_read(hdspm, HDSPM_statusRegister);
- status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
control = hdspm->control_register;
- freq = hdspm_read(hdspm, HDSPM_timecodeRegister);
- snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
- hdspm->card_name, hdspm->card->number + 1,
- hdspm->firmware_rev,
- (status2 & HDSPM_version0) |
- (status2 & HDSPM_version1) | (status2 &
- HDSPM_version2));
- snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
- (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
- hdspm->serial);
-
- snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
- hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
-
- snd_iprintf(buffer, "--- System ---\n");
-
- snd_iprintf(buffer,
- "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
- status & HDSPM_audioIRQPending,
- (status & HDSPM_midi0IRQPending) ? 1 : 0,
- (status & HDSPM_midi1IRQPending) ? 1 : 0,
- hdspm->irq_count);
- snd_iprintf(buffer,
- "HW pointer: id = %d, rawptr = %d (%d->%d) "
- "estimated= %ld (bytes)\n",
- ((status & HDSPM_BufferID) ? 1 : 0),
- (status & HDSPM_BufferPositionMask),
- (status & HDSPM_BufferPositionMask) %
- (2 * (int)hdspm->period_bytes),
- ((status & HDSPM_BufferPositionMask) - 64) %
- (2 * (int)hdspm->period_bytes),
- (long) hdspm_hw_pointer(hdspm) * 4);
-
- snd_iprintf(buffer,
- "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
- hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
- hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
- hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
- hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
- snd_iprintf(buffer,
- "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
- hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
- hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
- snd_iprintf(buffer,
- "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
- "status2=0x%x\n",
- hdspm->control_register, hdspm->control2_register,
- status, status2);
if (status & HDSPM_tco_detect) {
snd_iprintf(buffer, "TCO module detected.\n");
a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
@@ -4665,6 +4875,75 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
} else {
snd_iprintf(buffer, "No TCO module detected.\n");
}
+}
+
+static void
+snd_hdspm_proc_read_madi(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ unsigned int status, status2, control, freq;
+
+ char *pref_sync_ref;
+ char *autosync_ref;
+ char *system_clock_mode;
+ char *insel;
+ int x, x2;
+
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ control = hdspm->control_register;
+ freq = hdspm_read(hdspm, HDSPM_timecodeRegister);
+
+ snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
+ hdspm->card_name, hdspm->card->number + 1,
+ hdspm->firmware_rev,
+ (status2 & HDSPM_version0) |
+ (status2 & HDSPM_version1) | (status2 &
+ HDSPM_version2));
+
+ snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
+ (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
+ hdspm->serial);
+
+ snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+ hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
+
+ snd_iprintf(buffer, "--- System ---\n");
+
+ snd_iprintf(buffer,
+ "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
+ status & HDSPM_audioIRQPending,
+ (status & HDSPM_midi0IRQPending) ? 1 : 0,
+ (status & HDSPM_midi1IRQPending) ? 1 : 0,
+ hdspm->irq_count);
+ snd_iprintf(buffer,
+ "HW pointer: id = %d, rawptr = %d (%d->%d) "
+ "estimated= %ld (bytes)\n",
+ ((status & HDSPM_BufferID) ? 1 : 0),
+ (status & HDSPM_BufferPositionMask),
+ (status & HDSPM_BufferPositionMask) %
+ (2 * (int)hdspm->period_bytes),
+ ((status & HDSPM_BufferPositionMask) - 64) %
+ (2 * (int)hdspm->period_bytes),
+ (long) hdspm_hw_pointer(hdspm) * 4);
+
+ snd_iprintf(buffer,
+ "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
+ hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
+ snd_iprintf(buffer,
+ "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
+ hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
+ snd_iprintf(buffer,
+ "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
+ "status2=0x%x\n",
+ hdspm->control_register, hdspm->control2_register,
+ status, status2);
+
snd_iprintf(buffer, "--- Settings ---\n");
@@ -4768,6 +5047,9 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
(status & HDSPM_RX_64ch) ? "64 channels" :
"56 channels");
+ /* call readout function for TCO specific status */
+ snd_hdspm_proc_read_tco(entry, buffer);
+
snd_iprintf(buffer, "\n");
}
@@ -4909,11 +5191,18 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
autosync_ref = "AES7"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES8:
autosync_ref = "AES8"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_TCO:
+ autosync_ref = "TCO"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN:
+ autosync_ref = "Sync In"; break;
default:
autosync_ref = "---"; break;
}
snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
+ /* call readout function for TCO specific status */
+ snd_hdspm_proc_read_tco(entry, buffer);
+
snd_iprintf(buffer, "\n");
}
@@ -5097,7 +5386,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
case AES32:
hdspm->control_register =
- HDSPM_ClockModeMaster | /* Master Cloack Mode on */
+ HDSPM_ClockModeMaster | /* Master Clock Mode on */
hdspm_encode_latency(7) | /* latency max=8192samples */
HDSPM_SyncRef0 | /* AES1 is syncclock */
HDSPM_LineOut | /* Analog output in */
@@ -5123,9 +5412,8 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
- if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) {
+ if (hdspm_is_raydat_or_aio(hdspm))
hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
- }
/* set a default rate so that the channel map is set up. */
hdspm_set_rate(hdspm, 48000, 1);
@@ -5371,6 +5659,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
*/
+ /* For AES cards, the float format bit is the same as the
+ * preferred sync reference. Since we don't want to break
+ * sync settings, we have to skip the remaining part of this
+ * function.
+ */
+ if (hdspm->io_type == AES32) {
+ return 0;
+ }
+
+
/* Switch to native float format if requested */
if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
@@ -6013,7 +6311,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
ltc.format = fps_2997;
break;
default:
- ltc.format = 30;
+ ltc.format = fps_30;
break;
}
if (i & HDSPM_TCO1_set_drop_frame_flag) {
@@ -6479,10 +6777,6 @@ static int snd_hdspm_create(struct snd_card *card,
break;
case AIO:
- if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
- snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n");
- }
-
hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
@@ -6490,6 +6784,20 @@ static int snd_hdspm_create(struct snd_card *card,
hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
+ if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
+ snd_printk(KERN_INFO "HDSPM: AEB input board found\n");
+ hdspm->ss_in_channels += 4;
+ hdspm->ds_in_channels += 4;
+ hdspm->qs_in_channels += 4;
+ }
+
+ if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) {
+ snd_printk(KERN_INFO "HDSPM: AEB output board found\n");
+ hdspm->ss_out_channels += 4;
+ hdspm->ds_out_channels += 4;
+ hdspm->qs_out_channels += 4;
+ }
+
hdspm->channel_map_out_ss = channel_map_aio_out_ss;
hdspm->channel_map_out_ds = channel_map_aio_out_ds;
hdspm->channel_map_out_qs = channel_map_aio_out_qs;
@@ -6558,6 +6866,7 @@ static int snd_hdspm_create(struct snd_card *card,
break;
case MADI:
+ case AES32:
if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
hdspm->midiPorts++;
hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
@@ -6565,7 +6874,7 @@ static int snd_hdspm_create(struct snd_card *card,
if (NULL != hdspm->tco) {
hdspm_tco_write(hdspm);
}
- snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n");
+ snd_printk(KERN_INFO "HDSPM: MADI/AES TCO module found\n");
} else {
hdspm->tco = NULL;
}
@@ -6580,10 +6889,12 @@ static int snd_hdspm_create(struct snd_card *card,
case AES32:
if (hdspm->tco) {
hdspm->texts_autosync = texts_autosync_aes_tco;
- hdspm->texts_autosync_items = 10;
+ hdspm->texts_autosync_items =
+ ARRAY_SIZE(texts_autosync_aes_tco);
} else {
hdspm->texts_autosync = texts_autosync_aes;
- hdspm->texts_autosync_items = 9;
+ hdspm->texts_autosync_items =
+ ARRAY_SIZE(texts_autosync_aes);
}
break;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 45eeaa9f7fec..5138b8493051 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -26,12 +26,9 @@ if SND_SOC
config SND_SOC_AC97_BUS
bool
-config SND_SOC_DMAENGINE_PCM
- bool
-
config SND_SOC_GENERIC_DMAENGINE_PCM
bool
- select SND_SOC_DMAENGINE_PCM
+ select SND_DMAENGINE_PCM
# All the supported SoCs
source "sound/soc/atmel/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index bc0261476d7a..61a64d281905 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,10 +1,6 @@
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-compress.o soc-io.o
-ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),)
-snd-soc-core-objs += soc-dmaengine-pcm.o
-endif
-
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
endif
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 3fdd87fa18a9..e48d38a1b95c 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -13,6 +13,7 @@ config SND_ATMEL_SOC_PDC
config SND_ATMEL_SOC_DMA
tristate
depends on SND_ATMEL_SOC
+ select SND_SOC_GENERIC_DMAENGINE_PCM
config SND_ATMEL_SOC_SSC
tristate
@@ -32,6 +33,26 @@ config SND_AT91_SOC_SAM9G20_WM8731
Say Y if you want to add support for SoC audio on WM8731-based
AT91sam9g20 evaluation board.
+config SND_ATMEL_SOC_WM8904
+ tristate "Atmel ASoC driver for boards using WM8904 codec"
+ depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC
+ select SND_ATMEL_SOC_SSC
+ select SND_ATMEL_SOC_DMA
+ select SND_SOC_WM8904
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ WM8904 codec.
+
+config SND_AT91_SOC_SAM9X5_WM8731
+ tristate "SoC Audio support for WM8731-based at91sam9x5 board"
+ depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5
+ select SND_ATMEL_SOC_SSC
+ select SND_ATMEL_SOC_DMA
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for audio SoC on an
+ at91sam9x5 based board that is using WM8731 codec.
+
config SND_AT91_SOC_AFEB9260
tristate "SoC Audio support for AFEB9260 board"
depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 41967ccb6f41..5baabc8bde3a 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -11,6 +11,10 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
# AT91 Machine Support
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
+snd-atmel-soc-wm8904-objs := atmel_wm8904.o
+snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
+obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
+obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
index d12826526798..06082e5e5dcb 100644
--- a/sound/soc/atmel/atmel-pcm-dma.c
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -91,138 +91,52 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
}
}
-/*--------------------------------------------------------------------------*\
- * DMAENGINE operations
-\*--------------------------------------------------------------------------*/
-static bool filter(struct dma_chan *chan, void *slave)
-{
- struct at_dma_slave *sl = slave;
-
- if (sl->dma_dev == chan->device->dev) {
- chan->private = sl;
- return true;
- } else {
- return false;
- }
-}
-
static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct atmel_pcm_dma_params *prtd)
+ struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_pcm_dma_params *prtd;
struct ssc_device *ssc;
- struct dma_chan *dma_chan;
- struct dma_slave_config slave_config;
int ret;
+ prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
ssc = prtd->ssc;
- ret = snd_hwparams_to_dma_slave_config(substream, params,
- &slave_config);
+ ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
if (ret) {
pr_err("atmel-pcm: hwparams to dma slave configure failed\n");
return ret;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR;
- slave_config.dst_maxburst = 1;
+ slave_config->dst_addr = ssc->phybase + SSC_THR;
+ slave_config->dst_maxburst = 1;
} else {
- slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR;
- slave_config.src_maxburst = 1;
- }
-
- dma_chan = snd_dmaengine_pcm_get_chan(substream);
- if (dmaengine_slave_config(dma_chan, &slave_config)) {
- pr_err("atmel-pcm: failed to configure dma channel\n");
- ret = -EBUSY;
- return ret;
- }
-
- return 0;
-}
-
-static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct atmel_pcm_dma_params *prtd;
- struct ssc_device *ssc;
- struct at_dma_slave *sdata = NULL;
- int ret;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
- prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- ssc = prtd->ssc;
- if (ssc->pdev)
- sdata = ssc->pdev->dev.platform_data;
-
- ret = snd_dmaengine_pcm_open_request_chan(substream, filter, sdata);
- if (ret) {
- pr_err("atmel-pcm: dmaengine pcm open failed\n");
- return -EINVAL;
- }
-
- ret = atmel_pcm_configure_dma(substream, params, prtd);
- if (ret) {
- pr_err("atmel-pcm: failed to configure dmai\n");
- goto err;
+ slave_config->src_addr = ssc->phybase + SSC_RHR;
+ slave_config->src_maxburst = 1;
}
prtd->dma_intr_handler = atmel_pcm_dma_irq;
return 0;
-err:
- snd_dmaengine_pcm_close_release_chan(substream);
- return ret;
}
-static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct atmel_pcm_dma_params *prtd;
-
- prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error);
- ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable);
-
- return 0;
-}
-
-static int atmel_pcm_open(struct snd_pcm_substream *substream)
-{
- snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware);
-
- return 0;
-}
-
-static struct snd_pcm_ops atmel_pcm_ops = {
- .open = atmel_pcm_open,
- .close = snd_dmaengine_pcm_close_release_chan,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = atmel_pcm_hw_params,
- .prepare = atmel_pcm_dma_prepare,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = snd_dmaengine_pcm_pointer_no_residue,
- .mmap = atmel_pcm_mmap,
-};
-
-static struct snd_soc_platform_driver atmel_soc_platform = {
- .ops = &atmel_pcm_ops,
- .pcm_new = atmel_pcm_new,
- .pcm_free = atmel_pcm_free,
+static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_pcm_configure_dma,
+ .pcm_hardware = &atmel_pcm_dma_hardware,
+ .prealloc_buffer_size = ATMEL_SSC_DMABUF_SIZE,
};
int atmel_pcm_dma_platform_register(struct device *dev)
{
- return snd_soc_register_platform(dev, &atmel_soc_platform);
+ return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
}
EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
void atmel_pcm_dma_platform_unregister(struct device *dev)
{
- snd_soc_unregister_platform(dev);
+ snd_dmaengine_pcm_unregister(dev);
}
EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index f3fdfa07fcb9..0ecf356027f6 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -73,6 +73,7 @@ static struct atmel_ssc_mask ssc_tx_mask = {
.ssc_disable = SSC_BIT(CR_TXDIS),
.ssc_endx = SSC_BIT(SR_ENDTX),
.ssc_endbuf = SSC_BIT(SR_TXBUFE),
+ .ssc_error = SSC_BIT(SR_OVRUN),
.pdc_enable = ATMEL_PDC_TXTEN,
.pdc_disable = ATMEL_PDC_TXTDIS,
};
@@ -82,6 +83,7 @@ static struct atmel_ssc_mask ssc_rx_mask = {
.ssc_disable = SSC_BIT(CR_RXDIS),
.ssc_endx = SSC_BIT(SR_ENDRX),
.ssc_endbuf = SSC_BIT(SR_RXBUFF),
+ .ssc_error = SSC_BIT(SR_OVRUN),
.pdc_enable = ATMEL_PDC_RXTEN,
.pdc_disable = ATMEL_PDC_RXTDIS,
};
@@ -196,15 +198,27 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
- int dir_mask;
+ struct atmel_pcm_dma_params *dma_params;
+ int dir, dir_mask;
pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
ssc_readl(ssc_p->ssc->regs, SR));
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dir = 0;
dir_mask = SSC_DIR_MASK_PLAYBACK;
- else
+ } else {
+ dir = 1;
dir_mask = SSC_DIR_MASK_CAPTURE;
+ }
+
+ dma_params = &ssc_dma_params[dai->id][dir];
+ dma_params->ssc = ssc_p->ssc;
+ dma_params->substream = substream;
+
+ ssc_p->dma_params[dir] = dma_params;
+
+ snd_soc_dai_set_dma_data(dai, substream, dma_params);
spin_lock_irq(&ssc_p->lock);
if (ssc_p->dir_mask & dir_mask) {
@@ -325,7 +339,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
int id = dai->id;
struct atmel_ssc_info *ssc_p = &ssc_info[id];
struct atmel_pcm_dma_params *dma_params;
@@ -344,19 +357,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
else
dir = 1;
- dma_params = &ssc_dma_params[id][dir];
- dma_params->ssc = ssc_p->ssc;
- dma_params->substream = substream;
-
- ssc_p->dma_params[dir] = dma_params;
-
- /*
- * The snd_soc_pcm_stream->dma_data field is only used to communicate
- * the appropriate DMA parameters to the pcm driver hw_params()
- * function. It should not be used for other purposes
- * as it is common to all substreams.
- */
- snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params);
+ dma_params = ssc_p->dma_params[dir];
channels = params_channels(params);
@@ -648,6 +649,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
dma_params = ssc_p->dma_params[dir];
ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+ ssc_writel(ssc_p->ssc->regs, IER, dma_params->mask->ssc_error);
pr_debug("%s enabled SSC_SR=0x%08x\n",
dir ? "receive" : "transmit",
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
new file mode 100644
index 000000000000..7222380131ea
--- /dev/null
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -0,0 +1,254 @@
+/*
+ * atmel_wm8904 - Atmel ASoC driver for boards with WM8904 codec.
+ *
+ * Copyright (C) 2012 Atmel
+ *
+ * Author: Bo Shen <voice.shen@atmel.com>
+ *
+ * GPLv2 or later
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <sound/soc.h>
+
+#include "../codecs/wm8904.h"
+#include "atmel_ssc_dai.h"
+
+#define MCLK_RATE 32768
+
+static struct clk *mclk;
+
+static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+static int atmel_asoc_wm8904_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;
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
+ 32768, params_rate(params) * 256);
+ if (ret < 0) {
+ pr_err("%s - failed to set wm8904 codec PLL.", __func__);
+ return ret;
+ }
+
+ /*
+ * As here wm8904 use FLL output as its system clock
+ * so calling set_sysclk won't care freq parameter
+ * then we pass 0
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err("%s -failed to set wm8904 SYSCLK\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops atmel_asoc_wm8904_ops = {
+ .hw_params = atmel_asoc_wm8904_hw_params,
+};
+
+static int atmel_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ clk_prepare_enable(mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(mclk);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+};
+
+static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
+ .name = "WM8904",
+ .stream_name = "WM8904 PCM",
+ .codec_dai_name = "wm8904-hifi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ops = &atmel_asoc_wm8904_ops,
+};
+
+static struct snd_soc_card atmel_asoc_wm8904_card = {
+ .name = "atmel_asoc_wm8904",
+ .owner = THIS_MODULE,
+ .set_bias_level = atmel_set_bias_level,
+ .dai_link = &atmel_asoc_wm8904_dailink,
+ .num_links = 1,
+ .dapm_widgets = atmel_asoc_wm8904_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(atmel_asoc_wm8904_dapm_widgets),
+ .fully_routed = true,
+};
+
+static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_card *card = &atmel_asoc_wm8904_card;
+ struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "only device tree supported\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_of_parse_card_name(card, "atmel,model");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse card name\n");
+ return ret;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio routing\n");
+ return ret;
+ }
+
+ cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "failed to get dai and pcm info\n");
+ ret = -EINVAL;
+ return ret;
+ }
+ dailink->cpu_of_node = cpu_np;
+ dailink->platform_of_node = cpu_np;
+ of_node_put(cpu_np);
+
+ codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "failed to get codec info\n");
+ ret = -EINVAL;
+ return ret;
+ }
+ dailink->codec_of_node = codec_np;
+ of_node_put(codec_np);
+
+ return 0;
+}
+
+static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &atmel_asoc_wm8904_card;
+ struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+ struct clk *clk_src;
+ struct pinctrl *pinctrl;
+ int id, ret;
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ dev_err(&pdev->dev, "failed to request pinctrl\n");
+ return PTR_ERR(pinctrl);
+ }
+
+ card->dev = &pdev->dev;
+ ret = atmel_asoc_wm8904_dt_init(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init dt info\n");
+ return ret;
+ }
+
+ id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
+ ret = atmel_ssc_set_audio(id);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to set SSC %d for audio\n", id);
+ return ret;
+ }
+
+ mclk = clk_get(NULL, "pck0");
+ if (IS_ERR(mclk)) {
+ dev_err(&pdev->dev, "failed to get pck0\n");
+ ret = PTR_ERR(mclk);
+ goto err_set_audio;
+ }
+
+ clk_src = clk_get(NULL, "clk32k");
+ if (IS_ERR(clk_src)) {
+ dev_err(&pdev->dev, "failed to get clk32k\n");
+ ret = PTR_ERR(clk_src);
+ goto err_set_audio;
+ }
+
+ ret = clk_set_parent(mclk, clk_src);
+ clk_put(clk_src);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to set MCLK parent\n");
+ goto err_set_audio;
+ }
+
+ dev_info(&pdev->dev, "setting pck0 to %dHz\n", MCLK_RATE);
+ clk_set_rate(mclk, MCLK_RATE);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed\n");
+ goto err_set_audio;
+ }
+
+ return 0;
+
+err_set_audio:
+ atmel_ssc_put_audio(id);
+ return ret;
+}
+
+static int atmel_asoc_wm8904_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+ int id;
+
+ id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
+
+ snd_soc_unregister_card(card);
+ atmel_ssc_put_audio(id);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
+ { .compatible = "atmel,asoc-wm8904", },
+ { }
+};
+#endif
+
+static struct platform_driver atmel_asoc_wm8904_driver = {
+ .driver = {
+ .name = "atmel-wm8904-audio",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids),
+ },
+ .probe = atmel_asoc_wm8904_probe,
+ .remove = atmel_asoc_wm8904_remove,
+};
+
+module_platform_driver(atmel_asoc_wm8904_driver);
+
+/* Module information */
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for Atmel EK with WM8904 codec");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
new file mode 100644
index 000000000000..992ae38d5a15
--- /dev/null
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -0,0 +1,208 @@
+/*
+ * sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards
+ * that are using WM8731 as codec.
+ *
+ * Copyright (C) 2011 Atmel,
+ * Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Copyright (C) 2013 Paratronic,
+ * Richard Genoud <richard.genoud@gmail.com>
+ *
+ * Based on sam9g20_wm8731.c by:
+ * Sedji Gaouaou <sedji.gaouaou@atmel.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/of.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel_ssc_dai.h"
+
+
+#define MCLK_RATE 12288000
+
+#define DRV_NAME "sam9x5-snd-wm8731"
+
+struct sam9x5_drvdata {
+ int ssc_id;
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91sam9x5ek based board.
+ */
+static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct device *dev = rtd->dev;
+ int ret;
+
+ dev_dbg(dev, "ASoC: %s called\n", __func__);
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+ MCLK_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Audio paths on at91sam9x5ek board:
+ *
+ * |A| ------------> | | ---R----> Headphone Jack
+ * |T| <----\ | WM | ---L--/
+ * |9| ---> CLK <--> | 8731 | <--R----- Line In Jack
+ * |1| <------------ | | <--L--/
+ */
+static const struct snd_soc_dapm_widget sam9x5_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_card *card;
+ struct snd_soc_dai_link *dai;
+ struct sam9x5_drvdata *priv;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No device node supplied\n");
+ return -EINVAL;
+ }
+
+ card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai || !card || !priv) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ card->dev = &pdev->dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai;
+ card->num_links = 1;
+ card->dapm_widgets = sam9x5_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sam9x5_dapm_widgets);
+ dai->name = "WM8731";
+ dai->stream_name = "WM8731 PCM";
+ dai->codec_dai_name = "wm8731-hifi";
+ dai->init = sam9x5_wm8731_init;
+ dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM;
+
+ ret = snd_soc_of_parse_card_name(card, "atmel,model");
+ if (ret) {
+ dev_err(&pdev->dev, "atmel,model node missing\n");
+ goto out;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "atmel,audio-routing node missing\n");
+ goto out;
+ }
+
+ codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "atmel,audio-codec node missing\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dai->codec_of_node = codec_np;
+
+ cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "atmel,ssc-controller node missing\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ dai->cpu_of_node = cpu_np;
+ dai->platform_of_node = cpu_np;
+
+ priv->ssc_id = of_alias_get_id(cpu_np, "ssc");
+
+ ret = atmel_ssc_set_audio(priv->ssc_id);
+ if (ret != 0) {
+ dev_err(&pdev->dev,
+ "ASoC: Failed to set SSC %d for audio: %d\n",
+ ret, priv->ssc_id);
+ goto out;
+ }
+
+ of_node_put(codec_np);
+ of_node_put(cpu_np);
+
+ platform_set_drvdata(pdev, card);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "ASoC: Platform device allocation failed\n");
+ goto out_put_audio;
+ }
+
+ dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__);
+
+ return ret;
+
+out_put_audio:
+ atmel_ssc_put_audio(priv->ssc_id);
+out:
+ return ret;
+}
+
+static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct sam9x5_drvdata *priv = card->drvdata;
+
+ snd_soc_unregister_card(card);
+ atmel_ssc_put_audio(priv->ssc_id);
+
+ return 0;
+}
+
+static const struct of_device_id sam9x5_wm8731_of_match[] = {
+ { .compatible = "atmel,sam9x5-wm8731-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match);
+
+static struct platform_driver sam9x5_wm8731_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
+ },
+ .probe = sam9x5_wm8731_driver_probe,
+ .remove = sam9x5_wm8731_driver_remove,
+};
+module_platform_driver(sam9x5_wm8731_driver);
+
+/* Module information */
+MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
+MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index a497a0cfeba1..decba87a074c 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -73,12 +73,14 @@ static struct snd_soc_dai_link db1300_ac97_dai = {
static struct snd_soc_card db1300_ac97_machine = {
.name = "DB1300_AC97",
+ .owner = THIS_MODULE,
.dai_link = &db1300_ac97_dai,
.num_links = 1,
};
static struct snd_soc_card db1550_ac97_machine = {
.name = "DB1550_AC97",
+ .owner = THIS_MODULE,
.dai_link = &db1200_ac97_dai,
.num_links = 1,
};
@@ -145,6 +147,7 @@ static struct snd_soc_dai_link db1300_i2s_dai = {
static struct snd_soc_card db1300_i2s_machine = {
.name = "DB1300_I2S",
+ .owner = THIS_MODULE,
.dai_link = &db1300_i2s_dai,
.num_links = 1,
};
@@ -161,6 +164,7 @@ static struct snd_soc_dai_link db1550_i2s_dai = {
static struct snd_soc_card db1550_i2s_machine = {
.name = "DB1550_I2S",
+ .owner = THIS_MODULE,
.dai_link = &db1550_i2s_dai,
.num_links = 1,
};
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index a822ab822bb7..986dcec79fa0 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -379,9 +379,6 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
mutex_init(&wd->lock);
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iores)
- return -ENODEV;
-
wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
if (IS_ERR(wd->mmio))
return PTR_ERR(wd->mmio);
diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h
index 0c3e22d90a8d..a680fdc9bb42 100644
--- a/sound/soc/blackfin/bf5xx-ac97.h
+++ b/sound/soc/blackfin/bf5xx-ac97.h
@@ -9,7 +9,6 @@
#ifndef _BF5XX_AC97_H
#define _BF5XX_AC97_H
-extern struct snd_ac97 *ac97;
/* Frame format in memory, only support stereo currently */
struct ac97_frame {
u16 ac97_tag; /* slot 0 */
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index 04491f0e8d1b..efa75b5086a4 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -363,9 +363,6 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
info->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 17ad70bca9fe..a57643d6402f 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -376,9 +376,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
info->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
@@ -411,7 +408,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
return 0;
fail_put_lrclk:
- dev_set_drvdata(&pdev->dev, NULL);
clk_put(info->lrclk);
fail_put_sclk:
clk_put(info->sclk);
@@ -426,7 +422,6 @@ static int ep93xx_i2s_remove(struct platform_device *pdev)
struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
- dev_set_drvdata(&pdev->dev, NULL);
clk_put(info->lrclk);
clk_put(info->sclk);
clk_put(info->mclk);
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index badb6fbacaa6..15106c045478 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -10,6 +10,7 @@ config SND_SOC_I2C_AND_SPI
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
+ depends on COMPILE_TEST
select SND_SOC_88PM860X if MFD_88PM860X
select SND_SOC_L3
select SND_SOC_AB8500_CODEC if ABX500_CORE
@@ -20,6 +21,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD73311
select SND_SOC_ADAU1373 if I2C
select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
+ select SND_SOC_ADAU1701 if I2C
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
@@ -54,6 +56,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
select SND_SOC_HDMI_CODEC
+ select SND_SOC_PCM1681 if I2C
+ select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
@@ -122,6 +126,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8996 if I2C
+ select SND_SOC_WM8997 if MFD_WM8997
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -145,8 +150,10 @@ config SND_SOC_ARIZONA
tristate
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
+ default y if SND_SOC_WM8997=y
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
+ default m if SND_SOC_WM8997=m
config SND_SOC_WM_HUBS
tristate
@@ -198,6 +205,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535
tristate
+config SND_SOC_AK4554
+ tristate
+
config SND_SOC_AK4641
tristate
@@ -292,6 +302,12 @@ config SND_SOC_MAX9850
config SND_SOC_HDMI_CODEC
tristate
+config SND_SOC_PCM1681
+ tristate
+
+config SND_SOC_PCM1792A
+ tristate
+
config SND_SOC_PCM3008
tristate
@@ -500,6 +516,9 @@ config SND_SOC_WM8995
config SND_SOC_WM8996
tristate
+config SND_SOC_WM8997
+ tristate
+
config SND_SOC_WM9081
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 70fd8066f546..bc126764a44d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -11,6 +11,7 @@ snd-soc-adav80x-objs := adav80x.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4554-objs := ak4554.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
@@ -42,6 +43,8 @@ snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
snd-soc-hdmi-codec-objs := hdmi.o
+snd-soc-pcm1681-objs := pcm1681.o
+snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
@@ -114,6 +117,7 @@ snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
+snd-soc-wm8997-objs := wm8997.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
@@ -138,6 +142,7 @@ 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
+obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
@@ -171,6 +176,8 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
@@ -239,6 +246,7 @@ obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
+obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index ec7351803c24..8d9ba4ba4bfe 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -23,6 +23,16 @@
#include <sound/initval.h>
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget ac97_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ac97_routes[] = {
+ { "AC97 Capture", NULL, "RX" },
+ { "TX", NULL, "AC97 Playback" },
+};
+
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -117,6 +127,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
.probe = ac97_soc_probe,
.suspend = ac97_soc_suspend,
.resume = ac97_soc_resume,
+
+ .dapm_widgets = ac97_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
+ .dapm_routes = ac97_routes,
+ .num_dapm_routes = ARRAY_SIZE(ac97_routes),
};
static int ac97_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 89fcf7d6e7b8..7257a8885f42 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -96,6 +96,44 @@ SOC_ENUM("Capture Source", ad1980_cap_src),
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
};
+static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_INPUT("CD_L"),
+SND_SOC_DAPM_INPUT("CD_R"),
+SND_SOC_DAPM_INPUT("AUX_L"),
+SND_SOC_DAPM_INPUT("AUX_R"),
+SND_SOC_DAPM_INPUT("LINE_IN_L"),
+SND_SOC_DAPM_INPUT("LINE_IN_R"),
+
+SND_SOC_DAPM_OUTPUT("LFE_OUT"),
+SND_SOC_DAPM_OUTPUT("CENTER_OUT"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_L"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_R"),
+SND_SOC_DAPM_OUTPUT("MONO_OUT"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_L"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_R"),
+};
+
+static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
+ { "Capture", NULL, "MIC1" },
+ { "Capture", NULL, "MIC2" },
+ { "Capture", NULL, "CD_L" },
+ { "Capture", NULL, "CD_R" },
+ { "Capture", NULL, "AUX_L" },
+ { "Capture", NULL, "AUX_R" },
+ { "Capture", NULL, "LINE_IN_L" },
+ { "Capture", NULL, "LINE_IN_R" },
+
+ { "LFE_OUT", NULL, "Playback" },
+ { "CENTER_OUT", NULL, "Playback" },
+ { "LINE_OUT_L", NULL, "Playback" },
+ { "LINE_OUT_R", NULL, "Playback" },
+ { "MONO_OUT", NULL, "Playback" },
+ { "HP_OUT_L", NULL, "Playback" },
+ { "HP_OUT_R", NULL, "Playback" },
+};
+
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -253,6 +291,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
.reg_cache_step = 2,
.write = ac97_write,
.read = ac97_read,
+
+ .dapm_widgets = ad1980_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
+ .dapm_routes = ad1980_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
};
static int ad1980_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index b1f2baf42b48..5fac8adbc136 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -23,6 +23,21 @@
#include "ad73311.h"
+static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINP"),
+SND_SOC_DAPM_INPUT("VINN"),
+SND_SOC_DAPM_OUTPUT("VOUTN"),
+SND_SOC_DAPM_OUTPUT("VOUTP"),
+};
+
+static const struct snd_soc_dapm_route ad73311_dapm_routes[] = {
+ { "Capture", NULL, "VINP" },
+ { "Capture", NULL, "VINN" },
+
+ { "VOUTN", NULL, "Playback" },
+ { "VOUTP", NULL, "Playback" },
+};
+
static struct snd_soc_dai_driver ad73311_dai = {
.name = "ad73311-hifi",
.playback = {
@@ -39,7 +54,12 @@ static struct snd_soc_dai_driver ad73311_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
-static struct snd_soc_codec_driver soc_codec_dev_ad73311;
+static struct snd_soc_codec_driver soc_codec_dev_ad73311 = {
+ .dapm_widgets = ad73311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
+ .dapm_routes = ad73311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
+};
static int ad73311_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index d1124a5b3471..ebff1128be59 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -91,7 +91,7 @@
#define ADAU1701_OSCIPOW_OPD 0x04
#define ADAU1701_DACSET_DACINIT 1
-#define ADAU1707_CLKDIV_UNSET (-1UL)
+#define ADAU1707_CLKDIV_UNSET (-1U)
#define ADAU1701_FIRMWARE "adau1701.bin"
@@ -247,21 +247,21 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
gpio_is_valid(adau1701->gpio_pll_mode[1])) {
switch (clkdiv) {
case 64:
- gpio_set_value(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
break;
case 256:
- gpio_set_value(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
break;
case 384:
- gpio_set_value(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
break;
case 0: /* fallback */
case 512:
- gpio_set_value(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
break;
}
}
@@ -269,10 +269,10 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
adau1701->pll_clkdiv = clkdiv;
if (gpio_is_valid(adau1701->gpio_nreset)) {
- gpio_set_value(adau1701->gpio_nreset, 0);
+ gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
udelay(1);
- gpio_set_value(adau1701->gpio_nreset, 1);
+ gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
/* power-up time may be as long as 85ms */
mdelay(85);
}
@@ -734,7 +734,10 @@ static int adau1701_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id adau1701_i2c_id[] = {
+ { "adau1401", 0 },
+ { "adau1401a", 0 },
{ "adau1701", 0 },
+ { "adau1702", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 3c839cc4e00e..15b012d0f226 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -868,6 +868,12 @@ static int adav80x_bus_remove(struct device *dev)
}
#if defined(CONFIG_SPI_MASTER)
+static const struct spi_device_id adav80x_spi_id[] = {
+ { "adav801", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
+
static int adav80x_spi_probe(struct spi_device *spi)
{
return adav80x_bus_probe(&spi->dev, SND_SOC_SPI);
@@ -885,15 +891,16 @@ static struct spi_driver adav80x_spi_driver = {
},
.probe = adav80x_spi_probe,
.remove = adav80x_spi_remove,
+ .id_table = adav80x_spi_id,
};
#endif
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static const struct i2c_device_id adav80x_id[] = {
+static const struct i2c_device_id adav80x_i2c_id[] = {
{ "adav803", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, adav80x_id);
+MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id);
static int adav80x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@@ -913,7 +920,7 @@ static struct i2c_driver adav80x_i2c_driver = {
},
.probe = adav80x_i2c_probe,
.remove = adav80x_i2c_remove,
- .id_table = adav80x_id,
+ .id_table = adav80x_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index 506d474c4d22..8f388edff586 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -23,6 +23,28 @@
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("Input1"),
+SND_SOC_DAPM_INPUT("Input2"),
+SND_SOC_DAPM_INPUT("Input3"),
+SND_SOC_DAPM_INPUT("Input4"),
+SND_SOC_DAPM_INPUT("Input5"),
+SND_SOC_DAPM_INPUT("Input6"),
+SND_SOC_DAPM_INPUT("Input7"),
+SND_SOC_DAPM_INPUT("Input8"),
+};
+
+static const struct snd_soc_dapm_route ads117x_dapm_routes[] = {
+ { "Capture", NULL, "Input1" },
+ { "Capture", NULL, "Input2" },
+ { "Capture", NULL, "Input3" },
+ { "Capture", NULL, "Input4" },
+ { "Capture", NULL, "Input5" },
+ { "Capture", NULL, "Input6" },
+ { "Capture", NULL, "Input7" },
+ { "Capture", NULL, "Input8" },
+};
+
static struct snd_soc_dai_driver ads117x_dai = {
/* ADC */
.name = "ads117x-hifi",
@@ -34,7 +56,12 @@ static struct snd_soc_dai_driver ads117x_dai = {
.formats = ADS117X_FORMATS,},
};
-static struct snd_soc_codec_driver soc_codec_dev_ads117x;
+static struct snd_soc_codec_driver soc_codec_dev_ads117x = {
+ .dapm_widgets = ads117x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
+ .dapm_routes = ads117x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
+};
static int ads117x_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index c7cfdf957e4d..71059c07ae7b 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -51,6 +51,17 @@ struct ak4104_private {
struct regmap *regmap;
};
+static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = {
+SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
+ { "TXE", NULL, "Playback" },
+ { "TX", NULL, "TXE" },
+};
+
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
@@ -138,29 +149,11 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- /* enable transmitter */
- ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
- AK4104_TX_TXE, AK4104_TX_TXE);
- if (ret < 0)
- return ret;
-
return 0;
}
-static int ak4104_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
-
- /* disable transmitter */
- return regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
- AK4104_TX_TXE, 0);
-}
-
static const struct snd_soc_dai_ops ak4101_dai_ops = {
.hw_params = ak4104_hw_params,
- .hw_free = ak4104_hw_free,
.set_fmt = ak4104_set_dai_fmt,
};
@@ -214,6 +207,11 @@ static int ak4104_remove(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
.probe = ak4104_probe,
.remove = ak4104_remove,
+
+ .dapm_widgets = ak4104_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
+ .dapm_routes = ak4104_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
};
static const struct regmap_config ak4104_regmap = {
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
new file mode 100644
index 000000000000..79e9555766c0
--- /dev/null
+++ b/sound/soc/codecs/ak4554.c
@@ -0,0 +1,106 @@
+/*
+ * ak4554.c
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 <sound/soc.h>
+
+/*
+ * ak4554 is very simple DA/AD converter which has no setting register.
+ *
+ * CAUTION
+ *
+ * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J,
+ * and, capture format is SND_SOC_DAIFMT_LEFT_J
+ * on same bit clock, LR clock.
+ * But, this driver doesn't have snd_soc_dai_ops :: set_fmt
+ *
+ * CPU/Codec DAI image
+ *
+ * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554
+ * |
+ * CPU-DAI2 (capture only fmt = LEFT_J) ---+
+ */
+
+static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route ak4554_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver ak4554_dai = {
+ .name = "ak4554-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .symmetric_rates = 1,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4554 = {
+ .dapm_widgets = ak4554_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
+ .dapm_routes = ak4554_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
+};
+
+static int ak4554_soc_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_ak4554,
+ &ak4554_dai, 1);
+}
+
+static int ak4554_soc_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct of_device_id ak4554_of_match[] = {
+ { .compatible = "asahi-kasei,ak4554" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4554_of_match);
+
+static struct platform_driver ak4554_driver = {
+ .driver = {
+ .name = "ak4554-adc-dac",
+ .owner = THIS_MODULE,
+ .of_match_table = ak4554_of_match,
+ },
+ .probe = ak4554_soc_probe,
+ .remove = ak4554_soc_remove,
+};
+module_platform_driver(ak4554_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SoC AK4554 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index 1f303983ae02..72e953b2cb41 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -22,7 +22,22 @@ struct ak5386_priv {
int reset_gpio;
};
-static struct snd_soc_codec_driver soc_codec_ak5386;
+static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+};
+
+static struct snd_soc_codec_driver soc_codec_ak5386 = {
+ .dapm_widgets = ak5386_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
+ .dapm_routes = ak5386_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
+};
static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index de625813c0e6..657808ba1418 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -19,6 +19,7 @@
#include <sound/tlv.h>
#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/gpio.h>
#include <linux/mfd/arizona/registers.h>
#include "arizona.h"
@@ -199,9 +200,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
if (ret != 0)
return ret;
- ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkr, 1);
- if (ret != 0)
- return ret;
+ switch (arizona->type) {
+ case WM8997:
+ break;
+ default:
+ ret = snd_soc_dapm_new_controls(&codec->dapm,
+ &arizona_spkr, 1);
+ if (ret != 0)
+ return ret;
+ break;
+ }
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN_WARN,
"Thermal warning", arizona_thermal_warn,
@@ -223,6 +231,41 @@ int arizona_init_spk(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_spk);
+int arizona_init_gpio(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int i;
+
+ switch (arizona->type) {
+ case WM5110:
+ snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity");
+
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+ switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
+ case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
+ snd_soc_dapm_enable_pin(&codec->dapm,
+ "DRC1 Signal Activity");
+ break;
+ case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
+ snd_soc_dapm_enable_pin(&codec->dapm,
+ "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_gpio);
+
const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
@@ -517,6 +560,26 @@ const struct soc_enum arizona_ng_hold =
4, arizona_ng_hold_text);
EXPORT_SYMBOL_GPL(arizona_ng_hold);
+static const char * const arizona_in_dmic_osr_text[] = {
+ "1.536MHz", "3.072MHz", "6.144MHz",
+};
+
+const struct soc_enum arizona_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+};
+EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
+
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index b60b08ccc1d0..9e81b6392692 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -150,7 +150,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
-#define ARIZONA_MUX_ROUTES(name) \
+#define ARIZONA_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input" }, \
ARIZONA_MIXER_INPUT_ROUTES(name " Input")
#define ARIZONA_MIXER_ROUTES(widget, name) \
@@ -198,6 +199,7 @@ extern const struct soc_enum arizona_lhpf3_mode;
extern const struct soc_enum arizona_lhpf4_mode;
extern const struct soc_enum arizona_ng_hold;
+extern const struct soc_enum arizona_in_dmic_osr[];
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
@@ -242,6 +244,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout);
extern int arizona_init_spk(struct snd_soc_codec *codec);
+extern int arizona_init_gpio(struct snd_soc_codec *codec);
extern int arizona_init_dai(struct arizona_priv *priv, int dai);
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index a081d9fcb166..c4cf0699e77f 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -15,15 +15,27 @@
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route bt_sco_routes[] = {
+ { "Capture", NULL, "RX" },
+ { "TX", NULL, "Playback" },
+};
+
static struct snd_soc_dai_driver bt_sco_dai = {
.name = "bt-sco-pcm",
.playback = {
+ .stream_name = "Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
+ .stream_name = "Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
@@ -31,7 +43,12 @@ static struct snd_soc_dai_driver bt_sco_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_bt_sco;
+static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
+ .dapm_widgets = bt_sco_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
+ .dapm_routes = bt_sco_routes,
+ .num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
+};
static int bt_sco_probe(struct platform_device *pdev)
{
@@ -50,6 +67,9 @@ static struct platform_device_id bt_sco_driver_ids[] = {
{
.name = "dfbmcs320",
},
+ {
+ .name = "bt-sco",
+ },
{},
};
MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 8e4779812b96..83c835d9fd88 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -139,6 +139,22 @@ struct cs4270_private {
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
+static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
+ { "Capture", NULL, "AINA" },
+ { "Capture", NULL, "AINB" },
+
+ { "AOUTA", NULL, "Playback" },
+ { "AOUTB", NULL, "Playback" },
+};
+
/**
* struct cs4270_mode_ratios - clock ratio tables
* @ratio: the ratio of MCLK to the sample rate
@@ -612,6 +628,10 @@ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
.controls = cs4270_snd_controls,
.num_controls = ARRAY_SIZE(cs4270_snd_controls),
+ .dapm_widgets = cs4270_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets),
+ .dapm_routes = cs4270_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes),
};
/*
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 03036b326732..a20f1bb8f071 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -173,6 +173,26 @@ struct cs4271_private {
bool enable_soft_reset;
};
+static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINA"),
+SND_SOC_DAPM_INPUT("AINB"),
+
+SND_SOC_DAPM_OUTPUT("AOUTA+"),
+SND_SOC_DAPM_OUTPUT("AOUTA-"),
+SND_SOC_DAPM_OUTPUT("AOUTB+"),
+SND_SOC_DAPM_OUTPUT("AOUTB-"),
+};
+
+static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
+ { "Capture", NULL, "AINA" },
+ { "Capture", NULL, "AINB" },
+
+ { "AOUTA+", NULL, "Playback" },
+ { "AOUTA-", NULL, "Playback" },
+ { "AOUTB+", NULL, "Playback" },
+ { "AOUTB-", NULL, "Playback" },
+};
+
/*
* @freq is the desired MCLK rate
* MCLK rate should (c) be the sample rate, multiplied by one of the
@@ -576,8 +596,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
CS4271_MODE2_MUTECAEQUB,
CS4271_MODE2_MUTECAEQUB);
- return snd_soc_add_codec_controls(codec, cs4271_snd_controls,
- ARRAY_SIZE(cs4271_snd_controls));
+ return 0;
}
static int cs4271_remove(struct snd_soc_codec *codec)
@@ -596,6 +615,13 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
.remove = cs4271_remove,
.suspend = cs4271_soc_suspend,
.resume = cs4271_soc_resume,
+
+ .controls = cs4271_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4271_snd_controls),
+ .dapm_widgets = cs4271_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets),
+ .dapm_routes = cs4271_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes),
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 66967ba6f757..b2090b2a5e2d 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -50,20 +50,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"},
};
-static int dmic_probe(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
- ARRAY_SIZE(dmic_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(dapm);
-
- return 0;
-}
-
static struct snd_soc_codec_driver soc_dmic = {
- .probe = dmic_probe,
+ .dapm_widgets = dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
};
static int dmic_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
index 2bcae2b40c92..68342b121c96 100644
--- a/sound/soc/codecs/hdmi.c
+++ b/sound/soc/codecs/hdmi.c
@@ -23,11 +23,20 @@
#define DRV_NAME "hdmi-audio-codec"
-static struct snd_soc_codec_driver hdmi_codec;
+static const struct snd_soc_dapm_widget hdmi_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route hdmi_routes[] = {
+ { "Capture", NULL, "RX" },
+ { "TX", NULL, "Playback" },
+};
static struct snd_soc_dai_driver hdmi_codec_dai = {
.name = "hdmi-hifi",
.playback = {
+ .stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_32000 |
@@ -37,6 +46,25 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .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,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+
+};
+
+static struct snd_soc_codec_driver hdmi_codec = {
+ .dapm_widgets = hdmi_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
+ .dapm_routes = hdmi_routes,
+ .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
};
static int hdmi_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index 9f9f59573f72..0e5743ea79df 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -23,12 +24,15 @@
#include <sound/tlv.h>
struct lm4857 {
- struct i2c_client *i2c;
+ struct regmap *regmap;
uint8_t mode;
};
-static const uint8_t lm4857_default_regs[] = {
- 0x00, 0x00, 0x00, 0x00,
+static const struct reg_default lm4857_default_regs[] = {
+ { 0x0, 0x00 },
+ { 0x1, 0x00 },
+ { 0x2, 0x00 },
+ { 0x3, 0x00 },
};
/* The register offsets in the cache array */
@@ -42,39 +46,6 @@ static const uint8_t lm4857_default_regs[] = {
#define LM4857_WAKEUP 5
#define LM4857_EPGAIN 4
-static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- uint8_t data;
- int ret;
-
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return ret;
-
- data = (reg << 6) | value;
- ret = i2c_master_send(codec->control_data, &data, 1);
- if (ret != 1) {
- dev_err(codec->dev, "Failed to write register: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static unsigned int lm4857_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- unsigned int val;
- int ret;
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret)
- return -1;
-
- return val;
-}
-
static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -96,7 +67,7 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
lm4857->mode = value;
if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6);
+ regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6);
return 1;
}
@@ -108,10 +79,11 @@ static int lm4857_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6);
+ regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F,
+ lm4857->mode + 6);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0);
+ regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0);
break;
default:
break;
@@ -171,49 +143,32 @@ static const struct snd_soc_dapm_route lm4857_routes[] = {
{"EP", NULL, "IN"},
};
-static int lm4857_probe(struct snd_soc_codec *codec)
-{
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- int ret;
-
- codec->control_data = lm4857->i2c;
-
- ret = snd_soc_add_codec_controls(codec, lm4857_controls,
- ARRAY_SIZE(lm4857_controls));
- if (ret)
- return ret;
-
- ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets,
- ARRAY_SIZE(lm4857_dapm_widgets));
- if (ret)
- return ret;
+static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
+ .set_bias_level = lm4857_set_bias_level,
- ret = snd_soc_dapm_add_routes(dapm, lm4857_routes,
- ARRAY_SIZE(lm4857_routes));
- if (ret)
- return ret;
+ .controls = lm4857_controls,
+ .num_controls = ARRAY_SIZE(lm4857_controls),
+ .dapm_widgets = lm4857_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets),
+ .dapm_routes = lm4857_routes,
+ .num_dapm_routes = ARRAY_SIZE(lm4857_routes),
+};
- snd_soc_dapm_new_widgets(dapm);
+static const struct regmap_config lm4857_regmap_config = {
+ .val_bits = 6,
+ .reg_bits = 2,
- return 0;
-}
+ .max_register = LM4857_CTRL,
-static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
- .write = lm4857_write,
- .read = lm4857_read,
- .probe = lm4857_probe,
- .reg_cache_size = ARRAY_SIZE(lm4857_default_regs),
- .reg_word_size = sizeof(uint8_t),
- .reg_cache_default = lm4857_default_regs,
- .set_bias_level = lm4857_set_bias_level,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = lm4857_default_regs,
+ .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
};
static int lm4857_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct lm4857 *lm4857;
- int ret;
lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL);
if (!lm4857)
@@ -221,11 +176,11 @@ static int lm4857_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, lm4857);
- lm4857->i2c = i2c;
-
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
+ lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
+ if (IS_ERR(lm4857->regmap))
+ return PTR_ERR(lm4857->regmap);
- return ret;
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
}
static int lm4857_i2c_remove(struct i2c_client *i2c)
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index a6ac2313047d..31f91560e9f6 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -118,6 +118,18 @@ static const struct snd_kcontrol_new max9768_mute[] = {
SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio),
};
+static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN"),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+};
+
+static const struct snd_soc_dapm_route max9768_dapm_routes[] = {
+ { "OUT+", NULL, "IN" },
+ { "OUT-", NULL, "IN" },
+};
+
static int max9768_probe(struct snd_soc_codec *codec)
{
struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
@@ -148,6 +160,10 @@ static struct snd_soc_codec_driver max9768_codec_driver = {
.probe = max9768_probe,
.controls = max9768_volume,
.num_controls = ARRAY_SIZE(max9768_volume),
+ .dapm_widgets = max9768_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets),
+ .dapm_routes = max9768_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes),
};
static const struct regmap_config max9768_i2c_regmap_config = {
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index ad5313f98f28..0569a4c3ae00 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2084,8 +2084,9 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
pm_wakeup_event(codec->dev, 100);
- schedule_delayed_work(&max98090->jack_work,
- msecs_to_jiffies(100));
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
}
if (active & M98090_DRCACT_MASK)
@@ -2132,8 +2133,9 @@ int max98090_mic_detect(struct snd_soc_codec *codec,
snd_soc_jack_report(max98090->jack, 0,
SND_JACK_HEADSET | SND_JACK_BTN_0);
- schedule_delayed_work(&max98090->jack_work,
- msecs_to_jiffies(100));
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
return 0;
}
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 6b6c74cd83e2..29549cdbf4c1 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -14,170 +14,21 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "max9877.h"
-static struct i2c_client *i2c;
+static struct regmap *regmap;
-static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 };
-
-static void max9877_write_regs(void)
-{
- unsigned int i;
- u8 data[6];
-
- data[0] = MAX9877_INPUT_MODE;
- for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
- data[i + 1] = max9877_regs[i];
-
- if (i2c_master_send(i2c, data, 6) != 6)
- dev_err(&i2c->dev, "i2c write failed\n");
-}
-
-static int max9877_get_reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int invert = mc->invert;
-
- ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
-
- if (invert)
- ucontrol->value.integer.value[0] =
- mask - ucontrol->value.integer.value[0];
-
- return 0;
-}
-
-static int max9877_set_reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int invert = mc->invert;
- unsigned int val = (ucontrol->value.integer.value[0] & mask);
-
- if (invert)
- val = mask - val;
-
- if (((max9877_regs[reg] >> shift) & mask) == val)
- return 0;
-
- max9877_regs[reg] &= ~(mask << shift);
- max9877_regs[reg] |= val << shift;
- max9877_write_regs();
-
- return 1;
-}
-
-static int max9877_get_2reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
-
- ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
- ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask;
-
- return 0;
-}
-
-static int max9877_set_2reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int val = (ucontrol->value.integer.value[0] & mask);
- unsigned int val2 = (ucontrol->value.integer.value[1] & mask);
- unsigned int change = 0;
-
- if (((max9877_regs[reg] >> shift) & mask) != val)
- change = 1;
-
- if (((max9877_regs[reg2] >> shift) & mask) != val2)
- change = 1;
-
- if (change) {
- max9877_regs[reg] &= ~(mask << shift);
- max9877_regs[reg] |= val << shift;
- max9877_regs[reg2] &= ~(mask << shift);
- max9877_regs[reg2] |= val2 << shift;
- max9877_write_regs();
- }
-
- return change;
-}
-
-static int max9877_get_out_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK;
-
- if (value)
- value -= 1;
-
- ucontrol->value.integer.value[0] = value;
- return 0;
-}
-
-static int max9877_set_out_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = ucontrol->value.integer.value[0];
-
- value += 1;
-
- if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value)
- return 0;
-
- max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK;
- max9877_regs[MAX9877_OUTPUT_MODE] |= value;
- max9877_write_regs();
- return 1;
-}
-
-static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK);
-
- value = value >> MAX9877_OSC_OFFSET;
-
- ucontrol->value.integer.value[0] = value;
- return 0;
-}
-
-static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = ucontrol->value.integer.value[0];
-
- value = value << MAX9877_OSC_OFFSET;
- if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value)
- return 0;
-
- max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK;
- max9877_regs[MAX9877_OUTPUT_MODE] |= value;
- max9877_write_regs();
- return 1;
-}
+static struct reg_default max9877_regs[] = {
+ { 0, 0x40 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x49 },
+};
static const unsigned int max9877_pgain_tlv[] = {
TLV_DB_RANGE_HEAD(2),
@@ -212,65 +63,104 @@ static const char *max9877_osc_mode[] = {
};
static const struct soc_enum max9877_enum[] = {
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode),
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode),
+ max9877_out_mode),
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET,
+ ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
};
static const struct snd_kcontrol_new max9877_controls[] = {
- SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume",
- MAX9877_INPUT_MODE, 0, 2, 0,
- max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
- SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume",
- MAX9877_INPUT_MODE, 2, 2, 0,
- max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
- SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume",
- MAX9877_SPK_VOLUME, 0, 31, 0,
- max9877_get_reg, max9877_set_reg, max9877_output_tlv),
- SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume",
- MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
- max9877_get_2reg, max9877_set_2reg, max9877_output_tlv),
- SOC_SINGLE_EXT("MAX9877 INB Stereo Switch",
- MAX9877_INPUT_MODE, 4, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 INA Stereo Switch",
- MAX9877_INPUT_MODE, 5, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch",
- MAX9877_INPUT_MODE, 6, 1, 0,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch",
- MAX9877_OUTPUT_MODE, 6, 1, 0,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch",
- MAX9877_OUTPUT_MODE, 7, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0],
- max9877_get_out_mode, max9877_set_out_mode),
- SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1],
- max9877_get_osc_mode, max9877_set_osc_mode),
+ SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume",
+ MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume",
+ MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume",
+ MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv),
+ SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume",
+ MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+ max9877_output_tlv),
+ SOC_SINGLE("MAX9877 INB Stereo Switch",
+ MAX9877_INPUT_MODE, 4, 1, 1),
+ SOC_SINGLE("MAX9877 INA Stereo Switch",
+ MAX9877_INPUT_MODE, 5, 1, 1),
+ SOC_SINGLE("MAX9877 Zero-crossing detection Switch",
+ MAX9877_INPUT_MODE, 6, 1, 0),
+ SOC_SINGLE("MAX9877 Bypass Mode Switch",
+ MAX9877_OUTPUT_MODE, 6, 1, 0),
+ SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]),
+ SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]),
};
-/* This function is called from ASoC machine driver */
-int max9877_add_controls(struct snd_soc_codec *codec)
-{
- return snd_soc_add_codec_controls(codec, max9877_controls,
- ARRAY_SIZE(max9877_controls));
-}
-EXPORT_SYMBOL_GPL(max9877_add_controls);
+static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("INA1"),
+SND_SOC_DAPM_INPUT("INA2"),
+SND_SOC_DAPM_INPUT("INB1"),
+SND_SOC_DAPM_INPUT("INB2"),
+SND_SOC_DAPM_INPUT("RXIN+"),
+SND_SOC_DAPM_INPUT("RXIN-"),
+
+SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route max9877_dapm_routes[] = {
+ { "SHDN", NULL, "INA1" },
+ { "SHDN", NULL, "INA2" },
+ { "SHDN", NULL, "INB1" },
+ { "SHDN", NULL, "INB2" },
+
+ { "OUT+", NULL, "RXIN+" },
+ { "OUT+", NULL, "SHDN" },
+
+ { "OUT-", NULL, "SHDN" },
+ { "OUT-", NULL, "RXIN-" },
+
+ { "HPL", NULL, "SHDN" },
+ { "HPR", NULL, "SHDN" },
+};
+
+static const struct snd_soc_codec_driver max9877_codec = {
+ .controls = max9877_controls,
+ .num_controls = ARRAY_SIZE(max9877_controls),
+
+ .dapm_widgets = max9877_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets),
+ .dapm_routes = max9877_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes),
+};
+
+static const struct regmap_config max9877_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = max9877_regs,
+ .num_reg_defaults = ARRAY_SIZE(max9877_regs),
+ .cache_type = REGCACHE_RBTREE,
+};
static int max9877_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- i2c = client;
+ int i;
- max9877_write_regs();
+ regmap = devm_regmap_init_i2c(client, &max9877_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
- return 0;
+ /* Ensure the device is in reset state */
+ for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
+ regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
+
+ return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0);
}
static int max9877_i2c_remove(struct i2c_client *client)
{
- i2c = NULL;
+ snd_soc_unregister_codec(&client->dev);
return 0;
}
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 5402dfbbb716..4d3c8fd8c5db 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -94,7 +94,6 @@
#define AUDIO_DAC_CFS_DLY_B (1 << 10)
struct mc13783_priv {
- struct snd_soc_codec codec;
struct mc13xxx *mc13xxx;
enum mc13783_ssi_port adc_ssi_port;
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
new file mode 100644
index 000000000000..651ce0923675
--- /dev/null
+++ b/sound/soc/codecs/pcm1681.c
@@ -0,0 +1,339 @@
+/*
+ * PCM1681 ASoC codec driver
+ *
+ * Copyright (c) StreamUnlimited GmbH 2013
+ * Marek Belisko <marek.belisko@streamunlimited.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.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1681_SOFT_MUTE_ALL 0xff
+#define PCM1681_DEEMPH_RATE_MASK 0x18
+#define PCM1681_DEEMPH_MASK 0x01
+
+#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */
+#define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */
+#define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */
+#define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */
+#define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */
+#define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */
+
+static const struct reg_default pcm1681_reg_defaults[] = {
+ { 0x01, 0xff },
+ { 0x02, 0xff },
+ { 0x03, 0xff },
+ { 0x04, 0xff },
+ { 0x05, 0xff },
+ { 0x06, 0xff },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x06 },
+ { 0x0A, 0x00 },
+ { 0x0B, 0xff },
+ { 0x0C, 0x0f },
+ { 0x0D, 0x00 },
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+};
+
+static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return !((reg == 0x00) || (reg == 0x0f));
+}
+
+static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+{
+ return pcm1681_accessible_reg(dev, reg) &&
+ (reg != PCM1681_ZERO_DETECT_STATUS);
+}
+
+struct pcm1681_private {
+ struct regmap *regmap;
+ unsigned int format;
+ /* Current deemphasis status */
+ unsigned int deemph;
+ /* Current rate for deemphasis control */
+ unsigned int rate;
+};
+
+static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
+
+static int pcm1681_set_deemph(struct snd_soc_codec *codec)
+{
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int i = 0, val = -1, enable = 0;
+
+ if (priv->deemph)
+ for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++)
+ if (pcm1681_deemph[i] == priv->rate)
+ val = i;
+
+ if (val != -1) {
+ regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_RATE_MASK, val);
+ enable = 1;
+ } else
+ enable = 0;
+
+ /* enable/disable deemphasis functionality */
+ return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_MASK, enable);
+}
+
+static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = priv->deemph;
+
+ return 0;
+}
+
+static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->deemph = ucontrol->value.enumerated.item[0];
+
+ return pcm1681_set_deemph(codec);
+}
+
+static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ /* The PCM1681 can only be slave to all clocks */
+ if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ dev_err(codec->dev, "Invalid clocking mode\n");
+ return -EINVAL;
+ }
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val;
+
+ if (mute)
+ val = PCM1681_SOFT_MUTE_ALL;
+ else
+ val = 0;
+
+ return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val);
+}
+
+static int pcm1681_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 pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val = 0, ret;
+ int pcm_format = params_format(params);
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (pcm_format == SNDRV_PCM_FORMAT_S24_LE)
+ val = 0x00;
+ else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+ val = 0x03;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x04;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x05;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val);
+ if (ret < 0)
+ return ret;
+
+ return pcm1681_set_deemph(codec);
+}
+
+static const struct snd_soc_dai_ops pcm1681_dai_ops = {
+ .set_fmt = pcm1681_set_dai_fmt,
+ .hw_params = pcm1681_hw_params,
+ .digital_mute = pcm1681_digital_mute,
+};
+
+static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUT1"),
+SND_SOC_DAPM_OUTPUT("VOUT2"),
+SND_SOC_DAPM_OUTPUT("VOUT3"),
+SND_SOC_DAPM_OUTPUT("VOUT4"),
+SND_SOC_DAPM_OUTPUT("VOUT5"),
+SND_SOC_DAPM_OUTPUT("VOUT6"),
+SND_SOC_DAPM_OUTPUT("VOUT7"),
+SND_SOC_DAPM_OUTPUT("VOUT8"),
+};
+
+static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = {
+ { "VOUT1", NULL, "Playback" },
+ { "VOUT2", NULL, "Playback" },
+ { "VOUT3", NULL, "Playback" },
+ { "VOUT4", NULL, "Playback" },
+ { "VOUT5", NULL, "Playback" },
+ { "VOUT6", NULL, "Playback" },
+ { "VOUT7", NULL, "Playback" },
+ { "VOUT8", NULL, "Playback" },
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1);
+
+static const struct snd_kcontrol_new pcm1681_controls[] = {
+ SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
+ PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
+ PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
+ PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume",
+ PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ pcm1681_get_deemph, pcm1681_put_deemph),
+};
+
+static struct snd_soc_dai_driver pcm1681_dai = {
+ .name = "pcm1681-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = PCM1681_PCM_RATES,
+ .formats = PCM1681_PCM_FORMATS,
+ },
+ .ops = &pcm1681_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1681_dt_ids[] = {
+ { .compatible = "ti,pcm1681", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
+#endif
+
+static const struct regmap_config pcm1681_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ARRAY_SIZE(pcm1681_reg_defaults) + 1,
+ .reg_defaults = pcm1681_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1681_reg_defaults),
+ .writeable_reg = pcm1681_writeable_reg,
+ .readable_reg = pcm1681_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = {
+ .controls = pcm1681_controls,
+ .num_controls = ARRAY_SIZE(pcm1681_controls),
+ .dapm_widgets = pcm1681_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets),
+ .dapm_routes = pcm1681_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes),
+};
+
+static const struct i2c_device_id pcm1681_i2c_id[] = {
+ {"pcm1681", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
+
+static int pcm1681_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct pcm1681_private *priv;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, priv);
+
+ return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681,
+ &pcm1681_dai, 1);
+}
+
+static int pcm1681_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static struct i2c_driver pcm1681_i2c_driver = {
+ .driver = {
+ .name = "pcm1681",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcm1681_dt_ids),
+ },
+ .id_table = pcm1681_i2c_id,
+ .probe = pcm1681_i2c_probe,
+ .remove = pcm1681_i2c_remove,
+};
+
+module_i2c_driver(pcm1681_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Marek Belisko <marek.belisko@streamunlimited.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c
new file mode 100644
index 000000000000..2a8eccf64c76
--- /dev/null
+++ b/sound/soc/codecs/pcm1792a.c
@@ -0,0 +1,257 @@
+/*
+ * PCM1792A ASoC codec driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ * Michael Trimarchi <michael@amarulasolutions.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.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of_device.h>
+
+#include "pcm1792a.h"
+
+#define PCM1792A_DAC_VOL_LEFT 0x10
+#define PCM1792A_DAC_VOL_RIGHT 0x11
+#define PCM1792A_FMT_CONTROL 0x12
+#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL
+
+#define PCM1792A_FMT_MASK 0x70
+#define PCM1792A_FMT_SHIFT 4
+#define PCM1792A_MUTE_MASK 0x01
+#define PCM1792A_MUTE_SHIFT 0
+#define PCM1792A_ATLD_ENABLE (1 << 7)
+
+static const struct reg_default pcm1792a_reg_defaults[] = {
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x50 },
+ { 0x13, 0x00 },
+ { 0x14, 0x00 },
+ { 0x15, 0x01 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+};
+
+static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= 0x10 && reg <= 0x17;
+}
+
+static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg)
+{
+ bool accessible;
+
+ accessible = pcm1792a_accessible_reg(dev, reg);
+
+ return accessible && reg != 0x16 && reg != 0x17;
+}
+
+struct pcm1792a_private {
+ struct regmap *regmap;
+ unsigned int format;
+ unsigned int rate;
+};
+
+static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE,
+ PCM1792A_MUTE_MASK, !!mute);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcm1792a_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 pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val = 0, ret;
+ int pcm_format = params_format(params);
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
+ pcm_format == SNDRV_PCM_FORMAT_S32_LE)
+ val = 0x02;
+ else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+ val = 0x00;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
+ pcm_format == SNDRV_PCM_FORMAT_S32_LE)
+ val = 0x05;
+ else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+ val = 0x04;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE;
+
+ ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL,
+ PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
+ .set_fmt = pcm1792a_set_dai_fmt,
+ .hw_params = pcm1792a_hw_params,
+ .digital_mute = pcm1792a_digital_mute,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1);
+
+static const struct snd_kcontrol_new pcm1792a_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
+ PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
+ pcm1792a_dac_tlv),
+};
+
+static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("IOUTL+"),
+SND_SOC_DAPM_OUTPUT("IOUTL-"),
+SND_SOC_DAPM_OUTPUT("IOUTR+"),
+SND_SOC_DAPM_OUTPUT("IOUTR-"),
+};
+
+static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = {
+ { "IOUTL+", NULL, "Playback" },
+ { "IOUTL-", NULL, "Playback" },
+ { "IOUTR+", NULL, "Playback" },
+ { "IOUTR-", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver pcm1792a_dai = {
+ .name = "pcm1792a-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PCM1792A_RATES,
+ .formats = PCM1792A_FORMATS, },
+ .ops = &pcm1792a_dai_ops,
+};
+
+static const struct of_device_id pcm1792a_of_match[] = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
+
+static const struct regmap_config pcm1792a_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 24,
+ .reg_defaults = pcm1792a_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1792a_reg_defaults),
+ .writeable_reg = pcm1792a_writeable_reg,
+ .readable_reg = pcm1792a_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
+ .controls = pcm1792a_controls,
+ .num_controls = ARRAY_SIZE(pcm1792a_controls),
+ .dapm_widgets = pcm1792a_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1792a_dapm_widgets),
+ .dapm_routes = pcm1792a_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1792a_dapm_routes),
+};
+
+static int pcm1792a_spi_probe(struct spi_device *spi)
+{
+ struct pcm1792a_private *pcm1792a;
+ int ret;
+
+ pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private),
+ GFP_KERNEL);
+ if (!pcm1792a)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, pcm1792a);
+
+ pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap);
+ if (IS_ERR(pcm1792a->regmap)) {
+ ret = PTR_ERR(pcm1792a->regmap);
+ dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1);
+}
+
+static int pcm1792a_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id pcm1792a_spi_ids[] = {
+ { "pcm1792a", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
+
+static struct spi_driver pcm1792a_codec_driver = {
+ .driver = {
+ .name = "pcm1792a",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcm1792a_of_match),
+ },
+ .id_table = pcm1792a_spi_ids,
+ .probe = pcm1792a_spi_probe,
+ .remove = pcm1792a_spi_remove,
+};
+
+module_spi_driver(pcm1792a_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1792A driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm1792a.h
new file mode 100644
index 000000000000..7a83d1fc102a
--- /dev/null
+++ b/sound/soc/codecs/pcm1792a.h
@@ -0,0 +1,26 @@
+/*
+ * definitions for PCM1792A
+ *
+ * Copyright 2013 Amarula Solutions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __PCM1792A_H__
+#define __PCM1792A_H__
+
+#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+
+#endif
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index f2a6282b41f4..b6618c4a7597 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -28,7 +28,54 @@
#include "pcm3008.h"
-#define PCM3008_VERSION "0.2"
+static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct pcm3008_setup_data *setup = codec->dev->platform_data;
+
+ gpio_set_value_cansleep(setup->pdda_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct pcm3008_setup_data *setup = codec->dev->platform_data;
+
+ gpio_set_value_cansleep(setup->pdad_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINL"),
+SND_SOC_DAPM_INPUT("VINR"),
+
+SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = {
+ { "PCM3008 Capture", NULL, "ADC" },
+ { "ADC", NULL, "VINL" },
+ { "ADC", NULL, "VINR" },
+
+ { "DAC", NULL, "PCM3008 Playback" },
+ { "VOUTL", NULL, "DAC" },
+ { "VOUTR", NULL, "DAC" },
+};
#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
@@ -51,20 +98,20 @@ static struct snd_soc_dai_driver pcm3008_dai = {
},
};
-static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
-{
- gpio_free(setup->dem0_pin);
- gpio_free(setup->dem1_pin);
- gpio_free(setup->pdad_pin);
- gpio_free(setup->pdda_pin);
-}
+static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
+ .dapm_widgets = pcm3008_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets),
+ .dapm_routes = pcm3008_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes),
+};
-static int pcm3008_soc_probe(struct snd_soc_codec *codec)
+static int pcm3008_codec_probe(struct platform_device *pdev)
{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
- int ret = 0;
+ struct pcm3008_setup_data *setup = pdev->dev.platform_data;
+ int ret;
- printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
+ if (!setup)
+ return -EINVAL;
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
@@ -74,83 +121,29 @@ static int pcm3008_soc_probe(struct snd_soc_codec *codec)
*/
/* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
- ret = gpio_request(setup->dem0_pin, "codec_dem0");
- if (ret == 0)
- ret = gpio_direction_output(setup->dem0_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin,
+ GPIOF_OUT_INIT_HIGH, "codec_dem0");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
- ret = gpio_request(setup->dem1_pin, "codec_dem1");
- if (ret == 0)
- ret = gpio_direction_output(setup->dem1_pin, 0);
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin,
+ GPIOF_OUT_INIT_LOW, "codec_dem1");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure PDAD GPIO. */
- ret = gpio_request(setup->pdad_pin, "codec_pdad");
- if (ret == 0)
- ret = gpio_direction_output(setup->pdad_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdad");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure PDDA GPIO. */
- ret = gpio_request(setup->pdda_pin, "codec_pdda");
- if (ret == 0)
- ret = gpio_direction_output(setup->pdda_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdda");
if (ret != 0)
- goto gpio_err;
-
- return ret;
-
-gpio_err:
- pcm3008_gpio_free(setup);
+ return ret;
- return ret;
-}
-
-static int pcm3008_soc_remove(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- pcm3008_gpio_free(setup);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int pcm3008_soc_suspend(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- gpio_set_value(setup->pdad_pin, 0);
- gpio_set_value(setup->pdda_pin, 0);
-
- return 0;
-}
-
-static int pcm3008_soc_resume(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- gpio_set_value(setup->pdad_pin, 1);
- gpio_set_value(setup->pdda_pin, 1);
-
- return 0;
-}
-#else
-#define pcm3008_soc_suspend NULL
-#define pcm3008_soc_resume NULL
-#endif
-
-static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
- .probe = pcm3008_soc_probe,
- .remove = pcm3008_soc_remove,
- .suspend = pcm3008_soc_suspend,
- .resume = pcm3008_soc_resume,
-};
-
-static int pcm3008_codec_probe(struct platform_device *pdev)
-{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_pcm3008, &pcm3008_dai, 1);
}
@@ -158,6 +151,7 @@ static int pcm3008_codec_probe(struct platform_device *pdev)
static int pcm3008_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
+
return 0;
}
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index ce585e37e38a..c26a8f814b18 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -50,8 +50,6 @@ static const struct regmap_range_cfg rt5640_ranges[] = {
static struct reg_default init_list[] = {
{RT5640_PR_BASE + 0x3d, 0x3600},
- {RT5640_PR_BASE + 0x1c, 0x0D21},
- {RT5640_PR_BASE + 0x1b, 0x0000},
{RT5640_PR_BASE + 0x12, 0x0aa8},
{RT5640_PR_BASE + 0x14, 0x0aaa},
{RT5640_PR_BASE + 0x20, 0x6110},
@@ -384,15 +382,11 @@ static const SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5640_snd_controls[] = {
/* Speaker Output Volume */
- SOC_DOUBLE("Speaker Playback Switch", RT5640_SPK_VOL,
- RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL,
RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
/* Headphone Output Volume */
- SOC_DOUBLE("HP Playback Switch", RT5640_HP_VOL,
- RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL,
RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL,
@@ -737,28 +731,21 @@ static const struct snd_kcontrol_new rt5640_mono_mix[] = {
RT5640_M_BST1_MM_SFT, 1, 1),
};
-/* INL/R source */
-static const char * const rt5640_inl_src[] = {
- "IN2P", "MONOP"
-};
+static const struct snd_kcontrol_new spk_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
-static const SOC_ENUM_SINGLE_DECL(
- rt5640_inl_enum, RT5640_INL_INR_VOL,
- RT5640_INL_SEL_SFT, rt5640_inl_src);
+static const struct snd_kcontrol_new spk_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
-static const struct snd_kcontrol_new rt5640_inl_mux =
- SOC_DAPM_ENUM("INL source", rt5640_inl_enum);
+static const struct snd_kcontrol_new hp_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
-static const char * const rt5640_inr_src[] = {
- "IN2N", "MONON"
-};
-
-static const SOC_ENUM_SINGLE_DECL(
- rt5640_inr_enum, RT5640_INL_INR_VOL,
- RT5640_INR_SEL_SFT, rt5640_inr_src);
-
-static const struct snd_kcontrol_new rt5640_inr_mux =
- SOC_DAPM_ENUM("INR source", rt5640_inr_enum);
+static const struct snd_kcontrol_new hp_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
/* Stereo ADC source */
static const char * const rt5640_stereo_adc1_src[] = {
@@ -891,33 +878,6 @@ static const SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5640_sdi_mux =
SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
-static int spk_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1,
- 0x0001, 0x0001);
- regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c,
- 0xf000, 0xf000);
- break;
-
- case SND_SOC_DAPM_PRE_PMD:
- regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c,
- 0xf000, 0x0000);
- regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1,
- 0x0001, 0x0000);
- break;
-
- default:
- return 0;
- }
- return 0;
-}
-
static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -966,6 +926,117 @@ static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w,
return 0;
}
+void hp_amp_power_on(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ /* depop parameters */
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK, RT5640_DEPOP_MAN);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK,
+ RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU);
+ regmap_write(rt5640->regmap, RT5640_PR_BASE + RT5640_HP_DCC_INT1,
+ 0x9f00);
+ /* headphone amp power on */
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2, 0);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_HA,
+ RT5640_PWR_HA);
+ usleep_range(10000, 15000);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2 ,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2);
+}
+
+static void rt5640_pmu_depop(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK,
+ RT5640_DEPOP_AUTO | RT5640_DIG_DP_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_CHARGE_PUMP,
+ RT5640_PM_HP_MASK, RT5640_PM_HP_HV);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M3,
+ RT5640_CP_FQ1_MASK | RT5640_CP_FQ2_MASK | RT5640_CP_FQ3_MASK,
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ1_SFT) |
+ (RT5640_CP_FQ_12_KHZ << RT5640_CP_FQ2_SFT) |
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ3_SFT));
+
+ regmap_write(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_MAMP_INT_REG2, 0x1c00);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK,
+ RT5640_HP_CP_PD | RT5640_HP_SG_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0400);
+}
+
+static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt5640_pmu_depop(codec);
+ rt5640->hp_mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ rt5640->hp_mute = 1;
+ usleep_range(70000, 75000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power_on(codec);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!rt5640->hp_mute)
+ usleep_range(80000, 85000);
+
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
RT5640_PWR_PLL_BIT, 0, NULL, 0),
@@ -1005,9 +1076,6 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
RT5640_PWR_IN_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL,
RT5640_PWR_IN_R_BIT, 0, NULL, 0),
- /* IN Mux */
- SND_SOC_DAPM_MUX("INL Mux", SND_SOC_NOPM, 0, 0, &rt5640_inl_mux),
- SND_SOC_DAPM_MUX("INR Mux", SND_SOC_NOPM, 0, 0, &rt5640_inr_mux),
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0,
rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)),
@@ -1158,15 +1226,28 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
RT5640_PWR_MA_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Improve HP Amp Drv", RT5640_PWR_ANLG1,
- SND_SOC_NOPM, 0, NULL, 0),
- SND_SOC_DAPM_PGA("HP L Amp", RT5640_PWR_ANLG1,
+ SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM,
+ 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0,
+ rt5640_hp_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1,
RT5640_PWR_HP_L_BIT, 0, NULL, 0),
- SND_SOC_DAPM_PGA("HP R Amp", RT5640_PWR_ANLG1,
+ SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1,
RT5640_PWR_HP_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1,
- SND_SOC_NOPM, 0, spk_event,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5640_PWR_CLS_D_BIT, 0, NULL, 0),
+
+ /* Output Switch */
+ SND_SOC_DAPM_SWITCH("Speaker L Playback", SND_SOC_NOPM, 0, 0,
+ &spk_l_enable_control),
+ SND_SOC_DAPM_SWITCH("Speaker R Playback", SND_SOC_NOPM, 0, 0,
+ &spk_r_enable_control),
+ SND_SOC_DAPM_SWITCH("HP L Playback", SND_SOC_NOPM, 0, 0,
+ &hp_l_enable_control),
+ SND_SOC_DAPM_SWITCH("HP R Playback", SND_SOC_NOPM, 0, 0,
+ &hp_r_enable_control),
+ SND_SOC_DAPM_POST("HP Post", rt5640_hp_post_event),
/* Output Lines */
SND_SOC_DAPM_OUTPUT("SPOLP"),
SND_SOC_DAPM_OUTPUT("SPOLN"),
@@ -1407,9 +1488,11 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
{"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"},
{"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"},
+ {"HPO MIX L", NULL, "HP L Amp"},
{"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
{"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"},
{"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"},
+ {"HPO MIX R", NULL, "HP R Amp"},
{"LOUT MIX", "DAC L1 Switch", "DAC L1"},
{"LOUT MIX", "DAC R1 Switch", "DAC R1"},
@@ -1422,13 +1505,15 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
{"Mono MIX", "BST1 Switch", "BST1"},
- {"HP L Amp", NULL, "HPO MIX L"},
- {"HP R Amp", NULL, "HPO MIX R"},
+ {"HP Amp", NULL, "HPO MIX L"},
+ {"HP Amp", NULL, "HPO MIX R"},
- {"SPOLP", NULL, "SPOL MIX"},
- {"SPOLN", NULL, "SPOL MIX"},
- {"SPORP", NULL, "SPOR MIX"},
- {"SPORN", NULL, "SPOR MIX"},
+ {"Speaker L Playback", "Switch", "SPOL MIX"},
+ {"Speaker R Playback", "Switch", "SPOR MIX"},
+ {"SPOLP", NULL, "Speaker L Playback"},
+ {"SPOLN", NULL, "Speaker L Playback"},
+ {"SPORP", NULL, "Speaker R Playback"},
+ {"SPORN", NULL, "Speaker R Playback"},
{"SPOLP", NULL, "Improve SPK Amp Drv"},
{"SPOLN", NULL, "Improve SPK Amp Drv"},
@@ -1438,8 +1523,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"HPOL", NULL, "Improve HP Amp Drv"},
{"HPOR", NULL, "Improve HP Amp Drv"},
- {"HPOL", NULL, "HP L Amp"},
- {"HPOR", NULL, "HP R Amp"},
+ {"HP L Playback", "Switch", "HP Amp"},
+ {"HP R Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HP L Playback"},
+ {"HPOR", NULL, "HP R Playback"},
{"LOUTL", NULL, "LOUT MIX"},
{"LOUTR", NULL, "LOUT MIX"},
{"MONOP", NULL, "Mono MIX"},
@@ -1818,17 +1905,13 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
RT5640_PWR_BG | RT5640_PWR_VREF2,
RT5640_PWR_VREF1 | RT5640_PWR_MB |
RT5640_PWR_BG | RT5640_PWR_VREF2);
- mdelay(10);
+ usleep_range(10000, 15000);
snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
regcache_sync(rt5640->regmap);
snd_soc_update_bits(codec, RT5640_DUMMY1,
0x0301, 0x0301);
- snd_soc_update_bits(codec, RT5640_DEPOP_M1,
- 0x001d, 0x0019);
- snd_soc_update_bits(codec, RT5640_DEPOP_M2,
- 0x2000, 0x2000);
snd_soc_update_bits(codec, RT5640_MICBIAS,
0x0030, 0x0030);
}
@@ -1872,8 +1955,6 @@ static int rt5640_probe(struct snd_soc_codec *codec)
rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
- snd_soc_update_bits(codec, RT5640_DEPOP_M1, 0x001d, 0x0019);
- snd_soc_update_bits(codec, RT5640_DEPOP_M2, 0x2000, 0x2000);
snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
@@ -2095,6 +2176,8 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
RT5640_IN_DF2, RT5640_IN_DF2);
+ rt5640->hp_mute = 1;
+
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
rt5640_dai, ARRAY_SIZE(rt5640_dai));
if (ret < 0)
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index c48286d7118f..5e8df25a13f3 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -145,6 +145,8 @@
/* Index of Codec Private Register definition */
+#define RT5640_CHPUMP_INT_REG1 0x24
+#define RT5640_MAMP_INT_REG2 0x37
#define RT5640_3D_SPK 0x63
#define RT5640_WND_1 0x6c
#define RT5640_WND_2 0x6d
@@ -153,6 +155,7 @@
#define RT5640_WND_5 0x70
#define RT5640_WND_8 0x73
#define RT5640_DIP_SPK_INF 0x75
+#define RT5640_HP_DCC_INT1 0x77
#define RT5640_EQ_BW_LOP 0xa0
#define RT5640_EQ_GN_LOP 0xa1
#define RT5640_EQ_FC_BP1 0xa2
@@ -1201,6 +1204,14 @@
#define RT5640_CP_FQ2_SFT 4
#define RT5640_CP_FQ3_MASK (0x7)
#define RT5640_CP_FQ3_SFT 0
+#define RT5640_CP_FQ_1_5_KHZ 0
+#define RT5640_CP_FQ_3_KHZ 1
+#define RT5640_CP_FQ_6_KHZ 2
+#define RT5640_CP_FQ_12_KHZ 3
+#define RT5640_CP_FQ_24_KHZ 4
+#define RT5640_CP_FQ_48_KHZ 5
+#define RT5640_CP_FQ_96_KHZ 6
+#define RT5640_CP_FQ_192_KHZ 7
/* HPOUT charge pump (0x91) */
#define RT5640_OSW_L_MASK (0x1 << 11)
@@ -2087,6 +2098,7 @@ struct rt5640_priv {
int pll_out;
int dmic_en;
+ bool hp_mute;
};
#endif
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 760e8bfeacaa..1f4093f3f3a1 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -654,16 +654,19 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+
+ /* if using pll, clk_ctrl must be set after pll power up */
+ snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
} else {
+ /* otherwise, clk_ctrl must be set before pll power down */
+ snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+
/* power down pll */
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
0);
}
- /* if using pll, clk_ctrl must be set after pll power up */
- snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
-
return 0;
}
@@ -1480,6 +1483,7 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
static const struct regmap_config sgtl5000_regmap = {
.reg_bits = 16,
.val_bits = 16,
+ .reg_stride = 2,
.max_register = SGTL5000_MAX_REG_OFFSET,
.volatile_reg = sgtl5000_volatile,
diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c
index 73e205c892a0..38f3b105c17d 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -102,6 +102,16 @@ static int si476x_codec_write(struct snd_soc_codec *codec,
return err;
}
+static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+};
+
+static const struct snd_soc_dapm_route si476x_dapm_routes[] = {
+ { "Capture", NULL, "LOUT" },
+ { "Capture", NULL, "ROUT" },
+};
+
static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
@@ -260,6 +270,10 @@ static struct snd_soc_codec_driver soc_codec_dev_si476x = {
.probe = si476x_codec_probe,
.read = si476x_codec_read,
.write = si476x_codec_write,
+ .dapm_widgets = si476x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
+ .dapm_routes = si476x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
};
static int si476x_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c
index e9d7881ed2c8..e3501f40c7b3 100644
--- a/sound/soc/codecs/spdif_receiver.c
+++ b/sound/soc/codecs/spdif_receiver.c
@@ -23,11 +23,26 @@
#include <sound/initval.h>
#include <linux/of.h>
+static const struct snd_soc_dapm_widget dir_widgets[] = {
+ SND_SOC_DAPM_INPUT("spdif-in"),
+};
+
+static const struct snd_soc_dapm_route dir_routes[] = {
+ { "Capture", NULL, "spdif-in" },
+};
+
#define STUB_RATES SNDRV_PCM_RATE_8000_192000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
-static struct snd_soc_codec_driver soc_codec_spdif_dir;
+static struct snd_soc_codec_driver soc_codec_spdif_dir = {
+ .dapm_widgets = dir_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
+ .dapm_routes = dir_routes,
+ .num_dapm_routes = ARRAY_SIZE(dir_routes),
+};
static struct snd_soc_dai_driver dir_stub_dai = {
.name = "dir-hifi",
diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c
index 18280499fd55..a078aa31052a 100644
--- a/sound/soc/codecs/spdif_transmitter.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -25,10 +25,24 @@
#define DRV_NAME "spdif-dit"
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
-#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+static const struct snd_soc_dapm_widget dit_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("spdif-out"),
+};
+
+static const struct snd_soc_dapm_route dit_routes[] = {
+ { "spdif-out", NULL, "Playback" },
+};
-static struct snd_soc_codec_driver soc_codec_spdif_dit;
+static struct snd_soc_codec_driver soc_codec_spdif_dit = {
+ .dapm_widgets = dit_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
+ .dapm_routes = dit_routes,
+ .num_dapm_routes = ARRAY_SIZE(dit_routes),
+};
static struct snd_soc_dai_driver dit_stub_dai = {
.name = "dit-hifi",
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index cfb55fe35e98..06edb396e733 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -363,16 +363,18 @@ static void sta32x_watchdog(struct work_struct *work)
}
if (!sta32x->shutdown)
- schedule_delayed_work(&sta32x->watchdog_work,
- round_jiffies_relative(HZ));
+ queue_delayed_work(system_power_efficient_wq,
+ &sta32x->watchdog_work,
+ round_jiffies_relative(HZ));
}
static void sta32x_watchdog_start(struct sta32x_priv *sta32x)
{
if (sta32x->pdata->needs_esd_watchdog) {
sta32x->shutdown = 0;
- schedule_delayed_work(&sta32x->watchdog_work,
- round_jiffies_relative(HZ));
+ queue_delayed_work(system_power_efficient_wq,
+ &sta32x->watchdog_work,
+ round_jiffies_relative(HZ));
}
}
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index b1f6982c7c9c..7b8f3d965f43 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -29,7 +29,7 @@ MODULE_LICENSE("GPL");
/* AIC26 driver private data */
struct aic26 {
struct spi_device *spi;
- struct snd_soc_codec codec;
+ struct snd_soc_codec *codec;
int master;
int datfm;
int mclk;
@@ -119,6 +119,22 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
+static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route tlv320aic26_dapm_routes[] = {
+ { "Capture", NULL, "MICIN" },
+ { "Capture", NULL, "AUX" },
+
+ { "HPL", NULL, "Playback" },
+ { "HPR", NULL, "Playback" },
+};
+
/* ---------------------------------------------------------------------
* Digital Audio Interface Operations
*/
@@ -174,9 +190,9 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
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);
+ snd_soc_write(codec, AIC26_REG_PLL_PROG1, reg);
reg = dval << 2;
- aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg);
+ snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg);
/* Audio Control 3 (master mode, fsref rate) */
reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3);
@@ -185,13 +201,13 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
reg |= 0x0800;
if (fsref == 48000)
reg |= 0x2000;
- aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
/* Audio Control 1 (FSref divisor) */
reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1);
reg &= ~0x0fff;
reg |= wlen | aic26->datfm | (divisor << 3) | divisor;
- aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
+ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
return 0;
}
@@ -212,7 +228,7 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute)
reg |= 0x8080;
else
reg &= ~0x8080;
- aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg);
+ snd_soc_write(codec, AIC26_REG_DAC_GAIN, reg);
return 0;
}
@@ -330,7 +346,7 @@ static ssize_t aic26_keyclick_show(struct device *dev,
struct aic26 *aic26 = dev_get_drvdata(dev);
int val, amp, freq, len;
- val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2);
amp = (val >> 12) & 0x7;
freq = (125 << ((val >> 8) & 0x7)) >> 1;
len = 2 * (1 + ((val >> 4) & 0xf));
@@ -346,9 +362,9 @@ static ssize_t aic26_keyclick_set(struct device *dev,
struct aic26 *aic26 = dev_get_drvdata(dev);
int val;
- val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2);
val |= 0x8000;
- aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
+ snd_soc_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
return count;
}
@@ -360,25 +376,26 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
*/
static int aic26_probe(struct snd_soc_codec *codec)
{
+ struct aic26 *aic26 = dev_get_drvdata(codec->dev);
int ret, err, i, reg;
- dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n");
+ aic26->codec = codec;
/* Reset the codec to power on defaults */
- aic26_reg_write(codec, AIC26_REG_RESET, 0xBB00);
+ snd_soc_write(codec, AIC26_REG_RESET, 0xBB00);
/* Power up CODEC */
- aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0);
+ snd_soc_write(codec, AIC26_REG_POWER_CTRL, 0);
/* Audio Control 3 (master mode, fsref rate) */
- reg = aic26_reg_read(codec, AIC26_REG_AUDIO_CTRL3);
+ reg = snd_soc_read(codec, AIC26_REG_AUDIO_CTRL3);
reg &= ~0xf800;
reg |= 0x0800; /* set master mode */
- aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
/* Fill register cache */
for (i = 0; i < codec->driver->reg_cache_size; i++)
- aic26_reg_read(codec, i);
+ snd_soc_read(codec, i);
/* Register the sysfs files for debugging */
/* Create SysFS files */
@@ -401,6 +418,10 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = {
.write = aic26_reg_write,
.reg_cache_size = AIC26_NUM_REGS,
.reg_word_size = sizeof(u16),
+ .dapm_widgets = tlv320aic26_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets),
+ .dapm_routes = tlv320aic26_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes),
};
/* ---------------------------------------------------------------------
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 17df4e32feac..2ed57d4aa445 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -338,18 +338,6 @@ static inline int aic32x4_get_divs(int mclk, int rate)
return -EINVAL;
}
-static int aic32x4_add_widgets(struct snd_soc_codec *codec)
-{
- snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets,
- ARRAY_SIZE(aic32x4_dapm_widgets));
-
- snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes,
- ARRAY_SIZE(aic32x4_dapm_routes));
-
- snd_soc_dapm_new_widgets(&codec->dapm);
- return 0;
-}
-
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -683,9 +671,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
}
aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- snd_soc_add_codec_controls(codec, aic32x4_snd_controls,
- ARRAY_SIZE(aic32x4_snd_controls));
- aic32x4_add_widgets(codec);
/*
* Workaround: for an unknown reason, the ADC needs to be powered up
@@ -714,6 +699,13 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
.suspend = aic32x4_suspend,
.resume = aic32x4_resume,
.set_bias_level = aic32x4_set_bias_level,
+
+ .controls = aic32x4_snd_controls,
+ .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
+ .dapm_widgets = aic32x4_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
+ .dapm_routes = aic32x4_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
};
static int aic32x4_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index e5b926883131..6e3f269243e0 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -138,8 +138,7 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -147,10 +146,9 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- unsigned short val, val_mask;
- int ret;
- struct snd_soc_dapm_path *path;
- int found = 0;
+ unsigned short val;
+ struct snd_soc_dapm_update update;
+ int connect, change;
val = (ucontrol->value.integer.value[0] & mask);
@@ -158,42 +156,26 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
if (val)
val = mask;
+ connect = !!val;
+
if (invert)
val = mask - val;
- val_mask = mask << shift;
- val = val << shift;
-
- mutex_lock(&widget->codec->mutex);
- if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
- /* find dapm widget path assoc with kcontrol */
- list_for_each_entry(path, &widget->dapm->card->paths, list) {
- if (path->kcontrol != kcontrol)
- continue;
+ mask <<= shift;
+ val <<= shift;
- /* found, now check type */
- found = 1;
- if (val)
- /* new connection */
- path->connect = invert ? 0 : 1;
- else
- /* old connection must be powered down */
- path->connect = invert ? 1 : 0;
+ change = snd_soc_test_bits(codec, val, mask, reg);
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
- dapm_mark_dirty(path->source, "tlv320aic3x source");
- dapm_mark_dirty(path->sink, "tlv320aic3x sink");
-
- break;
- }
+ snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect,
+ &update);
}
- mutex_unlock(&widget->codec->mutex);
-
- if (found)
- snd_soc_dapm_sync(widget->dapm);
-
- ret = snd_soc_update_bits_locked(widget->codec, reg, val_mask, val);
- return ret;
+ return change;
}
/*
@@ -1492,6 +1474,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = {
{ "tlv320aic3x", AIC3X_MODEL_3X },
{ "tlv320aic33", AIC3X_MODEL_33 },
{ "tlv320aic3007", AIC3X_MODEL_3007 },
+ { "tlv320aic3106", AIC3X_MODEL_3X },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
@@ -1582,6 +1565,9 @@ static int aic3x_i2c_remove(struct i2c_client *client)
#if defined(CONFIG_OF)
static const struct of_device_id tlv320aic3x_of_match[] = {
{ .compatible = "ti,tlv320aic3x", },
+ { .compatible = "ti,tlv320aic33" },
+ { .compatible = "ti,tlv320aic3007" },
+ { .compatible = "ti,tlv320aic3106" },
{},
};
MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match);
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 8e6e5b016021..1e3884d6b3fb 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -137,8 +137,6 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
/* codec private data */
struct twl4030_priv {
- struct snd_soc_codec codec;
-
unsigned int codec_powered;
/* reference counts of AIF/APLL users */
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 44621ddc332d..3c79dbb6c323 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -429,7 +429,8 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
struct snd_soc_codec *codec = data;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hs_jack.work, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
@@ -437,9 +438,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 6d0aa44c3757..c94d4c1e3dac 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -325,7 +325,6 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int uda134x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u8 reg;
struct uda134x_platform_data *pd = codec->control_data;
int i;
u8 *cache = codec->reg_cache;
@@ -334,23 +333,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
- /* ADC, DAC on */
- switch (pd->model) {
- case UDA134X_UDA1340:
- case UDA134X_UDA1344:
- case UDA134X_UDA1345:
- reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
- uda134x_write(codec, UDA134X_DATA011, reg | 0x03);
- break;
- case UDA134X_UDA1341:
- reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
- uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
- break;
- default:
- printk(KERN_ERR "UDA134X SoC codec: "
- "unsupported model %d\n", pd->model);
- return -EINVAL;
- }
break;
case SND_SOC_BIAS_PREPARE:
/* power on */
@@ -362,23 +344,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
}
break;
case SND_SOC_BIAS_STANDBY:
- /* ADC, DAC power off */
- switch (pd->model) {
- case UDA134X_UDA1340:
- case UDA134X_UDA1344:
- case UDA134X_UDA1345:
- reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
- uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03));
- break;
- case UDA134X_UDA1341:
- reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
- uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
- break;
- default:
- printk(KERN_ERR "UDA134X SoC codec: "
- "unsupported model %d\n", pd->model);
- return -EINVAL;
- }
break;
case SND_SOC_BIAS_OFF:
/* power off */
@@ -450,6 +415,37 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
};
+/* UDA1341 has the DAC/ADC power down in STATUS1 */
+static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0),
+};
+
+/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */
+static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0),
+};
+
+/* Common DAPM widgets */
+static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route uda134x_dapm_routes[] = {
+ { "ADC", NULL, "VINL1" },
+ { "ADC", NULL, "VINR1" },
+ { "ADC", NULL, "VINL2" },
+ { "ADC", NULL, "VINR2" },
+ { "VOUTL", NULL, "DAC" },
+ { "VOUTR", NULL, "DAC" },
+};
+
static const struct snd_soc_dai_ops uda134x_dai_ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
@@ -485,6 +481,8 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
{
struct uda134x_priv *uda134x;
struct uda134x_platform_data *pd = codec->card->dev->platform_data;
+ const struct snd_soc_dapm_widget *widgets;
+ unsigned num_widgets;
int ret;
@@ -526,6 +524,22 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
else
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (pd->model == UDA134X_UDA1341) {
+ widgets = uda1341_dapm_widgets;
+ num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
+ } else {
+ widgets = uda1340_dapm_widgets;
+ num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
+ }
+
+ ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets);
+ if (ret) {
+ printk(KERN_ERR "%s failed to register dapm controls: %d",
+ __func__, ret);
+ kfree(uda134x);
+ return ret;
+ }
+
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
@@ -599,6 +613,10 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.read = uda134x_read_reg_cache,
.write = uda134x_write,
.set_bias_level = uda134x_set_bias_level,
+ .dapm_widgets = uda134x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
+ .dapm_routes = uda134x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
};
static int uda134x_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 54cd3da09abd..b7ab2ef567c8 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -290,6 +290,18 @@ static const struct snd_kcontrol_new wl1273_controls[] = {
snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put),
};
+static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route wl1273_dapm_routes[] = {
+ { "Capture", NULL, "RX" },
+
+ { "TX", NULL, "Playback" },
+};
+
static int wl1273_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -483,6 +495,11 @@ static int wl1273_remove(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_wl1273 = {
.probe = wl1273_probe,
.remove = wl1273_remove,
+
+ .dapm_widgets = wl1273_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
+ .dapm_routes = wl1273_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes),
};
static int wl1273_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index 10adc4145d46..d5ebcb00019b 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -420,7 +420,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
xfer->codec = codec;
list_add_tail(&xfer->list, &xfer_list);
- out = kzalloc(len, GFP_KERNEL);
+ out = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!out) {
dev_err(codec->dev,
"Failed to allocate RX buffer\n");
@@ -429,7 +429,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
}
xfer->t.rx_buf = out;
- img = kzalloc(len, GFP_KERNEL);
+ img = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!img) {
dev_err(codec->dev,
"Failed to allocate image buffer\n");
@@ -523,14 +523,14 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec)
dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
/* Copy to local buffer first as vmalloc causes problems for dma */
- img = kzalloc(fw->size, GFP_KERNEL);
+ img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
if (!img) {
dev_err(codec->dev, "Failed to allocate image buffer\n");
ret = -ENOMEM;
goto abort2;
}
- out = kzalloc(fw->size, GFP_KERNEL);
+ out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
if (!out) {
dev_err(codec->dev, "Failed to allocate output buffer\n");
ret = -ENOMEM;
@@ -670,14 +670,14 @@ static int wm0010_boot(struct snd_soc_codec *codec)
ret = -ENOMEM;
len = pll_rec.length + 8;
- out = kzalloc(len, GFP_KERNEL);
+ out = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!out) {
dev_err(codec->dev,
"Failed to allocate RX buffer\n");
goto abort;
}
- img_swap = kzalloc(len, GFP_KERNEL);
+ img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!img_swap) {
dev_err(codec->dev,
"Failed to allocate image buffer\n");
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 282fd232cdf7..8bbddc151aa8 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -998,6 +998,8 @@ SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -1421,9 +1423,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
- { "Mic Mute Mixer", NULL, "Noise Mixer" },
- { "Mic Mute Mixer", NULL, "Mic Mixer" },
-
{ "AIF1 Capture", NULL, "AIF1TX1" },
{ "AIF1 Capture", NULL, "AIF1TX2" },
{ "AIF1 Capture", NULL, "AIF1TX3" },
@@ -1499,23 +1498,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "IN3L PGA", NULL, "IN3L" },
{ "IN3R PGA", NULL, "IN3R" },
- { "ASRC1L", NULL, "ASRC1L Input" },
- { "ASRC1R", NULL, "ASRC1R Input" },
- { "ASRC2L", NULL, "ASRC2L Input" },
- { "ASRC2R", NULL, "ASRC2R Input" },
-
- { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" },
- { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" },
-
- { "ISRC1INT1", NULL, "ISRC1INT1 Input" },
- { "ISRC1INT2", NULL, "ISRC1INT2 Input" },
-
- { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" },
- { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" },
-
- { "ISRC2INT1", NULL, "ISRC2INT1 Input" },
- { "ISRC2INT2", NULL, "ISRC2INT2 Input" },
-
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1567,22 +1549,25 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MUX_ROUTES("ASRC1L"),
- ARIZONA_MUX_ROUTES("ASRC1R"),
- ARIZONA_MUX_ROUTES("ASRC2L"),
- ARIZONA_MUX_ROUTES("ASRC2R"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
- ARIZONA_MUX_ROUTES("ISRC1INT1"),
- ARIZONA_MUX_ROUTES("ISRC1INT2"),
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
- ARIZONA_MUX_ROUTES("ISRC1DEC1"),
- ARIZONA_MUX_ROUTES("ISRC1DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
- ARIZONA_MUX_ROUTES("ISRC2INT1"),
- ARIZONA_MUX_ROUTES("ISRC2INT2"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
- ARIZONA_MUX_ROUTES("ISRC2DEC1"),
- ARIZONA_MUX_ROUTES("ISRC2DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
ARIZONA_DSP_ROUTES("DSP1"),
@@ -1614,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "SPKDAT1R", NULL, "OUT5R" },
{ "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
};
static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1781,6 +1769,7 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
return ret;
arizona_init_spk(codec);
+ arizona_init_gpio(codec);
snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 2e7cb4ba161a..bbd64384ca1c 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -58,14 +58,10 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
static const struct snd_kcontrol_new wm5110_snd_controls[] = {
-SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
- ARIZONA_IN1_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
- ARIZONA_IN2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
- ARIZONA_IN3_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL,
- ARIZONA_IN4_OSR_SHIFT, 1, 0),
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
@@ -432,6 +428,9 @@ SND_SOC_DAPM_INPUT("IN3R"),
SND_SOC_DAPM_INPUT("IN4L"),
SND_SOC_DAPM_INPUT("IN4R"),
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -842,9 +841,6 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
- { "Mic Mute Mixer", NULL, "Noise Mixer" },
- { "Mic Mute Mixer", NULL, "Mic Mixer" },
-
{ "AIF1 Capture", NULL, "AIF1TX1" },
{ "AIF1 Capture", NULL, "AIF1TX2" },
{ "AIF1 Capture", NULL, "AIF1TX3" },
@@ -979,10 +975,13 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MUX_ROUTES("ASRC1L"),
- ARIZONA_MUX_ROUTES("ASRC1R"),
- ARIZONA_MUX_ROUTES("ASRC2L"),
- ARIZONA_MUX_ROUTES("ASRC2R"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
{ "HPOUT1L", NULL, "OUT1L" },
{ "HPOUT1R", NULL, "OUT1R" },
@@ -1006,6 +1005,11 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "SPKDAT2R", NULL, "OUT6R" },
{ "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+ { "DRC2 Signal Activity", NULL, "DRC2L" },
+ { "DRC2 Signal Activity", NULL, "DRC2R" },
};
static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1170,6 +1174,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
return ret;
arizona_init_spk(codec);
+ arizona_init_gpio(codec);
snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 0e8b3aaf6c8d..af1318ddb062 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1301,7 +1301,8 @@ static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data)
if (device_may_wakeup(wm8350->dev))
pm_wakeup_event(wm8350->dev, 250);
- schedule_delayed_work(&priv->hpl.work, msecs_to_jiffies(200));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hpl.work, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
@@ -1318,7 +1319,8 @@ static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
if (device_may_wakeup(wm8350->dev))
pm_wakeup_event(wm8350->dev, 250);
- schedule_delayed_work(&priv->hpr.work, msecs_to_jiffies(200));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hpr.work, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
index 462f5e4d5c05..7b1a6d5c11c6 100644
--- a/sound/soc/codecs/wm8727.c
+++ b/sound/soc/codecs/wm8727.c
@@ -23,6 +23,16 @@
#include <sound/initval.h>
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget wm8727_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8727_dapm_routes[] = {
+ { "VOUTL", NULL, "Playback" },
+ { "VOUTR", NULL, "Playback" },
+};
+
/*
* Note this is a simple chip with no configuration interface, sample rate is
* determined automatically by examining the Master clock and Bit clock ratios
@@ -43,7 +53,12 @@ static struct snd_soc_dai_driver wm8727_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8727;
+static struct snd_soc_codec_driver soc_codec_dev_wm8727 = {
+ .dapm_widgets = wm8727_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets),
+ .dapm_routes = wm8727_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes),
+};
static int wm8727_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 5276062d6c79..456bb8c6d759 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -45,6 +45,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
struct wm8731_priv {
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
+ const struct snd_pcm_hw_constraint_list *constraints;
unsigned int sysclk;
int sysclk_type;
int playback_fs;
@@ -290,6 +291,36 @@ static const struct _coeff_div coeff_div[] = {
{12000000, 88200, 136, 0xf, 0x1, 0x1},
};
+/* rates constraints */
+static const unsigned int wm8731_rates_12000000[] = {
+ 8000, 32000, 44100, 48000, 96000, 88200,
+};
+
+static const unsigned int wm8731_rates_12288000_18432000[] = {
+ 8000, 32000, 48000, 96000,
+};
+
+static const unsigned int wm8731_rates_11289600_16934400[] = {
+ 8000, 44100, 88200,
+};
+
+static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = {
+ .list = wm8731_rates_12000000,
+ .count = ARRAY_SIZE(wm8731_rates_12000000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = {
+ .list = wm8731_rates_12288000_18432000,
+ .count = ARRAY_SIZE(wm8731_rates_12288000_18432000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = {
+ .list = wm8731_rates_11289600_16934400,
+ .count = ARRAY_SIZE(wm8731_rates_11289600_16934400),
+};
+
static inline int get_coeff(int mclk, int rate)
{
int i;
@@ -362,17 +393,26 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
switch (freq) {
- case 11289600:
+ case 0:
+ wm8731->constraints = NULL;
+ break;
case 12000000:
+ wm8731->constraints = &wm8731_constraints_12000000;
+ break;
case 12288000:
- case 16934400:
case 18432000:
- wm8731->sysclk = freq;
+ wm8731->constraints = &wm8731_constraints_12288000_18432000;
+ break;
+ case 16934400:
+ case 11289600:
+ wm8731->constraints = &wm8731_constraints_11289600_16934400;
break;
default:
return -EINVAL;
}
+ wm8731->sysclk = freq;
+
snd_soc_dapm_sync(&codec->dapm);
return 0;
@@ -475,12 +515,26 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
+static int wm8731_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (wm8731->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ wm8731->constraints);
+
+ return 0;
+}
+
#define WM8731_RATES SNDRV_PCM_RATE_8000_96000
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static const struct snd_soc_dai_ops wm8731_dai_ops = {
+ .startup = wm8731_startup,
.hw_params = wm8731_hw_params,
.digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 0a4ab4c423d1..d96ebf52d953 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1456,8 +1456,9 @@ static int wm8753_resume(struct snd_soc_codec *codec)
if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
codec->dapm.bias_level = SND_SOC_BIAS_ON;
- schedule_delayed_work(&codec->dapm.delayed_work,
- msecs_to_jiffies(caps_charge));
+ queue_delayed_work(system_power_efficient_wq,
+ &codec->dapm.delayed_work,
+ msecs_to_jiffies(caps_charge));
}
return 0;
diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c
index f1fdbf63abb4..8092495605ce 100644
--- a/sound/soc/codecs/wm8782.c
+++ b/sound/soc/codecs/wm8782.c
@@ -26,6 +26,16 @@
#include <sound/initval.h>
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route wm8782_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+};
+
static struct snd_soc_dai_driver wm8782_dai = {
.name = "wm8782",
.capture = {
@@ -40,7 +50,12 @@ static struct snd_soc_dai_driver wm8782_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8782;
+static struct snd_soc_codec_driver soc_codec_dev_wm8782 = {
+ .dapm_widgets = wm8782_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets),
+ .dapm_routes = wm8782_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes),
+};
static int wm8782_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index fa24cedee687..eebcb1da3b7b 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -364,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
u16 reg;
int ret;
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 4c9fb142cb2d..4dfa8dceeabf 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1012,7 +1012,7 @@ static const struct soc_enum liner_enum =
SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text);
static const struct snd_kcontrol_new liner_mux =
- SOC_DAPM_ENUM("LINEL Mux", liner_enum);
+ SOC_DAPM_ENUM("LINER Mux", liner_enum);
static const char *sidetone_text[] = {
"None", "Left", "Right"
@@ -1202,7 +1202,6 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec)
break;
}
- snd_soc_dapm_new_widgets(dapm);
return 0;
}
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 0a4ffdd1d2a7..f156010e52bc 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -263,8 +263,8 @@ SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
-SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
- 0, 127, 0),
+SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC,
+ 0, 255, 0, adc_tlv),
SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
@@ -857,9 +857,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (pll_div.k) {
reg |= 0x20;
- snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
- snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
- snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff);
+ snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff);
+ snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff);
}
snd_soc_write(codec, WM8960_PLL1, reg);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index e2de9ecfd641..11d80f3b6137 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2621,8 +2621,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
wm8962->sysclk_rate = freq;
- wm8962_configure_bclk(codec);
-
return 0;
}
@@ -3046,8 +3044,9 @@ static irqreturn_t wm8962_irq(int irq, void *data)
pm_wakeup_event(dev, 300);
- schedule_delayed_work(&wm8962->mic_work,
- msecs_to_jiffies(250));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8962->mic_work,
+ msecs_to_jiffies(250));
}
return IRQ_HANDLED;
@@ -3175,7 +3174,7 @@ static ssize_t wm8962_beep_set(struct device *dev,
long int time;
int ret;
- ret = strict_strtol(buf, 10, &time);
+ ret = kstrtol(buf, 10, &time);
if (ret != 0)
return ret;
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index ba832b77c543..86426a117b07 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -819,8 +819,9 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
* don't want false reports.
*/
if (wm8994->jackdet && !wm8994->clk_has_run) {
- schedule_delayed_work(&wm8994->jackdet_bootstrap,
- msecs_to_jiffies(1000));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->jackdet_bootstrap,
+ msecs_to_jiffies(1000));
wm8994->clk_has_run = true;
}
break;
@@ -1432,14 +1433,12 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
#define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
- snd_soc_get_volsw, wm8994_put_class_w)
+ snd_soc_dapm_get_volsw, wm8994_put_class_w)
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *w = wlist->widgets[0];
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
@@ -3487,7 +3486,8 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
pm_wakeup_event(codec->dev, 300);
- schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->mic_work, msecs_to_jiffies(250));
return IRQ_HANDLED;
}
@@ -3575,8 +3575,9 @@ static void wm8958_mic_id(void *data, u16 status)
/* If nothing present then clear our statuses */
dev_dbg(codec->dev, "Detected open circuit\n");
- schedule_delayed_work(&wm8994->open_circuit_work,
- msecs_to_jiffies(2500));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->open_circuit_work,
+ msecs_to_jiffies(2500));
return;
}
@@ -3690,8 +3691,9 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
WM1811_JACKDET_DB, 0);
delay = control->pdata.micdet_delay;
- schedule_delayed_work(&wm8994->mic_work,
- msecs_to_jiffies(delay));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->mic_work,
+ msecs_to_jiffies(delay));
} else {
dev_dbg(codec->dev, "Jack not detected\n");
@@ -3936,8 +3938,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
id_delay = wm8994->wm8994->pdata.mic_id_delay;
if (wm8994->mic_detecting)
- schedule_delayed_work(&wm8994->mic_complete_work,
- msecs_to_jiffies(id_delay));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->mic_complete_work,
+ msecs_to_jiffies(id_delay));
else
wm8958_button_det(codec, reg);
@@ -4010,9 +4013,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->micdet_irq = control->pdata.micdet_irq;
- pm_runtime_enable(codec->dev);
- pm_runtime_idle(codec->dev);
-
/* By default use idle_bias_off, will override for WM8994 */
codec->dapm.idle_bias_off = 1;
@@ -4385,8 +4385,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
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(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
&wm8994->fll_locked[i]);
@@ -4445,6 +4443,9 @@ static int wm8994_probe(struct platform_device *pdev)
wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994,
wm8994_dai, ARRAY_SIZE(wm8994_dai));
}
@@ -4452,6 +4453,8 @@ static int wm8994_probe(struct platform_device *pdev)
static int wm8994_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 90a65c427541..da2899e6c401 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -549,12 +549,9 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source,
static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *w = wlist->widgets[0];
- struct snd_soc_codec *codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
- codec = w->codec;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
wm8995_update_class_w(codec);
return ret;
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
new file mode 100644
index 000000000000..6ec3de3efa4f
--- /dev/null
+++ b/sound/soc/codecs/wm8997.c
@@ -0,0 +1,1175 @@
+/*
+ * wm8997.c -- WM8997 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Charles Keepax <ckeepax@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/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm8997.h"
+
+struct wm8997_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+static const struct reg_default wm8997_sysclk_reva_patch[] = {
+ { 0x301D, 0x7B15 },
+ { 0x301B, 0x0050 },
+ { 0x305D, 0x7B17 },
+ { 0x305B, 0x0050 },
+ { 0x3001, 0x08FE },
+ { 0x3003, 0x00F4 },
+ { 0x3041, 0x08FF },
+ { 0x3043, 0x0005 },
+ { 0x3020, 0x0225 },
+ { 0x3021, 0x0A00 },
+ { 0x3022, 0xE24D },
+ { 0x3023, 0x0800 },
+ { 0x3024, 0xE24D },
+ { 0x3025, 0xF000 },
+ { 0x3060, 0x0226 },
+ { 0x3061, 0x0A00 },
+ { 0x3062, 0xE252 },
+ { 0x3063, 0x0800 },
+ { 0x3064, 0xE252 },
+ { 0x3065, 0xF000 },
+ { 0x3116, 0x022B },
+ { 0x3117, 0xFA00 },
+ { 0x3110, 0x246C },
+ { 0x3111, 0x0A03 },
+ { 0x3112, 0x246E },
+ { 0x3113, 0x0A03 },
+ { 0x3114, 0x2470 },
+ { 0x3115, 0x0A03 },
+ { 0x3126, 0x246C },
+ { 0x3127, 0x0A02 },
+ { 0x3128, 0x246E },
+ { 0x3129, 0x0A02 },
+ { 0x312A, 0x2470 },
+ { 0x312B, 0xFA02 },
+ { 0x3125, 0x0800 },
+};
+
+static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct regmap *regmap = codec->control_data;
+ const struct reg_default *patch = NULL;
+ int i, patch_size;
+
+ switch (arizona->rev) {
+ case 0:
+ patch = wm8997_sysclk_reva_patch;
+ patch_size = ARRAY_SIZE(wm8997_sysclk_reva_patch);
+ break;
+ default:
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (patch)
+ for (i = 0; i < patch_size; i++)
+ regmap_write(regmap, patch[i].reg,
+ patch[i].def);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char *wm8997_osr_text[] = {
+ "Low power", "Normal", "High performance",
+};
+
+static const unsigned int wm8997_osr_val[] = {
+ 0x0, 0x3, 0x5,
+};
+
+static const struct soc_enum wm8997_hpout_osr[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+ ARIZONA_OUT1_OSR_SHIFT, 0x7, 3,
+ wm8997_osr_text, wm8997_osr_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUT3_OSR_SHIFT, 0x7, 3,
+ wm8997_osr_text, wm8997_osr_val),
+};
+
+#define WM8997_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static const struct snd_kcontrol_new wm8997_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2_OSR_SHIFT, 1, 0),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+ ARIZONA_EQ1_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+ ARIZONA_EQ2_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+ ARIZONA_EQ3_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+ ARIZONA_EQ4_ENA_MASK),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+ ARIZONA_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+ ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_VALUE_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]),
+SOC_VALUE_ENUM("EPOUT OSR", wm8997_hpout_osr[1]),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM8997_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8997_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8997_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM8997_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8997_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8997_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+static const char *wm8997_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "EPOUT", "SPKOUT", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int wm8997_aec_loopback_values[] = {
+ 0, 1, 4, 6, 8, 9,
+};
+
+static const struct soc_enum wm8997_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(wm8997_aec_loopback_texts),
+ wm8997_aec_loopback_texts,
+ wm8997_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
+ SOC_DAPM_VALUE_ENUM("AEC Loopback", wm8997_aec_loopback);
+
+static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+ 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+ ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8997_aec_loopback_mux),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8997_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT3L", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC Loopback", "EPOUT", "OUT3L" },
+ { "EPOUTN", NULL, "OUT3L" },
+ { "EPOUTP", NULL, "OUT3L" },
+
+ { "AEC Loopback", "SPKOUT", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "AEC Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+};
+
+static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8997_priv *wm8997 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM8997_FLL1:
+ return arizona_set_fll(&wm8997->fll[0], source, Fref, Fout);
+ case WM8997_FLL2:
+ return arizona_set_fll(&wm8997->fll[1], source, Fref, Fout);
+ case WM8997_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm8997->fll[0], source, Fref,
+ Fout);
+ case WM8997_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm8997->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define WM8997_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8997_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 wm8997_dai[] = {
+ {
+ .name = "wm8997-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "wm8997-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "wm8997-slim1",
+ .id = 3,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8997-slim2",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8997-slim3",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm8997_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = priv->core.arizona->regmap;
+
+ ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ if (ret != 0)
+ return ret;
+
+ arizona_init_spk(codec);
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm8997_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM8997_DIG_VU 0x0200
+
+static unsigned int wm8997_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
+ .probe = wm8997_codec_probe,
+ .remove = wm8997_codec_remove,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8997_set_fll,
+
+ .controls = wm8997_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8997_snd_controls),
+ .dapm_widgets = wm8997_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets),
+ .dapm_routes = wm8997_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes),
+};
+
+static int wm8997_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm8997_priv *wm8997;
+ int i;
+
+ wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
+ GFP_KERNEL);
+ if (wm8997 == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm8997);
+
+ wm8997->core.arizona = arizona;
+ wm8997->core.num_inputs = 4;
+
+ for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
+ wm8997->fll[i].vco_mult = 1;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm8997->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm8997->fll[1]);
+
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+ for (i = 0; i < ARRAY_SIZE(wm8997_dai); i++)
+ arizona_init_dai(&wm8997->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm8997_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm8997_digital_vu[i],
+ WM8997_DIG_VU, WM8997_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
+ wm8997_dai, ARRAY_SIZE(wm8997_dai));
+}
+
+static int wm8997_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm8997_codec_driver = {
+ .driver = {
+ .name = "wm8997-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8997_probe,
+ .remove = wm8997_remove,
+};
+
+module_platform_driver(wm8997_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8997 driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8997-codec");
diff --git a/sound/soc/codecs/wm8997.h b/sound/soc/codecs/wm8997.h
new file mode 100644
index 000000000000..5e91c6a7d567
--- /dev/null
+++ b/sound/soc/codecs/wm8997.h
@@ -0,0 +1,23 @@
+/*
+ * wm8997.h -- WM8997 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8997_H
+#define _WM8997_H
+
+#include "arizona.h"
+
+#define WM8997_FLL1 1
+#define WM8997_FLL2 2
+#define WM8997_FLL1_REFCLK 3
+#define WM8997_FLL2_REFCLK 4
+
+#endif
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 05252ac936a3..b38f3506418f 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -225,15 +225,8 @@ struct wm_coeff_ctl_ops {
struct snd_ctl_elem_info *uinfo);
};
-struct wm_coeff {
- struct device *dev;
- struct list_head ctl_list;
- struct regmap *regmap;
-};
-
struct wm_coeff_ctl {
const char *name;
- struct snd_card *card;
struct wm_adsp_alg_region region;
struct wm_coeff_ctl_ops ops;
struct wm_adsp *adsp;
@@ -378,7 +371,6 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol,
static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
const void *buf, size_t len)
{
- struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
struct wm_adsp_alg_region *region = &ctl->region;
const struct wm_adsp_region *mem;
@@ -401,7 +393,7 @@ static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
if (!scratch)
return -ENOMEM;
- ret = regmap_raw_write(wm_coeff->regmap, reg, scratch,
+ ret = regmap_raw_write(adsp->regmap, reg, scratch,
ctl->len);
if (ret) {
adsp_err(adsp, "Failed to write %zu bytes to %x\n",
@@ -434,7 +426,6 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol,
static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
void *buf, size_t len)
{
- struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
struct wm_adsp_alg_region *region = &ctl->region;
const struct wm_adsp_region *mem;
@@ -457,7 +448,7 @@ static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
if (!scratch)
return -ENOMEM;
- ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len);
+ ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);
if (ret) {
adsp_err(adsp, "Failed to read %zu bytes from %x\n",
ctl->len, reg);
@@ -481,37 +472,18 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff,
- struct wm_coeff_ctl *ctl,
- const struct snd_kcontrol_new *kctl)
-{
- int ret;
- struct snd_kcontrol *kcontrol;
-
- kcontrol = snd_ctl_new1(kctl, wm_coeff);
- ret = snd_ctl_add(ctl->card, kcontrol);
- if (ret < 0) {
- dev_err(wm_coeff->dev, "Failed to add %s: %d\n",
- kctl->name, ret);
- return ret;
- }
- ctl->kcontrol = kcontrol;
- return 0;
-}
-
struct wmfw_ctl_work {
- struct wm_coeff *wm_coeff;
+ struct wm_adsp *adsp;
struct wm_coeff_ctl *ctl;
struct work_struct work;
};
-static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
- struct wm_coeff_ctl *ctl)
+static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
{
struct snd_kcontrol_new *kcontrol;
int ret;
- if (!wm_coeff || !ctl || !ctl->name || !ctl->card)
+ if (!ctl || !ctl->name)
return -EINVAL;
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
@@ -525,14 +497,17 @@ static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
kcontrol->put = wm_coeff_put;
kcontrol->private_value = (unsigned long)ctl;
- ret = wm_coeff_add_kcontrol(wm_coeff,
- ctl, kcontrol);
+ ret = snd_soc_add_card_controls(adsp->card,
+ kcontrol, 1);
if (ret < 0)
goto err_kcontrol;
kfree(kcontrol);
- list_add(&ctl->list, &wm_coeff->ctl_list);
+ ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card,
+ ctl->name);
+
+ list_add(&ctl->list, &adsp->ctl_list);
return 0;
err_kcontrol:
@@ -753,13 +728,12 @@ out:
return ret;
}
-static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
+static int wm_coeff_init_control_caches(struct wm_adsp *adsp)
{
struct wm_coeff_ctl *ctl;
int ret;
- list_for_each_entry(ctl, &wm_coeff->ctl_list,
- list) {
+ list_for_each_entry(ctl, &adsp->ctl_list, list) {
if (!ctl->enabled || ctl->set)
continue;
ret = wm_coeff_read_control(ctl->kcontrol,
@@ -772,13 +746,12 @@ static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
return 0;
}
-static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff)
+static int wm_coeff_sync_controls(struct wm_adsp *adsp)
{
struct wm_coeff_ctl *ctl;
int ret;
- list_for_each_entry(ctl, &wm_coeff->ctl_list,
- list) {
+ list_for_each_entry(ctl, &adsp->ctl_list, list) {
if (!ctl->enabled)
continue;
if (ctl->set) {
@@ -799,15 +772,14 @@ static void wm_adsp_ctl_work(struct work_struct *work)
struct wmfw_ctl_work,
work);
- wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl);
+ wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
kfree(ctl_work);
}
-static int wm_adsp_create_control(struct snd_soc_codec *codec,
+static int wm_adsp_create_control(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *region)
{
- struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
struct wm_coeff_ctl *ctl;
struct wmfw_ctl_work *ctl_work;
char *name;
@@ -842,7 +814,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec,
snprintf(name, PAGE_SIZE, "DSP%d %s %x",
dsp->num, region_name, region->alg);
- list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+ list_for_each_entry(ctl, &dsp->ctl_list,
list) {
if (!strcmp(ctl->name, name)) {
if (!ctl->enabled)
@@ -866,7 +838,6 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec,
ctl->set = 0;
ctl->ops.xget = wm_coeff_get;
ctl->ops.xput = wm_coeff_put;
- ctl->card = codec->card->snd_card;
ctl->adsp = dsp;
ctl->len = region->len;
@@ -882,7 +853,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec,
goto err_ctl_cache;
}
- ctl_work->wm_coeff = dsp->wm_coeff;
+ ctl_work->adsp = dsp;
ctl_work->ctl = ctl;
INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
schedule_work(&ctl_work->work);
@@ -903,7 +874,7 @@ err_name:
return ret;
}
-static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
+static int wm_adsp_setup_algs(struct wm_adsp *dsp)
{
struct regmap *regmap = dsp->regmap;
struct wmfw_adsp1_id_hdr adsp1_id;
@@ -1091,7 +1062,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
region->len -= be32_to_cpu(adsp1_alg[i].dm);
- wm_adsp_create_control(codec, region);
+ wm_adsp_create_control(dsp, region);
} else {
adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1108,7 +1079,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
region->len -= be32_to_cpu(adsp1_alg[i].zm);
- wm_adsp_create_control(codec, region);
+ wm_adsp_create_control(dsp, region);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1137,7 +1108,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
region->len -= be32_to_cpu(adsp2_alg[i].xm);
- wm_adsp_create_control(codec, region);
+ wm_adsp_create_control(dsp, region);
} else {
adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1154,7 +1125,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
region->len -= be32_to_cpu(adsp2_alg[i].ym);
- wm_adsp_create_control(codec, region);
+ wm_adsp_create_control(dsp, region);
} else {
adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1171,7 +1142,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
region->len -= be32_to_cpu(adsp2_alg[i].zm);
- wm_adsp_create_control(codec, region);
+ wm_adsp_create_control(dsp, region);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1391,6 +1362,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
int ret;
int val;
+ dsp->card = codec->card;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
@@ -1425,7 +1398,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp, codec);
+ ret = wm_adsp_setup_algs(dsp);
if (ret != 0)
goto err;
@@ -1434,12 +1407,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
goto err;
/* Initialize caches for enabled and unset controls */
- ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+ ret = wm_coeff_init_control_caches(dsp);
if (ret != 0)
goto err;
/* Sync set controls */
- ret = wm_coeff_sync_controls(dsp->wm_coeff);
+ ret = wm_coeff_sync_controls(dsp);
if (ret != 0)
goto err;
@@ -1460,10 +1433,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_SYS_ENA, 0);
- list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
- list) {
+ list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
- }
break;
default:
@@ -1520,6 +1491,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
unsigned int val;
int ret;
+ dsp->card = codec->card;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/*
@@ -1582,7 +1555,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp, codec);
+ ret = wm_adsp_setup_algs(dsp);
if (ret != 0)
goto err;
@@ -1591,12 +1564,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
goto err;
/* Initialize caches for enabled and unset controls */
- ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+ ret = wm_coeff_init_control_caches(dsp);
if (ret != 0)
goto err;
/* Sync set controls */
- ret = wm_coeff_sync_controls(dsp->wm_coeff);
+ ret = wm_coeff_sync_controls(dsp);
if (ret != 0)
goto err;
@@ -1637,10 +1610,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ret);
}
- list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
- list) {
+ list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
- }
while (!list_empty(&dsp->alg_regions)) {
alg_region = list_first_entry(&dsp->alg_regions,
@@ -1679,49 +1650,38 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
}
INIT_LIST_HEAD(&adsp->alg_regions);
-
- adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff),
- GFP_KERNEL);
- if (!adsp->wm_coeff)
- return -ENOMEM;
- adsp->wm_coeff->regmap = adsp->regmap;
- adsp->wm_coeff->dev = adsp->dev;
- INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list);
+ INIT_LIST_HEAD(&adsp->ctl_list);
if (dvfs) {
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
if (IS_ERR(adsp->dvfs)) {
ret = PTR_ERR(adsp->dvfs);
dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
- goto out_coeff;
+ return ret;
}
ret = regulator_enable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
ret);
- goto out_coeff;
+ return ret;
}
ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
if (ret != 0) {
dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
ret);
- goto out_coeff;
+ return ret;
}
ret = regulator_disable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
ret);
- goto out_coeff;
+ return ret;
}
}
return 0;
-
-out_coeff:
- kfree(adsp->wm_coeff);
- return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 9f922c82536c..d018dea6254d 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -39,6 +39,7 @@ struct wm_adsp {
int type;
struct device *dev;
struct regmap *regmap;
+ struct snd_soc_card *card;
int base;
int sysclk_reg;
@@ -57,7 +58,7 @@ struct wm_adsp {
struct regulator *dvfs;
- struct wm_coeff *wm_coeff;
+ struct list_head ctl_list;
};
#define WM_ADSP1(wname, num) \
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 2d9e099415a5..8b50e5958de5 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -699,9 +699,7 @@ EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
@@ -721,9 +719,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
static int class_w_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index 70eb37a5dd16..25c31f1655f6 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -421,13 +421,11 @@ static int dw_i2s_probe(struct platform_device *pdev)
dw_i2s_dai, 1);
if (ret != 0) {
dev_err(&pdev->dev, "not able to register dai\n");
- goto err_set_drvdata;
+ goto err_clk_disable;
}
return 0;
-err_set_drvdata:
- dev_set_drvdata(&pdev->dev, NULL);
err_clk_disable:
clk_disable(dev->clk);
err_clk_put:
@@ -440,7 +438,6 @@ static int dw_i2s_remove(struct platform_device *pdev)
struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
- dev_set_drvdata(&pdev->dev, NULL);
clk_put(dev->clk);
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index aa438546c912..704e246f5b1e 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,6 +1,9 @@
config SND_SOC_FSL_SSI
tristate
+config SND_SOC_FSL_SPDIF
+ tristate
+
config SND_SOC_FSL_UTILS
tristate
@@ -98,7 +101,7 @@ endif # SND_POWERPC_SOC
menuconfig SND_IMX_SOC
tristate "SoC Audio for Freescale i.MX CPUs"
- depends on ARCH_MXC
+ depends on ARCH_MXC || COMPILE_TEST
help
Say Y or M if you want to add support for codecs attached to
the i.MX CPUs.
@@ -109,11 +112,11 @@ config SND_SOC_IMX_SSI
tristate
config SND_SOC_IMX_PCM_FIQ
- bool
+ tristate
select FIQ
config SND_SOC_IMX_PCM_DMA
- bool
+ tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
config SND_SOC_IMX_AUDMUX
@@ -175,7 +178,6 @@ config SND_SOC_IMX_WM8962
select SND_SOC_IMX_PCM_DMA
select SND_SOC_IMX_AUDMUX
select SND_SOC_FSL_SSI
- select SND_SOC_FSL_UTILS
help
Say Y if you want to add support for SoC audio on an i.MX board with
a wm8962 codec.
@@ -187,14 +189,23 @@ config SND_SOC_IMX_SGTL5000
select SND_SOC_IMX_PCM_DMA
select SND_SOC_IMX_AUDMUX
select SND_SOC_FSL_SSI
- select SND_SOC_FSL_UTILS
help
Say Y if you want to add support for SoC audio on an i.MX board with
a sgtl5000 codec.
+config SND_SOC_IMX_SPDIF
+ tristate "SoC Audio support for i.MX boards with S/PDIF"
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_FSL_SPDIF
+ select SND_SOC_SPDIF
+ help
+ SoC Audio support for i.MX boards with S/PDIF
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a S/DPDIF.
+
config SND_SOC_IMX_MC13783
tristate "SoC Audio support for I.MX boards with mc13783"
- depends on MFD_MC13783
+ depends on MFD_MC13783 && ARM
select SND_SOC_IMX_SSI
select SND_SOC_IMX_AUDMUX
select SND_SOC_MC13783
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index d4b4aa8b5649..8db705b0fdf9 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale PowerPC SSI/DMA Platform Support
snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
@@ -43,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-wm8962-objs := imx-wm8962.o
+snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@@ -51,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
+obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
new file mode 100644
index 000000000000..3920c3e849ce
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -0,0 +1,1225 @@
+/*
+ * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Based on stmp3xxx_spdif_dai.c
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/bitrev.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <sound/asoundef.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "fsl_spdif.h"
+#include "imx-pcm.h"
+
+#define FSL_SPDIF_TXFIFO_WML 0x8
+#define FSL_SPDIF_RXFIFO_WML 0x8
+
+#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
+#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\
+ INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\
+ INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED)
+
+/* Index list for the values that has if (DPLL Locked) condition */
+static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
+#define SRPC_NODPLL_START1 0x5
+#define SRPC_NODPLL_START2 0xc
+
+#define DEFAULT_RXCLK_SRC 1
+
+/*
+ * SPDIF control structure
+ * Defines channel status, subcode and Q sub
+ */
+struct spdif_mixer_control {
+ /* spinlock to access control data */
+ spinlock_t ctl_lock;
+
+ /* IEC958 channel tx status bit */
+ unsigned char ch_status[4];
+
+ /* User bits */
+ unsigned char subcode[2 * SPDIF_UBITS_SIZE];
+
+ /* Q subcode part of user bits */
+ unsigned char qsub[2 * SPDIF_QSUB_SIZE];
+
+ /* Buffer offset for U/Q */
+ u32 upos;
+ u32 qpos;
+
+ /* Ready buffer index of the two buffers */
+ u32 ready_buf;
+};
+
+struct fsl_spdif_priv {
+ struct spdif_mixer_control fsl_spdif_control;
+ struct snd_soc_dai_driver cpu_dai_drv;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ bool dpll_locked;
+ u8 txclk_div[SPDIF_TXRATE_MAX];
+ u8 txclk_src[SPDIF_TXRATE_MAX];
+ u8 rxclk_src;
+ struct clk *txclk[SPDIF_TXRATE_MAX];
+ struct clk *rxclk;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+
+ /* The name space will be allocated dynamically */
+ char name[0];
+};
+
+
+/* DPLL locked and lock loss interrupt handler */
+static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 locked;
+
+ regmap_read(regmap, REG_SPDIF_SRPC, &locked);
+ locked &= SRPC_DPLL_LOCKED;
+
+ dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
+ locked ? "locked" : "loss lock");
+
+ spdif_priv->dpll_locked = locked ? true : false;
+}
+
+/* Receiver found illegal symbol interrupt handler */
+static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+
+ dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
+
+ if (!spdif_priv->dpll_locked) {
+ /* DPLL unlocked seems no audio stream */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
+ }
+}
+
+/* U/Q Channel receive register full */
+static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 *pos, size, val, reg;
+
+ switch (name) {
+ case 'U':
+ pos = &ctrl->upos;
+ size = SPDIF_UBITS_SIZE;
+ reg = REG_SPDIF_SRU;
+ break;
+ case 'Q':
+ pos = &ctrl->qpos;
+ size = SPDIF_QSUB_SIZE;
+ reg = REG_SPDIF_SRQ;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported channel name\n");
+ return;
+ }
+
+ dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
+
+ if (*pos >= size * 2) {
+ *pos = 0;
+ } else if (unlikely((*pos % size) + 3 > size)) {
+ dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+ return;
+ }
+
+ regmap_read(regmap, reg, &val);
+ ctrl->subcode[*pos++] = val >> 16;
+ ctrl->subcode[*pos++] = val >> 8;
+ ctrl->subcode[*pos++] = val;
+}
+
+/* U/Q Channel sync found */
+static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct platform_device *pdev = spdif_priv->pdev;
+
+ dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
+
+ /* U/Q buffer reset */
+ if (ctrl->qpos == 0)
+ return;
+
+ /* Set ready to this buffer */
+ ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1;
+}
+
+/* U/Q Channel framing error */
+static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 val;
+
+ dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n");
+
+ /* Read U/Q data to clear the irq and do buffer reset */
+ regmap_read(regmap, REG_SPDIF_SRU, &val);
+ regmap_read(regmap, REG_SPDIF_SRQ, &val);
+
+ /* Drop this U/Q buffer */
+ ctrl->ready_buf = 0;
+ ctrl->upos = 0;
+ ctrl->qpos = 0;
+}
+
+/* Get spdif interrupt status and clear the interrupt */
+static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val, val2;
+
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
+ regmap_read(regmap, REG_SPDIF_SIE, &val2);
+
+ regmap_write(regmap, REG_SPDIF_SIC, val & val2);
+
+ return val;
+}
+
+static irqreturn_t spdif_isr(int irq, void *devid)
+{
+ struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 sis;
+
+ sis = spdif_intr_status_clear(spdif_priv);
+
+ if (sis & INT_DPLL_LOCKED)
+ spdif_irq_dpll_lock(spdif_priv);
+
+ if (sis & INT_TXFIFO_UNOV)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n");
+
+ if (sis & INT_TXFIFO_RESYNC)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n");
+
+ if (sis & INT_CNEW)
+ dev_dbg(&pdev->dev, "isr: cstatus new\n");
+
+ if (sis & INT_VAL_NOGOOD)
+ dev_dbg(&pdev->dev, "isr: validity flag no good\n");
+
+ if (sis & INT_SYM_ERR)
+ spdif_irq_sym_error(spdif_priv);
+
+ if (sis & INT_BIT_ERR)
+ dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n");
+
+ if (sis & INT_URX_FUL)
+ spdif_irq_uqrx_full(spdif_priv, 'U');
+
+ if (sis & INT_URX_OV)
+ dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n");
+
+ if (sis & INT_QRX_FUL)
+ spdif_irq_uqrx_full(spdif_priv, 'Q');
+
+ if (sis & INT_QRX_OV)
+ dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n");
+
+ if (sis & INT_UQ_SYNC)
+ spdif_irq_uq_sync(spdif_priv);
+
+ if (sis & INT_UQ_ERR)
+ spdif_irq_uq_err(spdif_priv);
+
+ if (sis & INT_RXFIFO_UNOV)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n");
+
+ if (sis & INT_RXFIFO_RESYNC)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n");
+
+ if (sis & INT_LOSS_LOCK)
+ spdif_irq_dpll_lock(spdif_priv);
+
+ /* FIXME: Write Tx FIFO to clear TxEm */
+ if (sis & INT_TX_EM)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n");
+
+ /* FIXME: Read Rx FIFO to clear RxFIFOFul */
+ if (sis & INT_RXFIFO_FUL)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO full\n");
+
+ return IRQ_HANDLED;
+}
+
+static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val, cycle = 1000;
+
+ regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
+
+ /*
+ * RESET bit would be cleared after finishing its reset procedure,
+ * which typically lasts 8 cycles. 1000 cycles will keep it safe.
+ */
+ do {
+ regmap_read(regmap, REG_SPDIF_SCR, &val);
+ } while ((val & SCR_SOFT_RESET) && cycle--);
+
+ if (cycle)
+ return 0;
+ else
+ return -EBUSY;
+}
+
+static void spdif_set_cstatus(struct spdif_mixer_control *ctrl,
+ u8 mask, u8 cstatus)
+{
+ ctrl->ch_status[3] &= ~mask;
+ ctrl->ch_status[3] |= cstatus & mask;
+}
+
+static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 ch_status;
+
+ ch_status = (bitrev8(ctrl->ch_status[0]) << 16) |
+ (bitrev8(ctrl->ch_status[1]) << 8) |
+ bitrev8(ctrl->ch_status[2]);
+ regmap_write(regmap, REG_SPDIF_STCSCH, ch_status);
+
+ dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status);
+
+ ch_status = bitrev8(ctrl->ch_status[3]) << 16;
+ regmap_write(regmap, REG_SPDIF_STCSCL, ch_status);
+
+ dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status);
+}
+
+/* Set SPDIF PhaseConfig register for rx clock */
+static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_gainsel gainsel, int dpll_locked)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u8 clksrc = spdif_priv->rxclk_src;
+
+ if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX)
+ return -EINVAL;
+
+ regmap_update_bits(regmap, REG_SPDIF_SRPC,
+ SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+ SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel));
+
+ return 0;
+}
+
+static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
+ int sample_rate)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ unsigned long csfs = 0;
+ u32 stc, mask, rate;
+ u8 clk, div;
+ int ret;
+
+ switch (sample_rate) {
+ case 32000:
+ rate = SPDIF_TXRATE_32000;
+ csfs = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ rate = SPDIF_TXRATE_44100;
+ csfs = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ rate = SPDIF_TXRATE_48000;
+ csfs = IEC958_AES3_CON_FS_48000;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ clk = spdif_priv->txclk_src[rate];
+ if (clk >= STC_TXCLK_SRC_MAX) {
+ dev_err(&pdev->dev, "tx clock source is out of range\n");
+ return -EINVAL;
+ }
+
+ div = spdif_priv->txclk_div[rate];
+ if (div == 0) {
+ dev_err(&pdev->dev, "the divisor can't be zero\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block
+ * will divide by (div). So request 64 * fs * (div+1) which will
+ * get rounded.
+ */
+ ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set tx clock rate\n");
+ return ret;
+ }
+
+ dev_dbg(&pdev->dev, "expected clock rate = %d\n",
+ (64 * sample_rate * div));
+ dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
+ clk_get_rate(spdif_priv->txclk[rate]));
+
+ /* set fs field in consumer channel status */
+ spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
+
+ /* select clock source and divisor */
+ stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
+ mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+ regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+
+ dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+
+ return 0;
+}
+
+static int fsl_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 scr, mask, i;
+ int ret;
+
+ /* Reset module and interrupts only for first initialization */
+ if (!cpu_dai->active) {
+ ret = spdif_softreset(spdif_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to soft reset\n");
+ return ret;
+ }
+
+ /* Disable all the interrupts */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL |
+ SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP |
+ SCR_TXFIFO_FSEL_IF8;
+ mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+ SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+ SCR_TXFIFO_FSEL_MASK;
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_prepare_enable(spdif_priv->txclk[i]);
+ } else {
+ scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+ clk_prepare_enable(spdif_priv->rxclk);
+ }
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Power up SPDIF module */
+ regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
+
+ return 0;
+}
+
+static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 scr, mask, i;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ scr = 0;
+ mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+ SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+ SCR_TXFIFO_FSEL_MASK;
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_disable_unprepare(spdif_priv->txclk[i]);
+ } else {
+ scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+ clk_disable_unprepare(spdif_priv->rxclk);
+ }
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Power down SPDIF module only if tx&rx are both inactive */
+ if (!cpu_dai->active) {
+ spdif_intr_status_clear(spdif_priv);
+ regmap_update_bits(regmap, REG_SPDIF_SCR,
+ SCR_LOW_POWER, SCR_LOW_POWER);
+ }
+}
+
+static int fsl_spdif_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 fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 sample_rate = params_rate(params);
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = spdif_set_sample_rate(substream, sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
+ __func__, sample_rate);
+ return ret;
+ }
+ spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
+ IEC958_AES3_CON_CLOCK_1000PPM);
+ spdif_write_channel_status(spdif_priv);
+ } else {
+ /* Setup rx clock source */
+ ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
+ }
+
+ return ret;
+}
+
+static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
+ u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr);
+ regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
+ regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops fsl_spdif_dai_ops = {
+ .startup = fsl_spdif_startup,
+ .hw_params = fsl_spdif_hw_params,
+ .trigger = fsl_spdif_trigger,
+ .shutdown = fsl_spdif_shutdown,
+};
+
+
+/*
+ * FSL SPDIF IEC958 controller(mixer) functions
+ *
+ * Channel status get/put control
+ * User bit value get/put control
+ * Valid bit value get control
+ * DPLL lock status get control
+ * User bit sync mode selection control
+ */
+
+static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+ uvalue->value.iec958.status[0] = ctrl->ch_status[0];
+ uvalue->value.iec958.status[1] = ctrl->ch_status[1];
+ uvalue->value.iec958.status[2] = ctrl->ch_status[2];
+ uvalue->value.iec958.status[3] = ctrl->ch_status[3];
+
+ return 0;
+}
+
+static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+ ctrl->ch_status[0] = uvalue->value.iec958.status[0];
+ ctrl->ch_status[1] = uvalue->value.iec958.status[1];
+ ctrl->ch_status[2] = uvalue->value.iec958.status[2];
+ ctrl->ch_status[3] = uvalue->value.iec958.status[3];
+
+ spdif_write_channel_status(spdif_priv);
+
+ return 0;
+}
+
+/* Get channel status from SPDIF_RX_CCHAN register */
+static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 cstatus, val;
+
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
+ if (!(val & INT_CNEW)) {
+ return -EAGAIN;
+ }
+
+ regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus);
+ ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[2] = cstatus & 0xFF;
+
+ regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus);
+ ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[5] = cstatus & 0xFF;
+
+ /* Clear intr */
+ regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW);
+
+ return 0;
+}
+
+/*
+ * Get User bits (subcode) from chip value which readed out
+ * in UChannel register.
+ */
+static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ctrl->ctl_lock, flags);
+ if (ctrl->ready_buf) {
+ int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
+ memcpy(&ucontrol->value.iec958.subcode[0],
+ &ctrl->subcode[idx], SPDIF_UBITS_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+ return ret;
+}
+
+/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = SPDIF_QSUB_SIZE;
+
+ return 0;
+}
+
+/* Get Q subcode from chip value which readed out in QChannel register */
+static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ctrl->ctl_lock, flags);
+ if (ctrl->ready_buf) {
+ int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
+ memcpy(&ucontrol->value.bytes.data[0],
+ &ctrl->qsub[idx], SPDIF_QSUB_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+ return ret;
+}
+
+/* Valid bit infomation */
+static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+/* Get valid good bit from interrupt status register */
+static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ val = regmap_read(regmap, REG_SPDIF_SIS, &val);
+ ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
+ regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
+
+ return 0;
+}
+
+/* DPLL lock infomation */
+static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 16000;
+ uinfo->value.integer.max = 96000;
+
+ return 0;
+}
+
+static u32 gainsel_multi[GAINSEL_MULTI_MAX] = {
+ 24, 16, 12, 8, 6, 4, 3,
+};
+
+/* Get RX data clock rate given the SPDIF bus_clk */
+static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_gainsel gainsel)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u64 tmpval64, busclk_freq = 0;
+ u32 freqmeas, phaseconf;
+ u8 clksrc;
+
+ regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
+ regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
+
+ clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
+ if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
+ /* Get bus clock from system */
+ busclk_freq = clk_get_rate(spdif_priv->rxclk);
+ }
+
+ /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
+ tmpval64 = (u64) busclk_freq * freqmeas;
+ do_div(tmpval64, gainsel_multi[gainsel] * 1024);
+ do_div(tmpval64, 128 * 1024);
+
+ dev_dbg(&pdev->dev, "FreqMeas: %d\n", freqmeas);
+ dev_dbg(&pdev->dev, "BusclkFreq: %lld\n", busclk_freq);
+ dev_dbg(&pdev->dev, "RxRate: %lld\n", tmpval64);
+
+ return (int)tmpval64;
+}
+
+/*
+ * Get DPLL lock or not info from stable interrupt status register.
+ * User application must use this control to get locked,
+ * then can do next PCM operation
+ */
+static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+
+ if (spdif_priv->dpll_locked)
+ ucontrol->value.integer.value[0] = rate;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+/* User bit sync mode info */
+static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ regmap_read(regmap, REG_SPDIF_SRCD, &val);
+ ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0;
+
+ return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET;
+
+ regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val);
+
+ return 0;
+}
+
+/* FSL SPDIF IEC958 controller defines */
+static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
+ /* Status cchanel controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_pb_get,
+ .put = fsl_spdif_pb_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_capture_get,
+ },
+ /* User bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_subcode_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_qinfo,
+ .get = fsl_spdif_qget,
+ },
+ /* Valid bit error controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 V-Bit Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_vbit_info,
+ .get = fsl_spdif_vbit_get,
+ },
+ /* DPLL lock info get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "RX Sample Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_rxrate_info,
+ .get = fsl_spdif_rxrate_get,
+ },
+ /* User bit sync mode set/get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 USyncMode CDText",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_usync_info,
+ .get = fsl_spdif_usync_get,
+ .put = fsl_spdif_usync_put,
+ },
+};
+
+static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &spdif_private->dma_params_tx;
+ dai->capture_dma_data = &spdif_private->dma_params_rx;
+
+ snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver fsl_spdif_dai = {
+ .probe = &fsl_spdif_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_SPDIF_RATES_PLAYBACK,
+ .formats = FSL_SPDIF_FORMATS_PLAYBACK,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_SPDIF_RATES_CAPTURE,
+ .formats = FSL_SPDIF_FORMATS_CAPTURE,
+ },
+ .ops = &fsl_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_spdif_component = {
+ .name = "fsl-spdif",
+};
+
+/* FSL SPDIF REGMAP */
+
+static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SCR:
+ case REG_SPDIF_SRCD:
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIE:
+ case REG_SPDIF_SIS:
+ case REG_SPDIF_SRL:
+ case REG_SPDIF_SRR:
+ case REG_SPDIF_SRCSH:
+ case REG_SPDIF_SRCSL:
+ case REG_SPDIF_SRU:
+ case REG_SPDIF_SRQ:
+ case REG_SPDIF_STCSCH:
+ case REG_SPDIF_STCSCL:
+ case REG_SPDIF_SRFM:
+ case REG_SPDIF_STC:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SCR:
+ case REG_SPDIF_SRCD:
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIE:
+ case REG_SPDIF_SIC:
+ case REG_SPDIF_STL:
+ case REG_SPDIF_STR:
+ case REG_SPDIF_STCSCH:
+ case REG_SPDIF_STCSCL:
+ case REG_SPDIF_STC:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config fsl_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_SPDIF_STC,
+ .readable_reg = fsl_spdif_readable_reg,
+ .writeable_reg = fsl_spdif_writeable_reg,
+};
+
+static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
+ struct clk *clk, u64 savesub,
+ enum spdif_txrate index)
+{
+ const u32 rate[] = { 32000, 44100, 48000 };
+ u64 rate_ideal, rate_actual, sub;
+ u32 div, arate;
+
+ for (div = 1; div <= 128; div++) {
+ rate_ideal = rate[index] * (div + 1) * 64;
+ rate_actual = clk_round_rate(clk, rate_ideal);
+
+ arate = rate_actual / 64;
+ arate /= div;
+
+ if (arate == rate[index]) {
+ /* We are lucky */
+ savesub = 0;
+ spdif_priv->txclk_div[index] = div;
+ break;
+ } else if (arate / rate[index] == 1) {
+ /* A little bigger than expect */
+ sub = (arate - rate[index]) * 100000;
+ do_div(sub, rate[index]);
+ if (sub < savesub) {
+ savesub = sub;
+ spdif_priv->txclk_div[index] = div;
+ }
+ } else if (rate[index] / arate == 1) {
+ /* A little smaller than expect */
+ sub = (rate[index] - arate) * 100000;
+ do_div(sub, rate[index]);
+ if (sub < savesub) {
+ savesub = sub;
+ spdif_priv->txclk_div[index] = div;
+ }
+ }
+ }
+
+ return savesub;
+}
+
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_txrate index)
+{
+ const u32 rate[] = { 32000, 44100, 48000 };
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct device *dev = &pdev->dev;
+ u64 savesub = 100000, ret;
+ struct clk *clk;
+ char tmp[16];
+ int i;
+
+ for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+ sprintf(tmp, "rxtx%d", i);
+ clk = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "no rxtx%d clock in devicetree\n", i);
+ return PTR_ERR(clk);
+ }
+ if (!clk_get_rate(clk))
+ continue;
+
+ ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+ if (savesub == ret)
+ continue;
+
+ savesub = ret;
+ spdif_priv->txclk[index] = clk;
+ spdif_priv->txclk_src[index] = i;
+
+ /* To quick catch a divisor, we allow a 0.1% deviation */
+ if (savesub < 100)
+ break;
+ }
+
+ dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
+ spdif_priv->txclk_src[index], rate[index]);
+ dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
+ spdif_priv->txclk_div[index], rate[index]);
+
+ return 0;
+}
+
+static int fsl_spdif_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_spdif_priv *spdif_priv;
+ struct spdif_mixer_control *ctrl;
+ struct resource *res;
+ void __iomem *regs;
+ int irq, ret, i;
+
+ if (!np)
+ return -ENODEV;
+
+ spdif_priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1,
+ GFP_KERNEL);
+ if (!spdif_priv)
+ return -ENOMEM;
+
+ strcpy(spdif_priv->name, np->name);
+
+ spdif_priv->pdev = pdev;
+
+ /* Initialize this copy of the CPU DAI driver structure */
+ memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
+ spdif_priv->cpu_dai_drv.name = spdif_priv->name;
+
+ /* Get the addresses and IRQ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(&pdev->dev, "could not determine device resources\n");
+ return PTR_ERR(res);
+ }
+
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "core", regs, &fsl_spdif_regmap_config);
+ if (IS_ERR(spdif_priv->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(spdif_priv->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
+ spdif_priv->name, spdif_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "could not claim irq %u\n", irq);
+ return ret;
+ }
+
+ /* Select clock source for rx/tx clock */
+ spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
+ if (IS_ERR(spdif_priv->rxclk)) {
+ dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n");
+ return PTR_ERR(spdif_priv->rxclk);
+ }
+ spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+ ret = fsl_spdif_probe_txclk(spdif_priv, i);
+ if (ret)
+ return ret;
+ }
+
+ /* Initial spinlock for control data */
+ ctrl = &spdif_priv->fsl_spdif_control;
+ spin_lock_init(&ctrl->ctl_lock);
+
+ /* Init tx channel status default value */
+ ctrl->ch_status[0] =
+ IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
+ ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
+ ctrl->ch_status[2] = 0x00;
+ ctrl->ch_status[3] =
+ IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+
+ spdif_priv->dpll_locked = false;
+
+ spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
+ spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
+ spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL;
+ spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL;
+
+ /* Register with ASoC */
+ dev_set_drvdata(&pdev->dev, spdif_priv);
+
+ ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
+ &spdif_priv->cpu_dai_drv, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = imx_pcm_dma_init(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
+ goto error_component;
+ }
+
+ return ret;
+
+error_component:
+ snd_soc_unregister_component(&pdev->dev);
+
+ return ret;
+}
+
+static int fsl_spdif_remove(struct platform_device *pdev)
+{
+ imx_pcm_dma_exit(pdev);
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_spdif_dt_ids[] = {
+ { .compatible = "fsl,imx35-spdif", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
+
+static struct platform_driver fsl_spdif_driver = {
+ .driver = {
+ .name = "fsl-spdif-dai",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_spdif_dt_ids,
+ },
+ .probe = fsl_spdif_probe,
+ .remove = fsl_spdif_remove,
+};
+
+module_platform_driver(fsl_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl-spdif-dai");
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
new file mode 100644
index 000000000000..b1266790d117
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -0,0 +1,191 @@
+/*
+ * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <b42378@freescale.com>
+ *
+ * Based on fsl_ssi.h
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_SPDIF_DAI_H
+#define _FSL_SPDIF_DAI_H
+
+/* S/PDIF Register Map */
+#define REG_SPDIF_SCR 0x0 /* SPDIF Configuration Register */
+#define REG_SPDIF_SRCD 0x4 /* CDText Control Register */
+#define REG_SPDIF_SRPC 0x8 /* PhaseConfig Register */
+#define REG_SPDIF_SIE 0xc /* InterruptEn Register */
+#define REG_SPDIF_SIS 0x10 /* InterruptStat Register */
+#define REG_SPDIF_SIC 0x10 /* InterruptClear Register */
+#define REG_SPDIF_SRL 0x14 /* SPDIFRxLeft Register */
+#define REG_SPDIF_SRR 0x18 /* SPDIFRxRight Register */
+#define REG_SPDIF_SRCSH 0x1c /* SPDIFRxCChannel_h Register */
+#define REG_SPDIF_SRCSL 0x20 /* SPDIFRxCChannel_l Register */
+#define REG_SPDIF_SRU 0x24 /* UchannelRx Register */
+#define REG_SPDIF_SRQ 0x28 /* QchannelRx Register */
+#define REG_SPDIF_STL 0x2C /* SPDIFTxLeft Register */
+#define REG_SPDIF_STR 0x30 /* SPDIFTxRight Register */
+#define REG_SPDIF_STCSCH 0x34 /* SPDIFTxCChannelCons_h Register */
+#define REG_SPDIF_STCSCL 0x38 /* SPDIFTxCChannelCons_l Register */
+#define REG_SPDIF_SRFM 0x44 /* FreqMeas Register */
+#define REG_SPDIF_STC 0x50 /* SPDIFTxClk Register */
+
+
+/* SPDIF Configuration register */
+#define SCR_RXFIFO_CTL_OFFSET 23
+#define SCR_RXFIFO_CTL_MASK (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_CTL_ZERO (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_OFF_OFFSET 22
+#define SCR_RXFIFO_OFF_MASK (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_OFF (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_RST_OFFSET 21
+#define SCR_RXFIFO_RST_MASK (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_RST (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_FSEL_OFFSET 19
+#define SCR_RXFIFO_FSEL_MASK (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF0 (0x0 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF4 (0x1 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF8 (0x2 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF12 (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC_OFFSET 18
+#define SCR_RXFIFO_AUTOSYNC_MASK (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC_OFFSET 17
+#define SCR_TXFIFO_AUTOSYNC_MASK (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_FSEL_OFFSET 15
+#define SCR_TXFIFO_FSEL_MASK (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF0 (0x0 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_LOW_POWER (1 << 13)
+#define SCR_SOFT_RESET (1 << 12)
+#define SCR_TXFIFO_CTRL_OFFSET 10
+#define SCR_TXFIFO_CTRL_MASK (0x3 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ZERO (0x0 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_NORMAL (0x1 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ONESAMPLE (0x2 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_DMA_RX_EN_OFFSET 9
+#define SCR_DMA_RX_EN_MASK (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_RX_EN (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_TX_EN_OFFSET 8
+#define SCR_DMA_TX_EN_MASK (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_DMA_TX_EN (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_VAL_OFFSET 5
+#define SCR_VAL_MASK (1 << SCR_VAL_OFFSET)
+#define SCR_VAL_CLEAR (1 << SCR_VAL_OFFSET)
+#define SCR_TXSEL_OFFSET 2
+#define SCR_TXSEL_MASK (0x7 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_OFF (0 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_RX (1 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_NORMAL (0x5 << SCR_TXSEL_OFFSET)
+#define SCR_USRC_SEL_OFFSET 0x0
+#define SCR_USRC_SEL_MASK (0x3 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_NONE (0x0 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_RECV (0x1 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_CHIP (0x3 << SCR_USRC_SEL_OFFSET)
+
+/* SPDIF CDText control */
+#define SRCD_CD_USER_OFFSET 1
+#define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET)
+
+/* SPDIF Phase Configuration register */
+#define SRPC_DPLL_LOCKED (1 << 6)
+#define SRPC_CLKSRC_SEL_OFFSET 7
+#define SRPC_CLKSRC_SEL_MASK (0xf << SRPC_CLKSRC_SEL_OFFSET)
+#define SRPC_CLKSRC_SEL_SET(x) ((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK)
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET1 5
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2 2
+#define SRPC_GAINSEL_OFFSET 3
+#define SRPC_GAINSEL_MASK (0x7 << SRPC_GAINSEL_OFFSET)
+#define SRPC_GAINSEL_SET(x) ((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK)
+
+#define SRPC_CLKSRC_MAX 16
+
+enum spdif_gainsel {
+ GAINSEL_MULTI_24 = 0,
+ GAINSEL_MULTI_16,
+ GAINSEL_MULTI_12,
+ GAINSEL_MULTI_8,
+ GAINSEL_MULTI_6,
+ GAINSEL_MULTI_4,
+ GAINSEL_MULTI_3,
+};
+#define GAINSEL_MULTI_MAX (GAINSEL_MULTI_3 + 1)
+#define SPDIF_DEFAULT_GAINSEL GAINSEL_MULTI_8
+
+/* SPDIF interrupt mask define */
+#define INT_DPLL_LOCKED (1 << 20)
+#define INT_TXFIFO_UNOV (1 << 19)
+#define INT_TXFIFO_RESYNC (1 << 18)
+#define INT_CNEW (1 << 17)
+#define INT_VAL_NOGOOD (1 << 16)
+#define INT_SYM_ERR (1 << 15)
+#define INT_BIT_ERR (1 << 14)
+#define INT_URX_FUL (1 << 10)
+#define INT_URX_OV (1 << 9)
+#define INT_QRX_FUL (1 << 8)
+#define INT_QRX_OV (1 << 7)
+#define INT_UQ_SYNC (1 << 6)
+#define INT_UQ_ERR (1 << 5)
+#define INT_RXFIFO_UNOV (1 << 4)
+#define INT_RXFIFO_RESYNC (1 << 3)
+#define INT_LOSS_LOCK (1 << 2)
+#define INT_TX_EM (1 << 1)
+#define INT_RXFIFO_FUL (1 << 0)
+
+/* SPDIF Clock register */
+#define STC_SYSCLK_DIV_OFFSET 11
+#define STC_SYSCLK_DIV_MASK (0x1ff << STC_TXCLK_SRC_OFFSET)
+#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_TXCLK_SRC_OFFSET 8
+#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET)
+#define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
+#define STC_TXCLK_ALL_EN_OFFSET 7
+#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_DIV_OFFSET 0
+#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET)
+#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+#define STC_TXCLK_SRC_MAX 8
+
+/* SPDIF tx rate */
+enum spdif_txrate {
+ SPDIF_TXRATE_32000 = 0,
+ SPDIF_TXRATE_44100,
+ SPDIF_TXRATE_48000,
+};
+#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_48000 + 1)
+
+
+#define SPDIF_CSTATUS_BYTE 6
+#define SPDIF_UBITS_SIZE 96
+#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8)
+
+
+#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define FSL_SPDIF_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define FSL_SPDIF_FORMATS_CAPTURE (SNDRV_PCM_FMTBIT_S24_LE)
+
+#endif /* _FSL_SPDIF_DAI_H */
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 2f2d837df07f..c6b743978d5e 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -8,6 +8,26 @@
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
+ *
+ *
+ * Some notes why imx-pcm-fiq is used instead of DMA on some boards:
+ *
+ * The i.MX SSI core has some nasty limitations in AC97 mode. While most
+ * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
+ * one FIFO which combines all valid receive slots. We cannot even select
+ * which slots we want to receive. The WM9712 with which this driver
+ * was developed with always sends GPIO status data in slot 12 which
+ * we receive in our (PCM-) data stream. The only chance we have is to
+ * manually skip this data in the FIQ handler. With sampling rates different
+ * from 48000Hz not every frame has valid receive data, so the ratio
+ * between pcm data and GPIO status data changes. Our FIQ handler is not
+ * able to handle this, hence this driver only works with 48000Hz sampling
+ * rate.
+ * Reading and writing AC97 registers is another challenge. The core
+ * provides us status bits when the read register is updated with *another*
+ * value. When we read the same register two times (and the register still
+ * contains the same value) these status bits are not set. We work
+ * around this by not polling these bits but only wait a fixed delay.
*/
#include <linux/init.h>
@@ -36,7 +56,7 @@
#define read_ssi(addr) in_be32(addr)
#define write_ssi(val, addr) out_be32(addr, val)
#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
-#elif defined ARM
+#else
#define read_ssi(addr) readl(addr)
#define write_ssi(val, addr) writel(val, addr)
/*
@@ -121,11 +141,14 @@ struct fsl_ssi_private {
bool new_binding;
bool ssi_on_imx;
+ bool imx_ac97;
+ bool use_dma;
struct clk *clk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct imx_dma_data filter_data_tx;
struct imx_dma_data filter_data_rx;
+ struct imx_pcm_fiq_params fiq_params;
struct {
unsigned int rfrc;
@@ -298,6 +321,102 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
return ret;
}
+static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
+{
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ u8 i2s_mode;
+ u8 wm;
+ int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
+
+ if (ssi_private->imx_ac97)
+ i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
+ else
+ i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+
+ /*
+ * Section 16.5 of the MPC8610 reference manual says that the SSI needs
+ * to be disabled before updating the registers we set here.
+ */
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
+
+ /*
+ * Program the SSI into I2S Slave Non-Network Synchronous mode. Also
+ * enable the transmit and receive FIFO.
+ *
+ * FIXME: Little-endian samples require a different shift dir
+ */
+ write_ssi_mask(&ssi->scr,
+ CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
+ CCSR_SSI_SCR_TFR_CLK_DIS |
+ i2s_mode |
+ (synchronous ? CCSR_SSI_SCR_SYN : 0));
+
+ write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
+ CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
+ CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+
+ write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
+ CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
+ CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
+ /*
+ * The DC and PM bits are only used if the SSI is the clock master.
+ */
+
+ /*
+ * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
+ * use FIFO 1. We program the transmit water to signal a DMA transfer
+ * if there are only two (or fewer) elements left in the FIFO. Two
+ * elements equals one frame (left channel, right channel). This value,
+ * however, depends on the depth of the transmit buffer.
+ *
+ * We set the watermark on the same level as the DMA burstsize. For
+ * fiq it is probably better to use the biggest possible watermark
+ * size.
+ */
+ if (ssi_private->use_dma)
+ wm = ssi_private->fifo_depth - 2;
+ else
+ wm = ssi_private->fifo_depth;
+
+ write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
+ CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
+ &ssi->sfcsr);
+
+ /*
+ * For ac97 interrupts are enabled with the startup of the substream
+ * because it is also running without an active substream. Normally SSI
+ * is only enabled when there is a substream.
+ */
+ if (ssi_private->imx_ac97) {
+ /*
+ * Setup the clock control register
+ */
+ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+ &ssi->stccr);
+ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+ &ssi->srccr);
+
+ /*
+ * Enable AC97 mode and startup the SSI
+ */
+ write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
+ &ssi->sacnt);
+ write_ssi(0xff, &ssi->saccdis);
+ write_ssi(0x300, &ssi->saccen);
+
+ /*
+ * Enable SSI, Transmit and Receive
+ */
+ write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN |
+ CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
+
+ write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
+ }
+
+ return 0;
+}
+
+
/**
* fsl_ssi_startup: create a new substream
*
@@ -319,70 +438,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
* and initialize the SSI registers.
*/
if (!ssi_private->first_stream) {
- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-
ssi_private->first_stream = substream;
/*
- * Section 16.5 of the MPC8610 reference manual says that the
- * SSI needs to be disabled before updating the registers we set
- * here.
- */
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
-
- /*
- * Program the SSI into I2S Slave Non-Network Synchronous mode.
- * Also enable the transmit and receive FIFO.
- *
- * FIXME: Little-endian samples require a different shift dir
- */
- write_ssi_mask(&ssi->scr,
- CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
- CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
- | (synchronous ? CCSR_SSI_SCR_SYN : 0));
-
- write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
- CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
- CCSR_SSI_STCR_TSCKP, &ssi->stcr);
-
- write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
- CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
- CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
-
- /*
- * The DC and PM bits are only used if the SSI is the clock
- * master.
- */
-
- /* Enable the interrupts and DMA requests */
- write_ssi(SIER_FLAGS, &ssi->sier);
-
- /*
- * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
- * don't use FIFO 1. We program the transmit water to signal a
- * DMA transfer if there are only two (or fewer) elements left
- * in the FIFO. Two elements equals one frame (left channel,
- * right channel). This value, however, depends on the depth of
- * the transmit buffer.
- *
- * We program the receive FIFO to notify us if at least two
- * elements (one frame) have been written to the FIFO. We could
- * make this value larger (and maybe we should), but this way
- * data will be written to memory as soon as it's available.
- */
- write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
- CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
- &ssi->sfcsr);
-
- /*
- * We keep the SSI disabled because if we enable it, then the
- * DMA controller will start. It's not supposed to start until
- * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The
- * DMA controller will transfer one "BWC" of data (i.e. the
- * amount of data that the MR.BWC bits are set to). The reason
- * this is bad is because at this point, the PCM driver has not
- * finished initializing the DMA controller.
+ * fsl_ssi_setup was already called by ac97_init earlier if
+ * the driver is in ac97 mode.
*/
+ if (!ssi_private->imx_ac97)
+ fsl_ssi_setup(ssi_private);
} else {
if (synchronous) {
struct snd_pcm_runtime *first_runtime =
@@ -492,6 +555,27 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ unsigned int sier_bits;
+
+ /*
+ * Enable only the interrupts and DMA requests
+ * that are needed for the channel. As the fiq
+ * is polling for this bits, we have to ensure
+ * that this are aligned with the preallocated
+ * buffers
+ */
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (ssi_private->use_dma)
+ sier_bits = SIER_FLAGS;
+ else
+ sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN;
+ } else {
+ if (ssi_private->use_dma)
+ sier_bits = SIER_FLAGS;
+ else
+ sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN;
+ }
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -510,12 +594,18 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
else
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
+
+ if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
+ (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0)
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
break;
default:
return -EINVAL;
}
+ write_ssi(sier_bits, &ssi->sier);
+
return 0;
}
@@ -534,22 +624,13 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
ssi_private->first_stream = ssi_private->second_stream;
ssi_private->second_stream = NULL;
-
- /*
- * If this is the last active substream, disable the SSI.
- */
- if (!ssi_private->first_stream) {
- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
- }
}
static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
{
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
- if (ssi_private->ssi_on_imx) {
+ if (ssi_private->ssi_on_imx && ssi_private->use_dma) {
dai->playback_dma_data = &ssi_private->dma_params_tx;
dai->capture_dma_data = &ssi_private->dma_params_rx;
}
@@ -587,6 +668,133 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
.name = "fsl-ssi",
};
+/**
+ * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the
+ * transfer of data.
+ */
+static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(
+ rtd->cpu_dai);
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE |
+ CCSR_SSI_SIER_TFE0_EN);
+ else
+ write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE |
+ CCSR_SSI_SIER_RFF0_EN);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE |
+ CCSR_SSI_SIER_TFE0_EN, 0);
+ else
+ write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE |
+ CCSR_SSI_SIER_RFF0_EN, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
+ else
+ write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = {
+ .startup = fsl_ssi_startup,
+ .shutdown = fsl_ssi_shutdown,
+ .trigger = fsl_ssi_ac97_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
+ .ac97_control = 1,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &fsl_ssi_ac97_dai_ops,
+};
+
+
+static struct fsl_ssi_private *fsl_ac97_data;
+
+static void fsl_ssi_ac97_init(void)
+{
+ fsl_ssi_setup(fsl_ac97_data);
+}
+
+void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+ unsigned int lreg;
+ unsigned int lval;
+
+ if (reg > 0x7f)
+ return;
+
+
+ lreg = reg << 12;
+ write_ssi(lreg, &ssi->sacadd);
+
+ lval = val << 4;
+ write_ssi(lval , &ssi->sacdat);
+
+ write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+ CCSR_SSI_SACNT_WR);
+ udelay(100);
+}
+
+unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+
+ unsigned short val = -1;
+ unsigned int lreg;
+
+ lreg = (reg & 0x7f) << 12;
+ write_ssi(lreg, &ssi->sacadd);
+ write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+ CCSR_SSI_SACNT_RD);
+
+ udelay(100);
+
+ val = (read_ssi(&ssi->sacdat) >> 4) & 0xffff;
+
+ return val;
+}
+
+static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
+ .read = fsl_ssi_ac97_read,
+ .write = fsl_ssi_ac97_write,
+};
+
/* Show the statistics of a flag only if its interrupt is enabled. The
* compiler will optimze this code to a no-op if the interrupt is not
* enabled.
@@ -663,6 +871,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
struct resource res;
char name[64];
bool shared;
+ bool ac97 = false;
/* SSIs that are not connected on the board should have a
* status = "disabled"
@@ -673,14 +882,20 @@ static int fsl_ssi_probe(struct platform_device *pdev)
/* We only support the SSI in "I2S Slave" mode */
sprop = of_get_property(np, "fsl,mode", NULL);
- if (!sprop || strcmp(sprop, "i2s-slave")) {
+ if (!sprop) {
+ dev_err(&pdev->dev, "fsl,mode property is necessary\n");
+ return -EINVAL;
+ }
+ if (!strcmp(sprop, "ac97-slave")) {
+ ac97 = true;
+ } else if (strcmp(sprop, "i2s-slave")) {
dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
return -ENODEV;
}
/* The DAI name is the last part of the full name of the node. */
p = strrchr(np->full_name, '/') + 1;
- ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
+ ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p),
GFP_KERNEL);
if (!ssi_private) {
dev_err(&pdev->dev, "could not allocate DAI object\n");
@@ -689,38 +904,41 @@ static int fsl_ssi_probe(struct platform_device *pdev)
strcpy(ssi_private->name, p);
- /* Initialize this copy of the CPU DAI driver structure */
- memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
- sizeof(fsl_ssi_dai_template));
+ ssi_private->use_dma = !of_property_read_bool(np,
+ "fsl,fiq-stream-filter");
+
+ if (ac97) {
+ memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
+ sizeof(fsl_ssi_ac97_dai));
+
+ fsl_ac97_data = ssi_private;
+ ssi_private->imx_ac97 = true;
+
+ snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+ } else {
+ /* Initialize this copy of the CPU DAI driver structure */
+ memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
+ sizeof(fsl_ssi_dai_template));
+ }
ssi_private->cpu_dai_drv.name = ssi_private->name;
/* Get the addresses and IRQ */
ret = of_address_to_resource(np, 0, &res);
if (ret) {
dev_err(&pdev->dev, "could not determine device resources\n");
- goto error_kmalloc;
+ return ret;
}
ssi_private->ssi = of_iomap(np, 0);
if (!ssi_private->ssi) {
dev_err(&pdev->dev, "could not map device resources\n");
- ret = -ENOMEM;
- goto error_kmalloc;
+ return -ENOMEM;
}
ssi_private->ssi_phys = res.start;
ssi_private->irq = irq_of_parse_and_map(np, 0);
if (ssi_private->irq == NO_IRQ) {
dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
- ret = -ENXIO;
- goto error_iomap;
- }
-
- /* The 'name' should not have any slashes in it. */
- ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
- ssi_private);
- if (ret < 0) {
- dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
- goto error_irqmap;
+ return -ENXIO;
}
/* Are the RX and the TX clocks locked? */
@@ -739,13 +957,18 @@ static int fsl_ssi_probe(struct platform_device *pdev)
u32 dma_events[2];
ssi_private->ssi_on_imx = true;
- ssi_private->clk = clk_get(&pdev->dev, NULL);
+ ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ssi_private->clk)) {
ret = PTR_ERR(ssi_private->clk);
dev_err(&pdev->dev, "could not get clock: %d\n", ret);
- goto error_irq;
+ goto error_irqmap;
+ }
+ ret = clk_prepare_enable(ssi_private->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n",
+ ret);
+ goto error_irqmap;
}
- clk_prepare_enable(ssi_private->clk);
/*
* We have burstsize be "fifo_depth - 2" to match the SSI
@@ -763,24 +986,38 @@ static int fsl_ssi_probe(struct platform_device *pdev)
&ssi_private->filter_data_tx;
ssi_private->dma_params_rx.filter_data =
&ssi_private->filter_data_rx;
- /*
- * TODO: This is a temporary solution and should be changed
- * to use generic DMA binding later when the helplers get in.
- */
- ret = of_property_read_u32_array(pdev->dev.of_node,
+ if (!of_property_read_bool(pdev->dev.of_node, "dmas") &&
+ ssi_private->use_dma) {
+ /*
+ * FIXME: This is a temporary solution until all
+ * necessary dma drivers support the generic dma
+ * bindings.
+ */
+ ret = of_property_read_u32_array(pdev->dev.of_node,
"fsl,ssi-dma-events", dma_events, 2);
- if (ret) {
- dev_err(&pdev->dev, "could not get dma events\n");
- goto error_clk;
+ if (ret && ssi_private->use_dma) {
+ dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n");
+ goto error_clk;
+ }
}
shared = of_device_is_compatible(of_get_parent(np),
"fsl,spba-bus");
imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx,
- dma_events[0], shared);
+ dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
- dma_events[1], shared);
+ dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
+ } else if (ssi_private->use_dma) {
+ /* The 'name' should not have any slashes in it. */
+ ret = devm_request_irq(&pdev->dev, ssi_private->irq,
+ fsl_ssi_isr, 0, ssi_private->name,
+ ssi_private);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not claim irq %u\n",
+ ssi_private->irq);
+ goto error_irqmap;
+ }
}
/* Initialize the the device_attribute structure */
@@ -794,7 +1031,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "could not create sysfs %s file\n",
ssi_private->dev_attr.attr.name);
- goto error_irq;
+ goto error_clk;
}
/* Register with ASoC */
@@ -808,9 +1045,30 @@ static int fsl_ssi_probe(struct platform_device *pdev)
}
if (ssi_private->ssi_on_imx) {
- ret = imx_pcm_dma_init(pdev);
- if (ret)
- goto error_dev;
+ if (!ssi_private->use_dma) {
+
+ /*
+ * Some boards use an incompatible codec. To get it
+ * working, we are using imx-fiq-pcm-audio, that
+ * can handle those codecs. DMA is not possible in this
+ * situation.
+ */
+
+ ssi_private->fiq_params.irq = ssi_private->irq;
+ ssi_private->fiq_params.base = ssi_private->ssi;
+ ssi_private->fiq_params.dma_params_rx =
+ &ssi_private->dma_params_rx;
+ ssi_private->fiq_params.dma_params_tx =
+ &ssi_private->dma_params_tx;
+
+ ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
+ if (ret)
+ goto error_dev;
+ } else {
+ ret = imx_pcm_dma_init(pdev);
+ if (ret)
+ goto error_dev;
+ }
}
/*
@@ -845,6 +1103,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
}
done:
+ if (ssi_private->imx_ac97)
+ fsl_ssi_ac97_init();
+
return 0;
error_dai:
@@ -853,27 +1114,15 @@ error_dai:
snd_soc_unregister_component(&pdev->dev);
error_dev:
- dev_set_drvdata(&pdev->dev, NULL);
device_remove_file(&pdev->dev, dev_attr);
error_clk:
- if (ssi_private->ssi_on_imx) {
+ if (ssi_private->ssi_on_imx)
clk_disable_unprepare(ssi_private->clk);
- clk_put(ssi_private->clk);
- }
-
-error_irq:
- free_irq(ssi_private->irq, ssi_private);
error_irqmap:
irq_dispose_mapping(ssi_private->irq);
-error_iomap:
- iounmap(ssi_private->ssi);
-
-error_kmalloc:
- kfree(ssi_private);
-
return ret;
}
@@ -883,20 +1132,15 @@ static int fsl_ssi_remove(struct platform_device *pdev)
if (!ssi_private->new_binding)
platform_device_unregister(ssi_private->pdev);
- if (ssi_private->ssi_on_imx) {
+ if (ssi_private->ssi_on_imx)
imx_pcm_dma_exit(pdev);
- clk_disable_unprepare(ssi_private->clk);
- clk_put(ssi_private->clk);
- }
snd_soc_unregister_component(&pdev->dev);
+ dev_set_drvdata(&pdev->dev, NULL);
device_remove_file(&pdev->dev, &ssi_private->dev_attr);
-
- free_irq(ssi_private->irq, ssi_private);
+ if (ssi_private->ssi_on_imx)
+ clk_disable_unprepare(ssi_private->clk);
irq_dispose_mapping(ssi_private->irq);
- kfree(ssi_private);
- dev_set_drvdata(&pdev->dev, NULL);
-
return 0;
}
@@ -919,6 +1163,7 @@ static struct platform_driver fsl_ssi_driver = {
module_platform_driver(fsl_ssi_driver);
+MODULE_ALIAS("platform:fsl-ssi-dai");
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index e260f1f899db..ab17381cc981 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -73,8 +73,11 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
- if (audmux_clk)
- clk_prepare_enable(audmux_clk);
+ if (audmux_clk) {
+ ret = clk_prepare_enable(audmux_clk);
+ if (ret)
+ return ret;
+ }
ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port));
pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port));
@@ -224,14 +227,19 @@ EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port);
int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
unsigned int pdcr)
{
+ int ret;
+
if (audmux_type != IMX31_AUDMUX)
return -EINVAL;
if (!audmux_base)
return -ENOSYS;
- if (audmux_clk)
- clk_prepare_enable(audmux_clk);
+ if (audmux_clk) {
+ ret = clk_prepare_enable(audmux_clk);
+ if (ret)
+ return ret;
+ }
writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port));
writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port));
@@ -243,6 +251,66 @@ int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
}
EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
+static int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
+ struct device_node *of_node)
+{
+ struct device_node *child;
+
+ for_each_available_child_of_node(of_node, child) {
+ unsigned int port;
+ unsigned int ptcr = 0;
+ unsigned int pdcr = 0;
+ unsigned int pcr = 0;
+ unsigned int val;
+ int ret;
+ int i = 0;
+
+ ret = of_property_read_u32(child, "fsl,audmux-port", &port);
+ if (ret) {
+ dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%s\"\n",
+ child->full_name);
+ continue;
+ }
+ if (!of_property_read_bool(child, "fsl,port-config")) {
+ dev_warn(&pdev->dev, "child node \"%s\" does not have property fsl,port-config\n",
+ child->full_name);
+ continue;
+ }
+
+ for (i = 0; (ret = of_property_read_u32_index(child,
+ "fsl,port-config", i, &val)) == 0;
+ ++i) {
+ if (audmux_type == IMX31_AUDMUX) {
+ if (i % 2)
+ pdcr |= val;
+ else
+ ptcr |= val;
+ } else {
+ pcr |= val;
+ }
+ }
+
+ if (ret != -EOVERFLOW) {
+ dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n",
+ i, child->full_name);
+ continue;
+ }
+
+ if (audmux_type == IMX31_AUDMUX) {
+ if (i % 2) {
+ dev_err(&pdev->dev, "One pdcr value is missing in child node %s\n",
+ child->full_name);
+ continue;
+ }
+ imx_audmux_v2_configure_port(port, ptcr, pdcr);
+ } else {
+ imx_audmux_v1_configure_port(port, pcr);
+ }
+ }
+
+ return 0;
+}
+
static int imx_audmux_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -267,6 +335,8 @@ static int imx_audmux_probe(struct platform_device *pdev)
if (audmux_type == IMX31_AUDMUX)
audmux_debugfs_init();
+ imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node);
+
return 0;
}
diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h
index b8ff44b9dafa..38a4209af7c6 100644
--- a/sound/soc/fsl/imx-audmux.h
+++ b/sound/soc/fsl/imx-audmux.h
@@ -1,57 +1,7 @@
#ifndef __IMX_AUDMUX_H
#define __IMX_AUDMUX_H
-#define MX27_AUDMUX_HPCR1_SSI0 0
-#define MX27_AUDMUX_HPCR2_SSI1 1
-#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2
-#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3
-#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4
-#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5
-
-#define MX31_AUDMUX_PORT1_SSI0 0
-#define MX31_AUDMUX_PORT2_SSI1 1
-#define MX31_AUDMUX_PORT3_SSI_PINS_3 2
-#define MX31_AUDMUX_PORT4_SSI_PINS_4 3
-#define MX31_AUDMUX_PORT5_SSI_PINS_5 4
-#define MX31_AUDMUX_PORT6_SSI_PINS_6 5
-#define MX31_AUDMUX_PORT7_SSI_PINS_7 6
-
-#define MX51_AUDMUX_PORT1_SSI0 0
-#define MX51_AUDMUX_PORT2_SSI1 1
-#define MX51_AUDMUX_PORT3 2
-#define MX51_AUDMUX_PORT4 3
-#define MX51_AUDMUX_PORT5 4
-#define MX51_AUDMUX_PORT6 5
-#define MX51_AUDMUX_PORT7 6
-
-/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */
-#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff)
-#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8)
-#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10)
-#define IMX_AUDMUX_V1_PCR_SYN (1 << 12)
-#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13)
-#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20)
-#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24)
-#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25)
-#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26)
-#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30)
-#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31)
-
-/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */
-#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31)
-#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27)
-#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26)
-#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22)
-#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21)
-#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17)
-#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16)
-#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12)
-#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11)
-
-#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13)
-#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12)
-#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8)
-#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff)
+#include <dt-bindings/sound/fsl-imx-audmux.h>
int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr);
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
index 9df173c091a6..a3d60d4bea4c 100644
--- a/sound/soc/fsl/imx-mc13783.c
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -90,6 +90,7 @@ static const struct snd_soc_dapm_route imx_mc13783_routes[] = {
static struct snd_soc_card imx_mc13783 = {
.name = "imx_mc13783",
+ .owner = THIS_MODULE,
.dai_link = imx_mc13783_dai_mc13783,
.num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783),
.dapm_widgets = imx_mc13783_widget,
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index fde4d2ea68c8..4dc1296688e9 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/types.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -64,7 +65,6 @@ int imx_pcm_dma_init(struct platform_device *pdev)
{
return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
- SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
@@ -74,3 +74,5 @@ void imx_pcm_dma_exit(struct platform_device *pdev)
snd_dmaengine_pcm_unregister(&pdev->dev);
}
EXPORT_SYMBOL_GPL(imx_pcm_dma_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index 310d90290320..34043c55f2a6 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -32,6 +33,7 @@
#include <linux/platform_data/asoc-imx-ssi.h>
#include "imx-ssi.h"
+#include "imx-pcm.h"
struct imx_pcm_runtime_data {
unsigned int period;
@@ -366,9 +368,9 @@ static struct snd_soc_platform_driver imx_soc_platform_fiq = {
.pcm_free = imx_pcm_fiq_free,
};
-int imx_pcm_fiq_init(struct platform_device *pdev)
+int imx_pcm_fiq_init(struct platform_device *pdev,
+ struct imx_pcm_fiq_params *params)
{
- struct imx_ssi *ssi = platform_get_drvdata(pdev);
int ret;
ret = claim_fiq(&fh);
@@ -377,15 +379,15 @@ int imx_pcm_fiq_init(struct platform_device *pdev)
return ret;
}
- mxc_set_irq_fiq(ssi->irq, 1);
- ssi_irq = ssi->irq;
+ mxc_set_irq_fiq(params->irq, 1);
+ ssi_irq = params->irq;
- imx_pcm_fiq = ssi->irq;
+ imx_pcm_fiq = params->irq;
- imx_ssi_fiq_base = (unsigned long)ssi->base;
+ imx_ssi_fiq_base = (unsigned long)params->base;
- ssi->dma_params_tx.maxburst = 4;
- ssi->dma_params_rx.maxburst = 6;
+ params->dma_params_tx->maxburst = 4;
+ params->dma_params_rx->maxburst = 6;
ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq);
if (ret)
@@ -406,3 +408,5 @@ void imx_pcm_fiq_exit(struct platform_device *pdev)
snd_soc_unregister_platform(&pdev->dev);
}
EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index 67f656c7c320..5d5b73303e11 100644
--- a/sound/soc/fsl/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -22,17 +22,23 @@
static inline void
imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data,
- int dma, bool shared)
+ int dma, enum sdma_peripheral_type peripheral_type)
{
dma_data->dma_request = dma;
dma_data->priority = DMA_PRIO_HIGH;
- if (shared)
- dma_data->peripheral_type = IMX_DMATYPE_SSI_SP;
- else
- dma_data->peripheral_type = IMX_DMATYPE_SSI;
+ dma_data->peripheral_type = peripheral_type;
}
-#ifdef CONFIG_SND_SOC_IMX_PCM_DMA
+struct imx_pcm_fiq_params {
+ int irq;
+ void __iomem *base;
+
+ /* Pointer to original ssi driver to setup tx rx sizes */
+ struct snd_dmaengine_dai_dma_data *dma_params_rx;
+ struct snd_dmaengine_dai_dma_data *dma_params_tx;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA)
int imx_pcm_dma_init(struct platform_device *pdev);
void imx_pcm_dma_exit(struct platform_device *pdev);
#else
@@ -46,11 +52,13 @@ static inline void imx_pcm_dma_exit(struct platform_device *pdev)
}
#endif
-#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ
-int imx_pcm_fiq_init(struct platform_device *pdev);
+#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ)
+int imx_pcm_fiq_init(struct platform_device *pdev,
+ struct imx_pcm_fiq_params *params);
void imx_pcm_fiq_exit(struct platform_device *pdev);
#else
-static inline int imx_pcm_fiq_init(struct platform_device *pdev)
+static inline int imx_pcm_fiq_init(struct platform_device *pdev,
+ struct imx_pcm_fiq_params *params)
{
return -ENODEV;
}
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 3f726e4f88db..389cbfa6dca7 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -129,8 +129,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
}
data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
- if (IS_ERR(data->codec_clk))
+ if (IS_ERR(data->codec_clk)) {
+ ret = PTR_ERR(data->codec_clk);
goto fail;
+ }
data->clk_frequency = clk_get_rate(data->codec_clk);
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
new file mode 100644
index 000000000000..816013b0ebba
--- /dev/null
+++ b/sound/soc/fsl/imx-spdif.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+
+struct imx_spdif_data {
+ struct snd_soc_dai_link dai[2];
+ struct snd_soc_card card;
+ struct platform_device *txdev;
+ struct platform_device *rxdev;
+};
+
+static int imx_spdif_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *spdif_np, *np = pdev->dev.of_node;
+ struct imx_spdif_data *data;
+ int ret = 0, num_links = 0;
+
+ spdif_np = of_parse_phandle(np, "spdif-controller", 0);
+ if (!spdif_np) {
+ dev_err(&pdev->dev, "failed to find spdif-controller\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ if (of_property_read_bool(np, "spdif-out")) {
+ data->dai[num_links].name = "S/PDIF TX";
+ data->dai[num_links].stream_name = "S/PDIF PCM Playback";
+ data->dai[num_links].codec_dai_name = "dit-hifi";
+ data->dai[num_links].codec_name = "spdif-dit";
+ data->dai[num_links].cpu_of_node = spdif_np;
+ data->dai[num_links].platform_of_node = spdif_np;
+ num_links++;
+
+ data->txdev = platform_device_register_simple("spdif-dit", -1, NULL, 0);
+ if (IS_ERR(data->txdev)) {
+ ret = PTR_ERR(data->txdev);
+ dev_err(&pdev->dev, "register dit failed: %d\n", ret);
+ goto end;
+ }
+ }
+
+ if (of_property_read_bool(np, "spdif-in")) {
+ data->dai[num_links].name = "S/PDIF RX";
+ data->dai[num_links].stream_name = "S/PDIF PCM Capture";
+ data->dai[num_links].codec_dai_name = "dir-hifi";
+ data->dai[num_links].codec_name = "spdif-dir";
+ data->dai[num_links].cpu_of_node = spdif_np;
+ data->dai[num_links].platform_of_node = spdif_np;
+ num_links++;
+
+ data->rxdev = platform_device_register_simple("spdif-dir", -1, NULL, 0);
+ if (IS_ERR(data->rxdev)) {
+ ret = PTR_ERR(data->rxdev);
+ dev_err(&pdev->dev, "register dir failed: %d\n", ret);
+ goto error_dit;
+ }
+ }
+
+ if (!num_links) {
+ dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
+ goto error_dir;
+ }
+
+ data->card.dev = &pdev->dev;
+ data->card.num_links = num_links;
+ data->card.dai_link = data->dai;
+
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto error_dir;
+
+ ret = snd_soc_register_card(&data->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
+ goto error_dir;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ goto end;
+
+error_dir:
+ if (data->rxdev)
+ platform_device_unregister(data->rxdev);
+error_dit:
+ if (data->txdev)
+ platform_device_unregister(data->txdev);
+end:
+ if (spdif_np)
+ of_node_put(spdif_np);
+
+ return ret;
+}
+
+static int imx_spdif_audio_remove(struct platform_device *pdev)
+{
+ struct imx_spdif_data *data = platform_get_drvdata(pdev);
+
+ if (data->rxdev)
+ platform_device_unregister(data->rxdev);
+ if (data->txdev)
+ platform_device_unregister(data->txdev);
+
+ snd_soc_unregister_card(&data->card);
+
+ return 0;
+}
+
+static const struct of_device_id imx_spdif_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-spdif", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
+
+static struct platform_driver imx_spdif_driver = {
+ .driver = {
+ .name = "imx-spdif",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_spdif_dt_ids,
+ },
+ .probe = imx_spdif_audio_probe,
+ .remove = imx_spdif_audio_remove,
+};
+
+module_platform_driver(imx_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-spdif");
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index 51be3772cba9..f58bcd85c07f 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -571,13 +571,13 @@ static int imx_ssi_probe(struct platform_device *pdev)
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
if (res) {
imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start,
- false);
+ IMX_DMATYPE_SSI);
}
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0");
if (res) {
imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start,
- false);
+ IMX_DMATYPE_SSI);
}
platform_set_drvdata(pdev, ssi);
@@ -595,7 +595,12 @@ static int imx_ssi_probe(struct platform_device *pdev)
goto failed_register;
}
- ret = imx_pcm_fiq_init(pdev);
+ ssi->fiq_params.irq = ssi->irq;
+ ssi->fiq_params.base = ssi->base;
+ ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
+ ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
+
+ ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
if (ret)
goto failed_pcm_fiq;
diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
index d5003cefca8d..fb1616ba8c59 100644
--- a/sound/soc/fsl/imx-ssi.h
+++ b/sound/soc/fsl/imx-ssi.h
@@ -209,6 +209,7 @@ struct imx_ssi {
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct imx_dma_data filter_data_tx;
struct imx_dma_data filter_data_rx;
+ struct imx_pcm_fiq_params fiq_params;
int enabled;
};
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index 52a36a90f4f4..1d70e278e915 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -217,7 +217,8 @@ static int imx_wm8962_probe(struct platform_device *pdev)
codec_dev = of_find_i2c_device_by_node(codec_np);
if (!codec_dev || !codec_dev->driver) {
dev_err(&pdev->dev, "failed to find codec platform device\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 6cf8355a8542..8c49147db84c 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -105,6 +105,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev)
static struct platform_driver asoc_simple_card = {
.driver = {
.name = "asoc-simple-card",
+ .owner = THIS_MODULE,
},
.probe = asoc_simple_card_probe,
.remove = asoc_simple_card_remove,
@@ -112,6 +113,7 @@ static struct platform_driver asoc_simple_card = {
module_platform_driver(asoc_simple_card);
+MODULE_ALIAS("platform:asoc-simple-card");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ASoC Simple Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index c62d715235e2..78ed4a42ad21 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -1,19 +1,15 @@
config SND_KIRKWOOD_SOC
- tristate "SoC Audio for the Marvell Kirkwood chip"
- depends on ARCH_KIRKWOOD
+ tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
+ depends on ARCH_KIRKWOOD || ARCH_DOVE || COMPILE_TEST
help
Say Y or M if you want to add support for codecs attached to
the Kirkwood I2S interface. You will also need to select the
audio interfaces to support below.
-config SND_KIRKWOOD_SOC_I2S
- tristate
-
config SND_KIRKWOOD_SOC_OPENRD
tristate "SoC Audio support for Kirkwood Openrd Client"
- depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE)
+ depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE || COMPILE_TEST)
depends on I2C
- select SND_KIRKWOOD_SOC_I2S
select SND_SOC_CS42L51
help
Say Y if you want to add support for SoC audio on
@@ -21,8 +17,7 @@ config SND_KIRKWOOD_SOC_OPENRD
config SND_KIRKWOOD_SOC_T5325
tristate "SoC Audio support for HP t5325"
- depends on SND_KIRKWOOD_SOC && MACH_T5325 && I2C
- select SND_KIRKWOOD_SOC_I2S
+ depends on SND_KIRKWOOD_SOC && (MACH_T5325 || COMPILE_TEST) && I2C
select SND_SOC_ALC5623
help
Say Y if you want to add support for SoC audio on
diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile
index 3e62ae9e7bbe..9e781385cb88 100644
--- a/sound/soc/kirkwood/Makefile
+++ b/sound/soc/kirkwood/Makefile
@@ -1,8 +1,6 @@
-snd-soc-kirkwood-objs := kirkwood-dma.o
-snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o
+snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o
obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
-obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o
snd-soc-openrd-objs := kirkwood-openrd.o
snd-soc-t5325-objs := kirkwood-t5325.o
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index a9f14530c3db..b238434f92b0 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -33,11 +33,11 @@
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE)
-struct kirkwood_dma_priv {
- struct snd_pcm_substream *play_stream;
- struct snd_pcm_substream *rec_stream;
- struct kirkwood_dma_data *data;
-};
+static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
+ return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai);
+}
static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
@@ -51,7 +51,7 @@ static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
.rate_max = 384000,
.channels_min = 1,
.channels_max = 8,
- .buffer_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS,
+ .buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES,
.period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES,
.period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES,
.periods_min = KIRKWOOD_SND_MIN_PERIODS,
@@ -63,8 +63,7 @@ static u64 kirkwood_dma_dmamask = DMA_BIT_MASK(32);
static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
{
- struct kirkwood_dma_priv *prdata = dev_id;
- struct kirkwood_dma_data *priv = prdata->data;
+ struct kirkwood_dma_data *priv = dev_id;
unsigned long mask, status, cause;
mask = readl(priv->io + KIRKWOOD_INT_MASK);
@@ -89,10 +88,10 @@ static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
writel(status, priv->io + KIRKWOOD_INT_CAUSE);
if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
- snd_pcm_period_elapsed(prdata->play_stream);
+ snd_pcm_period_elapsed(priv->substream_play);
if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
- snd_pcm_period_elapsed(prdata->rec_stream);
+ snd_pcm_period_elapsed(priv->substream_rec);
return IRQ_HANDLED;
}
@@ -126,15 +125,10 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
{
int err;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_platform *platform = soc_runtime->platform;
- struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
- struct kirkwood_dma_data *priv;
- struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform);
+ struct kirkwood_dma_data *priv = kirkwood_priv(substream);
const struct mbus_dram_target_info *dram;
unsigned long addr;
- priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
/* Ensure that all constraints linked to dma burst are fulfilled */
@@ -157,21 +151,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
if (err < 0)
return err;
- if (prdata == NULL) {
- prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL);
- if (prdata == NULL)
- return -ENOMEM;
-
- prdata->data = priv;
-
+ if (!priv->substream_play && !priv->substream_rec) {
err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
- "kirkwood-i2s", prdata);
- if (err) {
- kfree(prdata);
+ "kirkwood-i2s", priv);
+ if (err)
return -EBUSY;
- }
-
- snd_soc_platform_set_drvdata(platform, prdata);
/*
* Enable Error interrupts. We're only ack'ing them but
@@ -183,11 +167,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
dram = mv_mbus_dram_info();
addr = substream->dma_buffer.addr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- prdata->play_stream = substream;
+ priv->substream_play = substream;
kirkwood_dma_conf_mbus_windows(priv->io,
KIRKWOOD_PLAYBACK_WIN, addr, dram);
} else {
- prdata->rec_stream = substream;
+ priv->substream_rec = substream;
kirkwood_dma_conf_mbus_windows(priv->io,
KIRKWOOD_RECORD_WIN, addr, dram);
}
@@ -197,27 +181,19 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
static int kirkwood_dma_close(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
- struct snd_soc_platform *platform = soc_runtime->platform;
- struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform);
- struct kirkwood_dma_data *priv;
-
- priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ struct kirkwood_dma_data *priv = kirkwood_priv(substream);
- if (!prdata || !priv)
+ if (!priv)
return 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- prdata->play_stream = NULL;
+ priv->substream_play = NULL;
else
- prdata->rec_stream = NULL;
+ priv->substream_rec = NULL;
- if (!prdata->play_stream && !prdata->rec_stream) {
+ if (!priv->substream_play && !priv->substream_rec) {
writel(0, priv->io + KIRKWOOD_ERR_MASK);
- free_irq(priv->irq, prdata);
- kfree(prdata);
- snd_soc_platform_set_drvdata(platform, NULL);
+ free_irq(priv->irq, priv);
}
return 0;
@@ -243,13 +219,9 @@ static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
- struct kirkwood_dma_data *priv;
+ struct kirkwood_dma_data *priv = kirkwood_priv(substream);
unsigned long size, count;
- priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
-
/* compute buffer size in term of "words" as requested in specs */
size = frames_to_bytes(runtime, runtime->buffer_size);
size = (size>>2)-1;
@@ -272,13 +244,9 @@ static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
*substream)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
- struct kirkwood_dma_data *priv;
+ struct kirkwood_dma_data *priv = kirkwood_priv(substream);
snd_pcm_uframes_t count;
- priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
count = bytes_to_frames(substream->runtime,
readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
@@ -366,36 +334,8 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
}
}
-static struct snd_soc_platform_driver kirkwood_soc_platform = {
+struct snd_soc_platform_driver kirkwood_soc_platform = {
.ops = &kirkwood_dma_ops,
.pcm_new = kirkwood_dma_new,
.pcm_free = kirkwood_dma_free_dma_buffers,
};
-
-static int kirkwood_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform);
-}
-
-static int kirkwood_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver kirkwood_pcm_driver = {
- .driver = {
- .name = "kirkwood-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = kirkwood_soc_platform_probe,
- .remove = kirkwood_soc_platform_remove,
-};
-
-module_platform_driver(kirkwood_pcm_driver);
-
-MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
-MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:kirkwood-pcm-audio");
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 4c9dad3263c5..7fce340ab3ef 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -22,13 +22,12 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <linux/platform_data/asoc-kirkwood.h>
+#include <linux/of.h>
+
#include "kirkwood.h"
-#define DRV_NAME "kirkwood-i2s"
+#define DRV_NAME "mvebu-audio"
-#define KIRKWOOD_I2S_RATES \
- (SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
#define KIRKWOOD_I2S_FORMATS \
(SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
@@ -105,14 +104,16 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai,
uint32_t clks_ctrl;
if (rate == 44100 || rate == 48000 || rate == 96000) {
- /* use internal dco for supported rates */
+ /* use internal dco for the supported rates
+ * defined in kirkwood_i2s_dai */
dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
__func__, rate);
kirkwood_set_dco(priv->io, rate);
clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
- } else if (!IS_ERR(priv->extclk)) {
- /* use optional external clk for other rates */
+ } else {
+ /* use the external clock for the other rates
+ * defined in kirkwood_i2s_dai_extclk */
dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
__func__, rate, 256 * rate);
clk_set_rate(priv->extclk, 256 * rate);
@@ -199,8 +200,7 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
- KIRKWOOD_PLAYCTL_I2S_EN |
- KIRKWOOD_PLAYCTL_SPDIF_EN |
+ KIRKWOOD_PLAYCTL_ENABLE_MASK |
KIRKWOOD_PLAYCTL_SIZE_MASK);
priv->ctl_play |= ctl_play;
} else {
@@ -244,8 +244,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_START:
/* configure */
ctl = priv->ctl_play;
- value = ctl & ~(KIRKWOOD_PLAYCTL_I2S_EN |
- KIRKWOOD_PLAYCTL_SPDIF_EN);
+ value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_PLAYCTL);
/* enable interrupts */
@@ -267,7 +266,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
writel(value, priv->io + KIRKWOOD_INT_MASK);
/* disable all playbacks */
- ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
+ ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
break;
@@ -387,7 +386,7 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai)
/* disable playback/record */
value = readl(priv->io + KIRKWOOD_PLAYCTL);
- value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
+ value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_PLAYCTL);
value = readl(priv->io + KIRKWOOD_RECCTL);
@@ -398,11 +397,6 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai)
}
-static int kirkwood_i2s_remove(struct snd_soc_dai *dai)
-{
- return 0;
-}
-
static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
.startup = kirkwood_i2s_startup,
.trigger = kirkwood_i2s_trigger,
@@ -413,17 +407,18 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
static struct snd_soc_dai_driver kirkwood_i2s_dai = {
.probe = kirkwood_i2s_probe,
- .remove = kirkwood_i2s_remove,
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = KIRKWOOD_I2S_RATES,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
.formats = KIRKWOOD_I2S_FORMATS,
},
.capture = {
.channels_min = 1,
.channels_max = 2,
- .rates = KIRKWOOD_I2S_RATES,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
.formats = KIRKWOOD_I2S_FORMATS,
},
.ops = &kirkwood_i2s_dai_ops,
@@ -431,7 +426,6 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai = {
static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = {
.probe = kirkwood_i2s_probe,
- .remove = kirkwood_i2s_remove,
.playback = {
.channels_min = 1,
.channels_max = 2,
@@ -461,6 +455,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai;
struct kirkwood_dma_data *priv;
struct resource *mem;
+ struct device_node *np = pdev->dev.of_node;
int err;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -481,14 +476,16 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
return -ENXIO;
}
- if (!data) {
- dev_err(&pdev->dev, "no platform data ?!\n");
+ if (np) {
+ priv->burst = 128; /* might be 32 or 128 */
+ } else if (data) {
+ priv->burst = data->burst;
+ } else {
+ dev_err(&pdev->dev, "no DT nor platform data ?!\n");
return -EINVAL;
}
- priv->burst = data->burst;
-
- priv->clk = devm_clk_get(&pdev->dev, NULL);
+ priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL);
if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "no clock\n");
return PTR_ERR(priv->clk);
@@ -498,10 +495,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
if (err < 0)
return err;
- priv->extclk = clk_get(&pdev->dev, "extclk");
+ priv->extclk = devm_clk_get(&pdev->dev, "extclk");
if (!IS_ERR(priv->extclk)) {
if (priv->extclk == priv->clk) {
- clk_put(priv->extclk);
+ devm_clk_put(&pdev->dev, priv->extclk);
priv->extclk = ERR_PTR(-EINVAL);
} else {
dev_info(&pdev->dev, "found external clock\n");
@@ -515,7 +512,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
/* Select the burst size */
- if (data->burst == 32) {
+ if (priv->burst == 32) {
priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
} else {
@@ -525,14 +522,22 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component,
soc_dai, 1);
- if (!err)
- return 0;
- dev_err(&pdev->dev, "snd_soc_register_component failed\n");
+ if (err) {
+ dev_err(&pdev->dev, "snd_soc_register_component failed\n");
+ goto err_component;
+ }
- if (!IS_ERR(priv->extclk)) {
- clk_disable_unprepare(priv->extclk);
- clk_put(priv->extclk);
+ err = snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform);
+ if (err) {
+ dev_err(&pdev->dev, "snd_soc_register_platform failed\n");
+ goto err_platform;
}
+ return 0;
+ err_platform:
+ snd_soc_unregister_component(&pdev->dev);
+ err_component:
+ if (!IS_ERR(priv->extclk))
+ clk_disable_unprepare(priv->extclk);
clk_disable_unprepare(priv->clk);
return err;
@@ -542,23 +547,31 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
{
struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
+ snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
- if (!IS_ERR(priv->extclk)) {
+ if (!IS_ERR(priv->extclk))
clk_disable_unprepare(priv->extclk);
- clk_put(priv->extclk);
- }
clk_disable_unprepare(priv->clk);
return 0;
}
+#ifdef CONFIG_OF
+static struct of_device_id mvebu_audio_of_match[] = {
+ { .compatible = "marvell,mvebu-audio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
+#endif
+
static struct platform_driver kirkwood_i2s_driver = {
.probe = kirkwood_i2s_dev_probe,
.remove = kirkwood_i2s_dev_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mvebu_audio_of_match),
},
};
@@ -568,4 +581,4 @@ module_platform_driver(kirkwood_i2s_driver);
MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:kirkwood-i2s");
+MODULE_ALIAS("platform:mvebu-audio");
diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c
index b979c7154715..025be0e97164 100644
--- a/sound/soc/kirkwood/kirkwood-openrd.c
+++ b/sound/soc/kirkwood/kirkwood-openrd.c
@@ -16,9 +16,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
-#include <mach/kirkwood.h>
#include <linux/platform_data/asoc-kirkwood.h>
-#include <asm/mach-types.h>
#include "../codecs/cs42l51.h"
static int openrd_client_hw_params(struct snd_pcm_substream *substream,
@@ -54,8 +52,8 @@ static struct snd_soc_dai_link openrd_client_dai[] = {
{
.name = "CS42L51",
.stream_name = "CS42L51 HiFi",
- .cpu_dai_name = "kirkwood-i2s",
- .platform_name = "kirkwood-pcm-audio",
+ .cpu_dai_name = "mvebu-audio",
+ .platform_name = "mvebu-audio",
.codec_dai_name = "cs42l51-hifi",
.codec_name = "cs42l51-codec.0-004a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
diff --git a/sound/soc/kirkwood/kirkwood-t5325.c b/sound/soc/kirkwood/kirkwood-t5325.c
index 1d0ed6f8add7..27545b0c4856 100644
--- a/sound/soc/kirkwood/kirkwood-t5325.c
+++ b/sound/soc/kirkwood/kirkwood-t5325.c
@@ -15,9 +15,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
-#include <mach/kirkwood.h>
#include <linux/platform_data/asoc-kirkwood.h>
-#include <asm/mach-types.h>
#include "../codecs/alc5623.h"
static int t5325_hw_params(struct snd_pcm_substream *substream,
@@ -70,8 +68,8 @@ static struct snd_soc_dai_link t5325_dai[] = {
{
.name = "ALC5621",
.stream_name = "ALC5621 HiFi",
- .cpu_dai_name = "kirkwood-i2s",
- .platform_name = "kirkwood-pcm-audio",
+ .cpu_dai_name = "mvebu-audio",
+ .platform_name = "mvebu-audio",
.codec_dai_name = "alc5621-hifi",
.codec_name = "alc562x-codec.0-001a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
index 4d92637ddb3f..f8e1ccc1c58c 100644
--- a/sound/soc/kirkwood/kirkwood.h
+++ b/sound/soc/kirkwood/kirkwood.h
@@ -54,7 +54,7 @@
#define KIRKWOOD_PLAYCTL_MONO_OFF (0<<5)
#define KIRKWOOD_PLAYCTL_I2S_MUTE (1<<7)
#define KIRKWOOD_PLAYCTL_SPDIF_EN (1<<4)
-#define KIRKWOOD_PLAYCTL_I2S_EN (1<<3)
+#define KIRKWOOD_PLAYCTL_I2S_EN (1<<3)
#define KIRKWOOD_PLAYCTL_SIZE_MASK (7<<0)
#define KIRKWOOD_PLAYCTL_SIZE_16 (7<<0)
#define KIRKWOOD_PLAYCTL_SIZE_16_C (3<<0)
@@ -62,6 +62,9 @@
#define KIRKWOOD_PLAYCTL_SIZE_24 (1<<0)
#define KIRKWOOD_PLAYCTL_SIZE_32 (0<<0)
+#define KIRKWOOD_PLAYCTL_ENABLE_MASK (KIRKWOOD_PLAYCTL_SPDIF_EN | \
+ KIRKWOOD_PLAYCTL_I2S_EN)
+
#define KIRKWOOD_PLAY_BUF_ADDR 0x1104
#define KIRKWOOD_PLAY_BUF_SIZE 0x1108
#define KIRKWOOD_PLAY_BYTE_COUNT 0x110C
@@ -122,6 +125,8 @@
#define KIRKWOOD_SND_MAX_PERIODS 16
#define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x4000
#define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x4000
+#define KIRKWOOD_SND_MAX_BUFFER_BYTES (KIRKWOOD_SND_MAX_PERIOD_BYTES \
+ * KIRKWOOD_SND_MAX_PERIODS)
struct kirkwood_dma_data {
void __iomem *io;
@@ -129,8 +134,12 @@ struct kirkwood_dma_data {
struct clk *extclk;
uint32_t ctl_play;
uint32_t ctl_rec;
+ struct snd_pcm_substream *substream_play;
+ struct snd_pcm_substream *substream_rec;
int irq;
int burst;
};
+extern struct snd_soc_platform_driver kirkwood_soc_platform;
+
#endif
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig
index 78d321cbe8b4..219235c02212 100644
--- a/sound/soc/mxs/Kconfig
+++ b/sound/soc/mxs/Kconfig
@@ -1,6 +1,7 @@
menuconfig SND_MXS_SOC
tristate "SoC Audio for Freescale MXS CPUs"
- depends on ARCH_MXS
+ depends on ARCH_MXS || COMPILE_TEST
+ depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 54511c5e6a7c..b56b8a0e8deb 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -31,7 +31,6 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <asm/mach-types.h>
#include "mxs-saif.h"
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index 1b134d72f120..4bb273786ff3 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -25,7 +25,6 @@
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/soc-dapm.h>
-#include <asm/mach-types.h>
#include "../codecs/sgtl5000.h"
#include "mxs-saif.h"
@@ -51,18 +50,27 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
}
/* Sgtl5000 sysclk should be >= 8MHz and <= 27M */
- if (mclk < 8000000 || mclk > 27000000)
+ if (mclk < 8000000 || mclk > 27000000) {
+ dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n",
+ mclk / 1000000, mclk / 1000 % 1000);
return -EINVAL;
+ }
/* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
- if (ret)
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
+ mclk / 1000000, mclk / 1000 % 1000);
return ret;
+ }
/* The SAIF MCLK should be the same as SGTL5000_SYSCLK */
ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0);
- if (ret)
+ if (ret) {
+ dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
+ mclk / 1000000, mclk / 1000 % 1000);
return ret;
+ }
/* set codec to slave mode */
dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
@@ -70,13 +78,19 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
- if (ret)
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set dai format to %08x\n",
+ dai_format);
return ret;
+ }
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
- if (ret)
+ if (ret) {
+ dev_err(cpu_dai->dev, "Failed to set dai format to %08x\n",
+ dai_format);
return ret;
+ }
return 0;
}
@@ -91,11 +105,13 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
.stream_name = "HiFi Playback",
.codec_dai_name = "sgtl5000",
.ops = &mxs_sgtl5000_hifi_ops,
+ .playback_only = true,
}, {
.name = "HiFi Rx",
.stream_name = "HiFi Capture",
.codec_dai_name = "sgtl5000",
.ops = &mxs_sgtl5000_hifi_ops,
+ .capture_only = true,
},
};
@@ -154,8 +170,10 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
* should be >= 8MHz and <= 27M.
*/
ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
- if (ret)
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get mclk\n");
return ret;
+ }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index f4c2417a8730..8987bf987e58 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -333,9 +333,6 @@ static int nuc900_ac97_drvprobe(struct platform_device *pdev)
spin_lock_init(&nuc900_audio->lock);
nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!nuc900_audio->res)
- return ret;
-
nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev,
nuc900_audio->res);
if (IS_ERR(nuc900_audio->mmio))
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 9f5d55e6b17a..daa78a0095fa 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -1,7 +1,7 @@
config SND_OMAP_SOC
tristate "SoC Audio for the Texas Instruments OMAP chips"
- depends on ARCH_OMAP && DMA_OMAP
- select SND_SOC_DMAENGINE_PCM
+ depends on (ARCH_OMAP && DMA_OMAP) || (ARCH_ARM && COMPILE_TEST)
+ select SND_DMAENGINE_PCM
config SND_OMAP_SOC_DMIC
tristate
@@ -26,7 +26,7 @@ config SND_OMAP_SOC_N810
config SND_OMAP_SOC_RX51
tristate "SoC Audio support for Nokia RX-51"
- depends on SND_OMAP_SOC && MACH_NOKIA_RX51
+ depends on SND_OMAP_SOC && ARCH_ARM && (MACH_NOKIA_RX51 || COMPILE_TEST)
select SND_OMAP_SOC_MCBSP
select SND_SOC_TLV320AIC3X
select SND_SOC_TPA6130A2
@@ -87,7 +87,7 @@ config SND_OMAP_SOC_OMAP_TWL4030
config SND_OMAP_SOC_OMAP_ABE_TWL6040
tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
- depends on TWL6040_CORE && SND_OMAP_SOC && ARCH_OMAP4
+ depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || COMPILE_TEST)
select SND_OMAP_SOC_DMIC
select SND_OMAP_SOC_MCPDM
select SND_SOC_TWL6040
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index 361e4c03646e..83433fdea32a 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -781,7 +781,7 @@ static ssize_t prop##_store(struct device *dev, \
unsigned long val; \
int status; \
\
- status = strict_strtoul(buf, 0, &val); \
+ status = kstrtoul(buf, 0, &val); \
if (status) \
return status; \
\
diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c
index 70cd5c7b2e14..ebb13906b3a0 100644
--- a/sound/soc/omap/omap-abe-twl6040.c
+++ b/sound/soc/omap/omap-abe-twl6040.c
@@ -23,7 +23,6 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/mfd/twl6040.h>
-#include <linux/platform_data/omap-abe-twl6040.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -166,19 +165,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"AFMR", NULL, "Line In"},
};
-static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm,
- int connected, char *pin)
-{
- if (!connected)
- snd_soc_dapm_disable_pin(dapm, pin);
-}
-
static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = codec->card;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev);
struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
int hs_trim;
int ret = 0;
@@ -203,24 +193,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
}
- /*
- * NULL pdata means we booted with DT. In this case the routing is
- * provided and the card is fully routed, no need to mark pins.
- */
- if (!pdata)
- return ret;
-
- /* Disable not connected paths if not used */
- twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
- twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk");
- twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk");
- twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out");
- twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator");
- twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
- twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic");
- twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic");
- twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In");
-
return ret;
}
@@ -274,13 +246,18 @@ static struct snd_soc_card omap_abe_card = {
static int omap_abe_probe(struct platform_device *pdev)
{
- struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
struct snd_soc_card *card = &omap_abe_card;
+ struct device_node *dai_node;
struct abe_twl6040 *priv;
int num_links = 0;
int ret = 0;
+ if (!node) {
+ dev_err(&pdev->dev, "of node is missing.\n");
+ return -ENODEV;
+ }
+
card->dev = &pdev->dev;
priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
@@ -289,78 +266,50 @@ static int omap_abe_probe(struct platform_device *pdev)
priv->dmic_codec_dev = ERR_PTR(-EINVAL);
- if (node) {
- struct device_node *dai_node;
-
- if (snd_soc_of_parse_card_name(card, "ti,model")) {
- dev_err(&pdev->dev, "Card name is not provided\n");
- return -ENODEV;
- }
+ if (snd_soc_of_parse_card_name(card, "ti,model")) {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
- ret = snd_soc_of_parse_audio_routing(card,
- "ti,audio-routing");
- if (ret) {
- dev_err(&pdev->dev,
- "Error while parsing DAPM routing\n");
- return ret;
- }
+ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "Error while parsing DAPM routing\n");
+ return ret;
+ }
- dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
- if (!dai_node) {
- dev_err(&pdev->dev, "McPDM node is not provided\n");
- return -EINVAL;
- }
- abe_twl6040_dai_links[0].cpu_dai_name = NULL;
- abe_twl6040_dai_links[0].cpu_of_node = dai_node;
+ dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "McPDM node is not provided\n");
+ return -EINVAL;
+ }
+ abe_twl6040_dai_links[0].cpu_dai_name = NULL;
+ abe_twl6040_dai_links[0].cpu_of_node = dai_node;
- dai_node = of_parse_phandle(node, "ti,dmic", 0);
- if (dai_node) {
- num_links = 2;
- abe_twl6040_dai_links[1].cpu_dai_name = NULL;
- abe_twl6040_dai_links[1].cpu_of_node = dai_node;
+ dai_node = of_parse_phandle(node, "ti,dmic", 0);
+ if (dai_node) {
+ num_links = 2;
+ abe_twl6040_dai_links[1].cpu_dai_name = NULL;
+ abe_twl6040_dai_links[1].cpu_of_node = dai_node;
- priv->dmic_codec_dev = platform_device_register_simple(
+ priv->dmic_codec_dev = platform_device_register_simple(
"dmic-codec", -1, NULL, 0);
- if (IS_ERR(priv->dmic_codec_dev)) {
- dev_err(&pdev->dev,
- "Can't instantiate dmic-codec\n");
- return PTR_ERR(priv->dmic_codec_dev);
- }
- } else {
- num_links = 1;
- }
-
- priv->jack_detection = of_property_read_bool(node,
- "ti,jack-detection");
- of_property_read_u32(node, "ti,mclk-freq",
- &priv->mclk_freq);
- if (!priv->mclk_freq) {
- dev_err(&pdev->dev, "MCLK frequency not provided\n");
- ret = -EINVAL;
- goto err_unregister;
+ if (IS_ERR(priv->dmic_codec_dev)) {
+ dev_err(&pdev->dev, "Can't instantiate dmic-codec\n");
+ return PTR_ERR(priv->dmic_codec_dev);
}
-
- omap_abe_card.fully_routed = 1;
- } else if (pdata) {
- if (pdata->card_name) {
- card->name = pdata->card_name;
- } else {
- dev_err(&pdev->dev, "Card name is not provided\n");
- return -ENODEV;
- }
-
- if (pdata->has_dmic)
- num_links = 2;
- else
- num_links = 1;
-
- priv->jack_detection = pdata->jack_detection;
- priv->mclk_freq = pdata->mclk_freq;
} else {
- dev_err(&pdev->dev, "Missing pdata\n");
- return -ENODEV;
+ num_links = 1;
+ }
+
+ priv->jack_detection = of_property_read_bool(node, "ti,jack-detection");
+ of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq);
+ if (!priv->mclk_freq) {
+ dev_err(&pdev->dev, "MCLK frequency not provided\n");
+ ret = -EINVAL;
+ goto err_unregister;
}
+ card->fully_routed = 1;
if (!priv->mclk_freq) {
dev_err(&pdev->dev, "MCLK frequency missing\n");
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c
index 4db1f8e6e172..12e566be3793 100644
--- a/sound/soc/omap/omap-dmic.c
+++ b/sound/soc/omap/omap-dmic.c
@@ -480,15 +480,12 @@ static int asoc_dmic_probe(struct platform_device *pdev)
dmic->dma_data.filter_data = "up_link";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- if (!res) {
- dev_err(dmic->dev, "invalid memory resource\n");
- ret = -ENODEV;
+ dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dmic->io_base)) {
+ ret = PTR_ERR(dmic->io_base);
goto err_put_clk;
}
- dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(dmic->io_base))
- return PTR_ERR(dmic->io_base);
ret = snd_soc_register_component(&pdev->dev, &omap_dmic_component,
&omap_dmic_dai, 1);
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 7483efb6dc67..6c19bba23570 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -433,6 +433,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
/* Sample rate generator drives the FS */
regs->srgr2 |= FSGM;
break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ /* McBSP slave. FS clock as output */
+ regs->srgr2 |= FSGM;
+ regs->pcr0 |= FSXM;
+ break;
case SND_SOC_DAIFMT_CBM_CFM:
/* McBSP slave */
break;
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index a49dc52f8abc..90d2a7cd2563 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -480,9 +480,6 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
mcpdm->dma_data[1].filter_data = "up_link";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- if (res == NULL)
- return -ENOMEM;
-
mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mcpdm->io_base))
return PTR_ERR(mcpdm->io_base);
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index b35809467547..4db74a083db1 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -11,7 +11,7 @@ config SND_PXA2XX_SOC
config SND_MMP_SOC
bool "Soc Audio for Marvell MMP chips"
depends on ARCH_MMP
- select SND_SOC_DMAENGINE_PCM
+ select SND_DMAENGINE_PCM
select SND_ARM
help
Say Y if you want to add support for codecs attached to
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index 4ad76099dd43..5b7d969f89a9 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -129,6 +129,7 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
/* audio machine driver */
static struct snd_soc_card brownstone = {
.name = "brownstone",
+ .owner = THIS_MODULE,
.dai_link = brownstone_wm8994_dai,
.num_links = ARRAY_SIZE(brownstone_wm8994_dai),
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index 97b711e12821..bbea7780eac6 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -56,8 +56,6 @@
#include "pxa2xx-ac97.h"
#include "../codecs/wm9713.h"
-#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
-
#define AC97_GPIO_PULL 0x58
/* Use GPIO8 for rear speaker amplifier */
@@ -133,10 +131,11 @@ static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
unsigned short reg;
/* Add mioa701 specific widgets */
- snd_soc_dapm_new_controls(dapm, ARRAY_AND_SIZE(mioa701_dapm_widgets));
+ snd_soc_dapm_new_controls(dapm, mioa701_dapm_widgets,
+ ARRAY_SIZE(mioa701_dapm_widgets));
/* Set up mioa701 specific audio path audio_mapnects */
- snd_soc_dapm_add_routes(dapm, ARRAY_AND_SIZE(audio_map));
+ snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
/* Prepare GPIO8 for rear speaker amplifier */
reg = codec->driver->read(codec, AC97_GPIO_CFG);
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 5d57e071cdf5..8235e231d89c 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -17,6 +17,7 @@
#include <linux/dmaengine.h>
#include <linux/platform_data/dma-mmp_tdma.h>
#include <linux/platform_data/mmp_audio.h>
+
#include <sound/pxa2xx-lib.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -67,7 +68,7 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct pxa2xx_pcm_dma_params *dma_params;
+ struct snd_dmaengine_dai_dma_data *dma_params;
struct dma_slave_config slave_config;
int ret;
@@ -80,10 +81,10 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- slave_config.dst_addr = dma_params->dev_addr;
+ slave_config.dst_addr = dma_params->addr;
slave_config.dst_maxburst = 4;
} else {
- slave_config.src_addr = dma_params->dev_addr;
+ slave_config.src_addr = dma_params->addr;
slave_config.src_maxburst = 4;
}
diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c
index 62142ce367c7..41752a5fe3b0 100644
--- a/sound/soc/pxa/mmp-sspa.c
+++ b/sound/soc/pxa/mmp-sspa.c
@@ -27,12 +27,15 @@
#include <linux/slab.h>
#include <linux/pxa2xx_ssp.h>
#include <linux/io.h>
+#include <linux/dmaengine.h>
+
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include "mmp-sspa.h"
/*
@@ -40,7 +43,7 @@
*/
struct sspa_priv {
struct ssp_device *sspa;
- struct pxa2xx_pcm_dma_params *dma_params;
+ struct snd_dmaengine_dai_dma_data *dma_params;
struct clk *audio_clk;
struct clk *sysclk;
int dai_fmt;
@@ -266,7 +269,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
struct ssp_device *sspa = sspa_priv->sspa;
- struct pxa2xx_pcm_dma_params *dma_params;
+ struct snd_dmaengine_dai_dma_data *dma_params;
u32 sspa_ctrl;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -309,7 +312,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
}
dma_params = &sspa_priv->dma_params[substream->stream];
- dma_params->dev_addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
(sspa->phys_base + SSPA_TXD) :
(sspa->phys_base + SSPA_RXD);
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params);
@@ -425,14 +428,12 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev)
return -ENOMEM;
priv->dma_params = devm_kzalloc(&pdev->dev,
- 2 * sizeof(struct pxa2xx_pcm_dma_params), GFP_KERNEL);
+ 2 * sizeof(struct snd_dmaengine_dai_dma_data),
+ GFP_KERNEL);
if (priv->dma_params == NULL)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL)
- return -ENOMEM;
-
priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->sspa->mmio_base))
return PTR_ERR(priv->sspa->mmio_base);
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 6f4dd7543e82..a3119a00d8fa 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -21,6 +21,8 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/pxa2xx_ssp.h>
+#include <linux/of.h>
+#include <linux/dmaengine.h>
#include <asm/irq.h>
@@ -30,9 +32,9 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include <mach/hardware.h>
-#include <mach/dma.h>
#include "../../arm/pxa2xx-pcm.h"
#include "pxa-ssp.h"
@@ -79,27 +81,13 @@ static void pxa_ssp_disable(struct ssp_device *ssp)
__raw_writel(sscr0, ssp->mmio_base + SSCR0);
}
-struct pxa2xx_pcm_dma_data {
- struct pxa2xx_pcm_dma_params params;
- char name[20];
-};
-
static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4,
- int out, struct pxa2xx_pcm_dma_params *dma_data)
+ int out, struct snd_dmaengine_dai_dma_data *dma)
{
- struct pxa2xx_pcm_dma_data *dma;
-
- dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params);
-
- snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id,
- width4 ? "32-bit" : "16-bit", out ? "out" : "in");
-
- dma->params.name = dma->name;
- dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx);
- dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) :
- (DCMD_INCTRGADDR | DCMD_FLOWSRC)) |
- (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16;
- dma->params.dev_addr = ssp->phys_base + SSDR;
+ dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES :
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dma->maxburst = 16;
+ dma->addr = ssp->phys_base + SSDR;
}
static int pxa_ssp_startup(struct snd_pcm_substream *substream,
@@ -107,7 +95,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
{
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
struct ssp_device *ssp = priv->ssp;
- struct pxa2xx_pcm_dma_data *dma;
+ struct snd_dmaengine_dai_dma_data *dma;
int ret = 0;
if (!cpu_dai->active) {
@@ -115,10 +103,14 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
pxa_ssp_disable(ssp);
}
- dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
+ dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL);
if (!dma)
return -ENOMEM;
- snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params);
+
+ dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &ssp->drcmr_tx : &ssp->drcmr_rx;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, dma);
return ret;
}
@@ -559,7 +551,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
u32 sspsp;
int width = snd_pcm_format_physical_width(params_format(params));
int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf;
- struct pxa2xx_pcm_dma_params *dma_data;
+ struct snd_dmaengine_dai_dma_data *dma_data;
dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
@@ -719,6 +711,7 @@ static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
static int pxa_ssp_probe(struct snd_soc_dai *dai)
{
+ struct device *dev = dai->dev;
struct ssp_priv *priv;
int ret;
@@ -726,10 +719,26 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai)
if (!priv)
return -ENOMEM;
- priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
- if (priv->ssp == NULL) {
- ret = -ENODEV;
- goto err_priv;
+ if (dev->of_node) {
+ struct device_node *ssp_handle;
+
+ ssp_handle = of_parse_phandle(dev->of_node, "port", 0);
+ if (!ssp_handle) {
+ dev_err(dev, "unable to get 'port' phandle\n");
+ return -ENODEV;
+ }
+
+ priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio");
+ if (priv->ssp == NULL) {
+ ret = -ENODEV;
+ goto err_priv;
+ }
+ } else {
+ priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
+ if (priv->ssp == NULL) {
+ ret = -ENODEV;
+ goto err_priv;
+ }
}
priv->dai_fmt = (unsigned int) -1;
@@ -798,6 +807,12 @@ static const struct snd_soc_component_driver pxa_ssp_component = {
.name = "pxa-ssp",
};
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ssp_of_ids[] = {
+ { .compatible = "mrvl,pxa-ssp-dai" },
+};
+#endif
+
static int asoc_ssp_probe(struct platform_device *pdev)
{
return snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
@@ -812,8 +827,9 @@ static int asoc_ssp_remove(struct platform_device *pdev)
static struct platform_driver asoc_ssp_driver = {
.driver = {
- .name = "pxa-ssp-dai",
- .owner = THIS_MODULE,
+ .name = "pxa-ssp-dai",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pxa_ssp_of_ids),
},
.probe = asoc_ssp_probe,
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 1475515712e6..f1059d999de6 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -14,15 +14,16 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
#include <sound/core.h>
#include <sound/ac97_codec.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include <mach/hardware.h>
#include <mach/regs-ac97.h>
-#include <mach/dma.h>
#include <mach/audio.h>
#include "pxa2xx-ac97.h"
@@ -48,44 +49,44 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_cold_reset,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
- .name = "AC97 PCM Stereo out",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(12),
- .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
- DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
+ .addr = __PREG(PCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .maxburst = 32,
+ .filter_data = &pxa2xx_ac97_pcm_stereo_in_req,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {
- .name = "AC97 PCM Stereo in",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(11),
- .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
- DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
+ .addr = __PREG(PCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .maxburst = 32,
+ .filter_data = &pxa2xx_ac97_pcm_stereo_out_req,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {
- .name = "AC97 Aux PCM (Slot 5) Mono out",
- .dev_addr = __PREG(MODR),
- .drcmr = &DRCMR(10),
- .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
- DCMD_BURST16 | DCMD_WIDTH2,
+static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
+ .addr = __PREG(MODR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .maxburst = 16,
+ .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {
- .name = "AC97 Aux PCM (Slot 5) Mono in",
- .dev_addr = __PREG(MODR),
- .drcmr = &DRCMR(9),
- .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
- DCMD_BURST16 | DCMD_WIDTH2,
+static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
+ .addr = __PREG(MODR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .maxburst = 16,
+ .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
- .name = "AC97 Mic PCM (Slot 6) Mono in",
- .dev_addr = __PREG(MCDR),
- .drcmr = &DRCMR(8),
- .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
- DCMD_BURST16 | DCMD_WIDTH2,
+static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
+ .addr = __PREG(MCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .maxburst = 16,
+ .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req,
};
#ifdef CONFIG_PM
@@ -119,7 +120,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
- struct pxa2xx_pcm_dma_params *dma_data;
+ struct snd_dmaengine_dai_dma_data *dma_data;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dma_data = &pxa2xx_ac97_pcm_stereo_out;
@@ -135,7 +136,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
- struct pxa2xx_pcm_dma_params *dma_data;
+ struct snd_dmaengine_dai_dma_data *dma_data;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index f7ca71664112..d5340a088858 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -23,9 +23,9 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include <mach/hardware.h>
-#include <mach/dma.h>
#include <mach/audio.h>
#include "pxa2xx-i2s.h"
@@ -82,20 +82,20 @@ static struct pxa_i2s_port pxa_i2s;
static struct clk *clk_i2s;
static int clk_ena = 0;
-static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
- .name = "I2S PCM Stereo out",
- .dev_addr = __PREG(SADR),
- .drcmr = &DRCMR(3),
- .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
- DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3;
+static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = {
+ .addr = __PREG(SADR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .maxburst = 32,
+ .filter_data = &pxa2xx_i2s_pcm_stereo_out_req,
};
-static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
- .name = "I2S PCM Stereo in",
- .dev_addr = __PREG(SADR),
- .drcmr = &DRCMR(2),
- .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
- DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2;
+static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = {
+ .addr = __PREG(SADR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .maxburst = 32,
+ .filter_data = &pxa2xx_i2s_pcm_stereo_in_req,
};
static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
@@ -163,7 +163,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct pxa2xx_pcm_dma_params *dma_data;
+ struct snd_dmaengine_dai_dma_data *dma_data;
BUG_ON(IS_ERR(clk_i2s));
clk_prepare_enable(clk_i2s);
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index ecff116cb7b0..806da27b8b67 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -12,10 +12,13 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include "../../arm/pxa2xx-pcm.h"
@@ -25,7 +28,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct pxa2xx_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct pxa2xx_pcm_dma_params *dma;
+ struct snd_dmaengine_dai_dma_data *dma;
int ret;
dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
@@ -39,7 +42,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
* with different params */
if (prtd->params == NULL) {
prtd->params = dma;
- ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
+ ret = pxa_request_dma("name", DMA_PRIO_LOW,
pxa2xx_pcm_dma_irq, substream);
if (ret < 0)
return ret;
@@ -47,7 +50,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
} else if (prtd->params != dma) {
pxa_free_dma(prtd->dma_ch);
prtd->params = dma;
- ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
+ ret = pxa_request_dma("name", DMA_PRIO_LOW,
pxa2xx_pcm_dma_irq, substream);
if (ret < 0)
return ret;
@@ -131,10 +134,18 @@ static int pxa2xx_soc_platform_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id snd_soc_pxa_audio_match[] = {
+ { .compatible = "mrvl,pxa-pcm-audio" },
+ { }
+};
+#endif
+
static struct platform_driver pxa_pcm_driver = {
.driver = {
- .name = "pxa-pcm-audio",
- .owner = THIS_MODULE,
+ .name = "pxa-pcm-audio",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(snd_soc_pxa_audio_match),
},
.probe = pxa2xx_soc_platform_probe,
diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c
index f4ea4f6663a2..13c9ee0cb83b 100644
--- a/sound/soc/pxa/ttc-dkb.c
+++ b/sound/soc/pxa/ttc-dkb.c
@@ -122,6 +122,7 @@ static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = {
/* ttc/td audio machine driver */
static struct snd_soc_card ttc_dkb_card = {
.name = "ttc-dkb-hifi",
+ .owner = THIS_MODULE,
.dai_link = ttc_pm860x_hifi_dai,
.num_links = ARRAY_SIZE(ttc_pm860x_hifi_dai),
diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c
index 58cfb1eb7dd3..945e8abdc10f 100644
--- a/sound/soc/s6000/s6105-ipcam.c
+++ b/sound/soc/s6000/s6105-ipcam.c
@@ -192,7 +192,7 @@ static struct snd_soc_card snd_soc_card_s6105 = {
.num_links = 1,
};
-static struct s6000_snd_platform_data __initdata s6105_snd_data = {
+static struct s6000_snd_platform_data s6105_snd_data __initdata = {
.wide = 0,
.channel_in = 0,
.channel_out = 1,
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index 2dd623fa3882..2acf987844e8 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -404,18 +404,13 @@ static int s3c_ac97_probe(struct platform_device *pdev)
return -ENXIO;
}
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem_res) {
- dev_err(&pdev->dev, "Unable to get register resource\n");
- return -ENXIO;
- }
-
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res) {
dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
return -ENXIO;
}
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(s3c_ac97.regs))
return PTR_ERR(s3c_ac97.regs);
@@ -462,7 +457,7 @@ static int s3c_ac97_probe(struct platform_device *pdev)
if (ret)
goto err5;
- ret = asoc_dma_platform_register(&pdev->dev);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
goto err6;
@@ -485,7 +480,7 @@ static int s3c_ac97_remove(struct platform_device *pdev)
{
struct resource *irq_res;
- asoc_dma_platform_unregister(&pdev->dev);
+ samsung_asoc_dma_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index 21b79262010e..9338d11e9216 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -90,6 +90,13 @@ static void dma_enqueue(struct snd_pcm_substream *substream)
dma_info.period = prtd->dma_period;
dma_info.len = prtd->dma_period*limit;
+ if (dma_info.cap == DMA_CYCLIC) {
+ dma_info.buf = pos;
+ prtd->params->ops->prepare(prtd->params->ch, &dma_info);
+ prtd->dma_loaded += limit;
+ return;
+ }
+
while (prtd->dma_loaded < limit) {
pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
@@ -176,6 +183,10 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
prtd->params->ch = prtd->params->ops->request(
prtd->params->channel, &req, rtd->cpu_dai->dev,
prtd->params->ch_name);
+ if (!prtd->params->ch) {
+ pr_err("Failed to allocate DMA channel\n");
+ return -ENXIO;
+ }
prtd->params->ops->config(prtd->params->ch, &config);
}
@@ -433,17 +444,17 @@ static struct snd_soc_platform_driver samsung_asoc_platform = {
.pcm_free = dma_free_dma_buffers,
};
-int asoc_dma_platform_register(struct device *dev)
+int samsung_asoc_dma_platform_register(struct device *dev)
{
return snd_soc_register_platform(dev, &samsung_asoc_platform);
}
-EXPORT_SYMBOL_GPL(asoc_dma_platform_register);
+EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
-void asoc_dma_platform_unregister(struct device *dev)
+void samsung_asoc_dma_platform_unregister(struct device *dev)
{
snd_soc_unregister_platform(dev);
}
-EXPORT_SYMBOL_GPL(asoc_dma_platform_unregister);
+EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index 189a7a6d5020..0e86315a3eaf 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -22,7 +22,7 @@ struct s3c_dma_params {
char *ch_name;
};
-int asoc_dma_platform_register(struct device *dev);
-void asoc_dma_platform_unregister(struct device *dev);
+int samsung_asoc_dma_platform_register(struct device *dev);
+void samsung_asoc_dma_platform_unregister(struct device *dev);
#endif
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
index c0e6d9a19efc..821a50231002 100644
--- a/sound/soc/samsung/i2s-regs.h
+++ b/sound/soc/samsung/i2s-regs.h
@@ -31,6 +31,10 @@
#define I2SLVL1ADDR 0x34
#define I2SLVL2ADDR 0x38
#define I2SLVL3ADDR 0x3c
+#define I2SSTR1 0x40
+#define I2SVER 0x44
+#define I2SFIC2 0x48
+#define I2STDM 0x4c
#define CON_RSTCLR (1 << 31)
#define CON_FRXOFSTATUS (1 << 26)
@@ -95,24 +99,39 @@
#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_LRP_SHIFT 7
+#define MOD_LR_LLOW 0
+#define MOD_LR_RLOW 1
+#define MOD_SDF_SHIFT 5
+#define MOD_SDF_IIS 0
+#define MOD_SDF_MSB 1
+#define MOD_SDF_LSB 2
+#define MOD_SDF_MASK 3
+#define MOD_RCLK_SHIFT 3
+#define MOD_RCLK_256FS 0
+#define MOD_RCLK_512FS 1
+#define MOD_RCLK_384FS 2
+#define MOD_RCLK_768FS 3
+#define MOD_RCLK_MASK 3
+#define MOD_BCLK_SHIFT 1
+#define MOD_BCLK_32FS 0
+#define MOD_BCLK_48FS 1
+#define MOD_BCLK_16FS 2
+#define MOD_BCLK_24FS 3
+#define MOD_BCLK_MASK 3
#define MOD_8BIT (1 << 0)
+#define EXYNOS5420_MOD_LRP_SHIFT 15
+#define EXYNOS5420_MOD_SDF_SHIFT 6
+#define EXYNOS5420_MOD_RCLK_SHIFT 4
+#define EXYNOS5420_MOD_BCLK_SHIFT 0
+#define EXYNOS5420_MOD_BCLK_64FS 4
+#define EXYNOS5420_MOD_BCLK_96FS 5
+#define EXYNOS5420_MOD_BCLK_128FS 6
+#define EXYNOS5420_MOD_BCLK_192FS 7
+#define EXYNOS5420_MOD_BCLK_256FS 8
+#define EXYNOS5420_MOD_BCLK_MASK 0xf
+
#define MOD_CDCLKCON (1 << 12)
#define PSR_PSREN (1 << 15)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 959c702235c8..b302f3b7a587 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -40,6 +40,7 @@ enum samsung_dai_type {
struct samsung_i2s_dai_data {
int dai_type;
+ u32 quirks;
};
struct i2s_dai {
@@ -198,7 +199,13 @@ static inline bool is_manager(struct i2s_dai *i2s)
/* Read RCLK of I2S (in multiples of LRCLK) */
static inline unsigned get_rfs(struct i2s_dai *i2s)
{
- u32 rfs = (readl(i2s->addr + I2SMOD) >> 3) & 0x3;
+ u32 rfs;
+
+ if (i2s->quirks & QUIRK_SUPPORTS_TDM)
+ rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT;
+ else
+ rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
+ rfs &= MOD_RCLK_MASK;
switch (rfs) {
case 3: return 768;
@@ -212,21 +219,26 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
{
u32 mod = readl(i2s->addr + I2SMOD);
+ int rfs_shift;
- mod &= ~MOD_RCLK_MASK;
+ if (i2s->quirks & QUIRK_SUPPORTS_TDM)
+ rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT;
+ else
+ rfs_shift = MOD_RCLK_SHIFT;
+ mod &= ~(MOD_RCLK_MASK << rfs_shift);
switch (rfs) {
case 768:
- mod |= MOD_RCLK_768FS;
+ mod |= (MOD_RCLK_768FS << rfs_shift);
break;
case 512:
- mod |= MOD_RCLK_512FS;
+ mod |= (MOD_RCLK_512FS << rfs_shift);
break;
case 384:
- mod |= MOD_RCLK_384FS;
+ mod |= (MOD_RCLK_384FS << rfs_shift);
break;
default:
- mod |= MOD_RCLK_256FS;
+ mod |= (MOD_RCLK_256FS << rfs_shift);
break;
}
@@ -236,9 +248,22 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
/* Read Bit-Clock of I2S (in multiples of LRCLK) */
static inline unsigned get_bfs(struct i2s_dai *i2s)
{
- u32 bfs = (readl(i2s->addr + I2SMOD) >> 1) & 0x3;
+ u32 bfs;
+
+ if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
+ bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT;
+ bfs &= EXYNOS5420_MOD_BCLK_MASK;
+ } else {
+ bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT;
+ bfs &= MOD_BCLK_MASK;
+ }
switch (bfs) {
+ case 8: return 256;
+ case 7: return 192;
+ case 6: return 128;
+ case 5: return 96;
+ case 4: return 64;
case 3: return 24;
case 2: return 16;
case 1: return 48;
@@ -250,21 +275,50 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
{
u32 mod = readl(i2s->addr + I2SMOD);
+ int bfs_shift;
+ int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
- mod &= ~MOD_BCLK_MASK;
+ if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
+ bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT;
+ mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift);
+ } else {
+ bfs_shift = MOD_BCLK_SHIFT;
+ mod &= ~(MOD_BCLK_MASK << bfs_shift);
+ }
+
+ /* Non-TDM I2S controllers do not support BCLK > 48 * FS */
+ if (!tdm && bfs > 48) {
+ dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n");
+ return;
+ }
switch (bfs) {
case 48:
- mod |= MOD_BCLK_48FS;
+ mod |= (MOD_BCLK_48FS << bfs_shift);
break;
case 32:
- mod |= MOD_BCLK_32FS;
+ mod |= (MOD_BCLK_32FS << bfs_shift);
break;
case 24:
- mod |= MOD_BCLK_24FS;
+ mod |= (MOD_BCLK_24FS << bfs_shift);
break;
case 16:
- mod |= MOD_BCLK_16FS;
+ mod |= (MOD_BCLK_16FS << bfs_shift);
+ break;
+ case 64:
+ mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift);
+ break;
+ case 96:
+ mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift);
+ break;
+ case 128:
+ mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift);
+ break;
+ case 192:
+ mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift);
+ break;
+ case 256:
+ mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift);
break;
default:
dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n");
@@ -491,20 +545,32 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
{
struct i2s_dai *i2s = to_info(dai);
u32 mod = readl(i2s->addr + I2SMOD);
+ int lrp_shift, sdf_shift, sdf_mask, lrp_rlow;
u32 tmp = 0;
+ if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
+ lrp_shift = EXYNOS5420_MOD_LRP_SHIFT;
+ sdf_shift = EXYNOS5420_MOD_SDF_SHIFT;
+ } else {
+ lrp_shift = MOD_LRP_SHIFT;
+ sdf_shift = MOD_SDF_SHIFT;
+ }
+
+ sdf_mask = MOD_SDF_MASK << sdf_shift;
+ lrp_rlow = MOD_LR_RLOW << lrp_shift;
+
/* Format is priority */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
- tmp |= MOD_LR_RLOW;
- tmp |= MOD_SDF_MSB;
+ tmp |= lrp_rlow;
+ tmp |= (MOD_SDF_MSB << sdf_shift);
break;
case SND_SOC_DAIFMT_LEFT_J:
- tmp |= MOD_LR_RLOW;
- tmp |= MOD_SDF_LSB;
+ tmp |= lrp_rlow;
+ tmp |= (MOD_SDF_LSB << sdf_shift);
break;
case SND_SOC_DAIFMT_I2S:
- tmp |= MOD_SDF_IIS;
+ tmp |= (MOD_SDF_IIS << sdf_shift);
break;
default:
dev_err(&i2s->pdev->dev, "Format not supported\n");
@@ -519,10 +585,10 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
- if (tmp & MOD_LR_RLOW)
- tmp &= ~MOD_LR_RLOW;
+ if (tmp & lrp_rlow)
+ tmp &= ~lrp_rlow;
else
- tmp |= MOD_LR_RLOW;
+ tmp |= lrp_rlow;
break;
default:
dev_err(&i2s->pdev->dev, "Polarity not supported\n");
@@ -544,15 +610,18 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
+ /*
+ * Don't change the I2S mode if any controller is active on this
+ * channel.
+ */
if (any_active(i2s) &&
- ((mod & (MOD_SDF_MASK | MOD_LR_RLOW
- | MOD_SLAVE)) != tmp)) {
+ ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
}
- mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE);
+ mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE);
mod |= tmp;
writel(mod, i2s->addr + I2SMOD);
@@ -1007,6 +1076,8 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
if (IS_ERR(i2s->pdev))
return NULL;
+ i2s->pdev->dev.parent = &pdev->dev;
+
platform_set_drvdata(i2s->pdev, i2s);
ret = platform_device_add(i2s->pdev);
if (ret < 0)
@@ -1018,18 +1089,18 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
static const struct of_device_id exynos_i2s_match[];
-static inline int samsung_i2s_get_driver_data(struct platform_device *pdev)
+static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
+ struct platform_device *pdev)
{
#ifdef CONFIG_OF
- struct samsung_i2s_dai_data *data;
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
- data = (struct samsung_i2s_dai_data *) match->data;
- return data->dai_type;
+ return match->data;
} else
#endif
- return platform_get_device_id(pdev)->driver_data;
+ return (struct samsung_i2s_dai_data *)
+ platform_get_device_id(pdev)->driver_data;
}
#ifdef CONFIG_PM_RUNTIME
@@ -1060,13 +1131,13 @@ static int samsung_i2s_probe(struct platform_device *pdev)
struct resource *res;
u32 regs_base, quirks = 0, idma_addr = 0;
struct device_node *np = pdev->dev.of_node;
- enum samsung_dai_type samsung_dai_type;
+ const struct samsung_i2s_dai_data *i2s_dai_data;
int ret = 0;
/* Call during Seconday interface registration */
- samsung_dai_type = samsung_i2s_get_driver_data(pdev);
+ i2s_dai_data = samsung_i2s_get_driver_data(pdev);
- if (samsung_dai_type == TYPE_SEC) {
+ if (i2s_dai_data->dai_type == TYPE_SEC) {
sec_dai = dev_get_drvdata(&pdev->dev);
if (!sec_dai) {
dev_err(&pdev->dev, "Unable to get drvdata\n");
@@ -1075,7 +1146,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
snd_soc_register_component(&sec_dai->pdev->dev,
&samsung_i2s_component,
&sec_dai->i2s_dai_drv, 1);
- asoc_dma_platform_register(&pdev->dev);
+ samsung_asoc_dma_platform_register(&pdev->dev);
return 0;
}
@@ -1115,15 +1186,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
idma_addr = i2s_cfg->idma_addr;
}
} else {
- if (of_find_property(np, "samsung,supports-6ch", NULL))
- quirks |= QUIRK_PRI_6CHAN;
-
- if (of_find_property(np, "samsung,supports-secdai", NULL))
- quirks |= QUIRK_SEC_DAI;
-
- if (of_find_property(np, "samsung,supports-rstclr", NULL))
- quirks |= QUIRK_NEED_RSTCLR;
-
+ quirks = i2s_dai_data->quirks;
if (of_property_read_u32(np, "samsung,idma-addr",
&idma_addr)) {
if (quirks & QUIRK_SEC_DAI) {
@@ -1200,7 +1263,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
- asoc_dma_platform_register(&pdev->dev);
+ samsung_asoc_dma_platform_register(&pdev->dev);
return 0;
err:
@@ -1230,33 +1293,59 @@ static int samsung_i2s_remove(struct platform_device *pdev)
i2s->pri_dai = NULL;
i2s->sec_dai = NULL;
- asoc_dma_platform_unregister(&pdev->dev);
+ samsung_asoc_dma_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
return 0;
}
+static const struct samsung_i2s_dai_data i2sv3_dai_type = {
+ .dai_type = TYPE_PRI,
+ .quirks = QUIRK_NO_MUXPSR,
+};
+
+static const struct samsung_i2s_dai_data i2sv5_dai_type = {
+ .dai_type = TYPE_PRI,
+ .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
+};
+
+static const struct samsung_i2s_dai_data i2sv6_dai_type = {
+ .dai_type = TYPE_PRI,
+ .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+ QUIRK_SUPPORTS_TDM,
+};
+
+static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
+ .dai_type = TYPE_PRI,
+};
+
+static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
+ .dai_type = TYPE_SEC,
+};
+
static struct platform_device_id samsung_i2s_driver_ids[] = {
{
.name = "samsung-i2s",
- .driver_data = TYPE_PRI,
+ .driver_data = (kernel_ulong_t)&samsung_dai_type_pri,
}, {
.name = "samsung-i2s-sec",
- .driver_data = TYPE_SEC,
+ .driver_data = (kernel_ulong_t)&samsung_dai_type_sec,
},
{},
};
MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids);
#ifdef CONFIG_OF
-static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = {
- [TYPE_PRI] = { TYPE_PRI },
- [TYPE_SEC] = { TYPE_SEC },
-};
-
static const struct of_device_id exynos_i2s_match[] = {
- { .compatible = "samsung,i2s-v5",
- .data = &samsung_i2s_dai_data_array[TYPE_PRI],
+ {
+ .compatible = "samsung,s3c6410-i2s",
+ .data = &i2sv3_dai_type,
+ }, {
+ .compatible = "samsung,s5pv210-i2s",
+ .data = &i2sv5_dai_type,
+ }, {
+ .compatible = "samsung,exynos5420-i2s",
+ .data = &i2sv6_dai_type,
},
{},
};
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index 1566afe9ef52..e54256fc4b2c 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -594,7 +594,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
goto err5;
}
- ret = asoc_dma_platform_register(&pdev->dev);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
goto err6;
@@ -623,7 +623,7 @@ static int s3c_pcm_dev_remove(struct platform_device *pdev)
struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
struct resource *mem_res;
- asoc_dma_platform_unregister(&pdev->dev);
+ samsung_asoc_dma_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index 47e23864ea72..ea885cb9f76c 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -176,7 +176,7 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
return ret;
}
- ret = asoc_dma_platform_register(&pdev->dev);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev);
if (ret) {
pr_err("failed to register the DMA: %d\n", ret);
goto err;
@@ -190,7 +190,7 @@ err:
static int s3c2412_iis_dev_remove(struct platform_device *pdev)
{
- asoc_dma_platform_unregister(&pdev->dev);
+ samsung_asoc_dma_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
return 0;
}
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index 8b3414551a62..9c8ebd872fac 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -480,7 +480,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
return ret;
}
- ret = asoc_dma_platform_register(&pdev->dev);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev);
if (ret) {
pr_err("failed to register the dma: %d\n", ret);
goto err;
@@ -494,7 +494,7 @@ err:
static int s3c24xx_iis_dev_remove(struct platform_device *pdev)
{
- asoc_dma_platform_unregister(&pdev->dev);
+ samsung_asoc_dma_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
return 0;
}
diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index 581ea4a06fc6..5fd7a05a9b9e 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -11,6 +11,7 @@
#include <sound/pcm_params.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
/*
* Default CFG switch settings to use this driver:
@@ -37,11 +38,19 @@
/* SMDK has a 16.934MHZ crystal attached to WM8994 */
#define SMDK_WM8994_FREQ 16934000
+struct smdk_wm8994_data {
+ int mclk1_rate;
+};
+
+/* Default SMDKs */
+static struct smdk_wm8994_data smdk_board_data = {
+ .mclk1_rate = SMDK_WM8994_FREQ,
+};
+
static int smdk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int pll_out;
int ret;
@@ -54,18 +63,6 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
else
pll_out = params_rate(params) * 256;
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
SMDK_WM8994_FREQ, pll_out);
if (ret < 0)
@@ -131,6 +128,8 @@ static struct snd_soc_dai_link smdk_dai[] = {
.platform_name = "samsung-i2s.0",
.codec_name = "wm8994-codec",
.init = smdk_wm8994_init_paiftx,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.ops = &smdk_ops,
}, { /* Sec_Fifo Playback i/f */
.name = "Sec_FIFO TX",
@@ -139,6 +138,8 @@ static struct snd_soc_dai_link smdk_dai[] = {
.codec_dai_name = "wm8994-aif1",
.platform_name = "samsung-i2s-sec",
.codec_name = "wm8994-codec",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.ops = &smdk_ops,
},
};
@@ -150,15 +151,28 @@ static struct snd_soc_card smdk = {
.num_links = ARRAY_SIZE(smdk_dai),
};
+#ifdef CONFIG_OF
+static const struct of_device_id samsung_wm8994_of_match[] = {
+ { .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
+#endif /* CONFIG_OF */
static int smdk_audio_probe(struct platform_device *pdev)
{
int ret;
struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &smdk;
+ struct smdk_wm8994_data *board;
+ const struct of_device_id *id;
card->dev = &pdev->dev;
+ board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+ if (!board)
+ return -ENOMEM;
+
if (np) {
smdk_dai[0].cpu_dai_name = NULL;
smdk_dai[0].cpu_of_node = of_parse_phandle(np,
@@ -173,6 +187,12 @@ static int smdk_audio_probe(struct platform_device *pdev)
smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node;
}
+ id = of_match_device(samsung_wm8994_of_match, &pdev->dev);
+ if (id)
+ *board = *((struct smdk_wm8994_data *)id->data);
+
+ platform_set_drvdata(pdev, board);
+
ret = snd_soc_register_card(card);
if (ret)
@@ -190,17 +210,9 @@ static int smdk_audio_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_OF
-static const struct of_device_id samsung_wm8994_of_match[] = {
- { .compatible = "samsung,smdk-wm8994", },
- {},
-};
-MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
-#endif /* CONFIG_OF */
-
static struct platform_driver smdk_audio_driver = {
.driver = {
- .name = "smdk-audio",
+ .name = "smdk-audio-wm8894",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(samsung_wm8994_of_match),
},
@@ -212,4 +224,4 @@ module_platform_driver(smdk_audio_driver);
MODULE_DESCRIPTION("ALSA SoC SMDK WM8994");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:smdk-audio");
+MODULE_ALIAS("platform:smdk-audio-wm8994");
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index 2e5ebb2f1982..28487dcc4538 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -395,7 +395,7 @@ static int spdif_probe(struct platform_device *pdev)
spin_lock_init(&spdif->lock);
- spdif->pclk = clk_get(&pdev->dev, "spdif");
+ spdif->pclk = devm_clk_get(&pdev->dev, "spdif");
if (IS_ERR(spdif->pclk)) {
dev_err(&pdev->dev, "failed to get peri-clock\n");
ret = -ENOENT;
@@ -403,7 +403,7 @@ static int spdif_probe(struct platform_device *pdev)
}
clk_prepare_enable(spdif->pclk);
- spdif->sclk = clk_get(&pdev->dev, "sclk_spdif");
+ spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif");
if (IS_ERR(spdif->sclk)) {
dev_err(&pdev->dev, "failed to get internal source clock\n");
ret = -ENOENT;
@@ -442,7 +442,7 @@ static int spdif_probe(struct platform_device *pdev)
spdif->dma_playback = &spdif_stereo_out;
- ret = asoc_dma_platform_register(&pdev->dev);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
goto err5;
@@ -457,10 +457,8 @@ err3:
release_mem_region(mem_res->start, resource_size(mem_res));
err2:
clk_disable_unprepare(spdif->sclk);
- clk_put(spdif->sclk);
err1:
clk_disable_unprepare(spdif->pclk);
- clk_put(spdif->pclk);
err0:
return ret;
}
@@ -470,7 +468,7 @@ static int spdif_remove(struct platform_device *pdev)
struct samsung_spdif_info *spdif = &spdif_info;
struct resource *mem_res;
- asoc_dma_platform_unregister(&pdev->dev);
+ samsung_asoc_dma_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
iounmap(spdif->regs);
@@ -480,9 +478,7 @@ static int spdif_remove(struct platform_device *pdev)
release_mem_region(mem_res->start, resource_size(mem_res));
clk_disable_unprepare(spdif->sclk);
- clk_put(spdif->sclk);
clk_disable_unprepare(spdif->pclk);
- clk_put(spdif->pclk);
return 0;
}
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 6bcb1164d599..56d8ff6a402d 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU
select SH_DMAE
select FW_LOADER
+config SND_SOC_RCAR
+ tristate "R-Car series SRU/SCU/SSIU/SSI support"
+ select SND_SIMPLE_CARD
+ select RCAR_CLK_ADG
+ help
+ This option enables R-Car SUR/SCU/SSIU/SSI sound support
+
##
## Boards
##
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
index 849b387d17d9..aaf3dcd1ee2a 100644
--- a/sound/soc/sh/Makefile
+++ b/sound/soc/sh/Makefile
@@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o
obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o
obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o
+## audio units for R-Car
+obj-$(CONFIG_SND_SOC_RCAR) += rcar/
+
## boards
snd-soc-sh7760-ac97-objs := sh7760-ac97.o
snd-soc-migor-objs := migor.o
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 30390260bb67..b33ca7cd085b 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -235,6 +235,8 @@ struct fsi_stream {
struct sh_dmae_slave slave; /* see fsi_handler_init() */
struct work_struct work;
dma_addr_t dma;
+ int loop_cnt;
+ int additional_pos;
};
struct fsi_clk {
@@ -1289,6 +1291,8 @@ static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io)
io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+ io->loop_cnt = 2; /* push 1st, 2nd period first, then 3rd, 4th... */
+ io->additional_pos = 0;
io->dma = dma_map_single(dai->dev, runtime->dma_area,
snd_pcm_lib_buffer_bytes(io->substream), dir);
return 0;
@@ -1305,11 +1309,15 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io)
return 0;
}
-static dma_addr_t fsi_dma_get_area(struct fsi_stream *io)
+static dma_addr_t fsi_dma_get_area(struct fsi_stream *io, int additional)
{
struct snd_pcm_runtime *runtime = io->substream->runtime;
+ int period = io->period_pos + additional;
- return io->dma + samples_to_bytes(runtime, io->buff_sample_pos);
+ if (period >= runtime->periods)
+ period = 0;
+
+ return io->dma + samples_to_bytes(runtime, period * io->period_samples);
}
static void fsi_dma_complete(void *data)
@@ -1321,7 +1329,7 @@ static void fsi_dma_complete(void *data)
enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE;
- dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io),
+ dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io, 0),
samples_to_bytes(runtime, io->period_samples), dir);
io->buff_sample_pos += io->period_samples;
@@ -1347,7 +1355,7 @@ static void fsi_dma_do_work(struct work_struct *work)
struct snd_pcm_runtime *runtime;
enum dma_data_direction dir;
int is_play = fsi_stream_is_play(fsi, io);
- int len;
+ int len, i;
dma_addr_t buf;
if (!fsi_stream_is_working(fsi, io))
@@ -1357,26 +1365,33 @@ static void fsi_dma_do_work(struct work_struct *work)
runtime = io->substream->runtime;
dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
len = samples_to_bytes(runtime, io->period_samples);
- buf = fsi_dma_get_area(io);
- dma_sync_single_for_device(dai->dev, buf, len, dir);
+ for (i = 0; i < io->loop_cnt; i++) {
+ buf = fsi_dma_get_area(io, io->additional_pos);
- desc = dmaengine_prep_slave_single(io->chan, buf, len, dir,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc) {
- dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
- return;
- }
+ dma_sync_single_for_device(dai->dev, buf, len, dir);
- desc->callback = fsi_dma_complete;
- desc->callback_param = io;
+ desc = dmaengine_prep_slave_single(io->chan, buf, len, dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
+ return;
+ }
- if (dmaengine_submit(desc) < 0) {
- dev_err(dai->dev, "tx_submit() fail\n");
- return;
+ desc->callback = fsi_dma_complete;
+ desc->callback_param = io;
+
+ if (dmaengine_submit(desc) < 0) {
+ dev_err(dai->dev, "tx_submit() fail\n");
+ return;
+ }
+
+ dma_async_issue_pending(io->chan);
+
+ io->additional_pos = 1;
}
- dma_async_issue_pending(io->chan);
+ io->loop_cnt = 1;
/*
* FIXME
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
new file mode 100644
index 000000000000..0ff492df7929
--- /dev/null
+++ b/sound/soc/sh/rcar/Makefile
@@ -0,0 +1,2 @@
+snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o
+obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
new file mode 100644
index 000000000000..d80deb7ccf13
--- /dev/null
+++ b/sound/soc/sh/rcar/adg.c
@@ -0,0 +1,234 @@
+/*
+ * Helper routines for R-Car sound ADG.
+ *
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/sh_clk.h>
+#include <mach/clock.h>
+#include "rsnd.h"
+
+#define CLKA 0
+#define CLKB 1
+#define CLKC 2
+#define CLKI 3
+#define CLKMAX 4
+
+struct rsnd_adg {
+ struct clk *clk[CLKMAX];
+
+ int rate_of_441khz_div_6;
+ int rate_of_48khz_div_6;
+};
+
+#define for_each_rsnd_clk(pos, adg, i) \
+ for (i = 0, (pos) = adg->clk[i]; \
+ i < CLKMAX; \
+ i++, (pos) = adg->clk[i])
+#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+
+static enum rsnd_reg rsnd_adg_ssi_reg_get(int id)
+{
+ enum rsnd_reg reg;
+
+ /*
+ * SSI 8 is not connected to ADG.
+ * it works with SSI 7
+ */
+ if (id == 8)
+ return RSND_REG_MAX;
+
+ if (0 <= id && id <= 3)
+ reg = RSND_REG_AUDIO_CLK_SEL0;
+ else if (4 <= id && id <= 7)
+ reg = RSND_REG_AUDIO_CLK_SEL1;
+ else
+ reg = RSND_REG_AUDIO_CLK_SEL2;
+
+ return reg;
+}
+
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ enum rsnd_reg reg;
+ int id;
+
+ /*
+ * "mod" = "ssi" here.
+ * we can get "ssi id" from mod
+ */
+ id = rsnd_mod_id(mod);
+ reg = rsnd_adg_ssi_reg_get(id);
+
+ rsnd_write(priv, mod, reg, 0);
+
+ return 0;
+}
+
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ enum rsnd_reg reg;
+ int id, shift, i;
+ u32 data;
+ int sel_table[] = {
+ [CLKA] = 0x1,
+ [CLKB] = 0x2,
+ [CLKC] = 0x3,
+ [CLKI] = 0x0,
+ };
+
+ dev_dbg(dev, "request clock = %d\n", rate);
+
+ /*
+ * find suitable clock from
+ * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
+ */
+ data = 0;
+ for_each_rsnd_clk(clk, adg, i) {
+ if (rate == clk_get_rate(clk)) {
+ data = sel_table[i];
+ goto found_clock;
+ }
+ }
+
+ /*
+ * find 1/6 clock from BRGA/BRGB
+ */
+ if (rate == adg->rate_of_441khz_div_6) {
+ data = 0x10;
+ goto found_clock;
+ }
+
+ if (rate == adg->rate_of_48khz_div_6) {
+ data = 0x20;
+ goto found_clock;
+ }
+
+ return -EIO;
+
+found_clock:
+
+ /*
+ * This "mod" = "ssi" here.
+ * we can get "ssi id" from mod
+ */
+ id = rsnd_mod_id(mod);
+ reg = rsnd_adg_ssi_reg_get(id);
+
+ dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate);
+
+ /*
+ * Enable SSIx clock
+ */
+ shift = (id % 4) * 8;
+
+ rsnd_bset(priv, mod, reg,
+ 0xFF << shift,
+ data << shift);
+
+ return 0;
+}
+
+static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
+{
+ struct clk *clk;
+ unsigned long rate;
+ u32 ckr;
+ int i;
+ int brg_table[] = {
+ [CLKA] = 0x0,
+ [CLKB] = 0x1,
+ [CLKC] = 0x4,
+ [CLKI] = 0x2,
+ };
+
+ /*
+ * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
+ * have 44.1kHz or 48kHz base clocks for now.
+ *
+ * SSI itself can divide parent clock by 1/1 - 1/16
+ * So, BRGA outputs 44.1kHz base parent clock 1/32,
+ * and, BRGB outputs 48.0kHz base parent clock 1/32 here.
+ * see
+ * rsnd_adg_ssi_clk_try_start()
+ */
+ ckr = 0;
+ adg->rate_of_441khz_div_6 = 0;
+ adg->rate_of_48khz_div_6 = 0;
+ for_each_rsnd_clk(clk, adg, i) {
+ rate = clk_get_rate(clk);
+
+ if (0 == rate) /* not used */
+ continue;
+
+ /* RBGA */
+ if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) {
+ adg->rate_of_441khz_div_6 = rate / 6;
+ ckr |= brg_table[i] << 20;
+ }
+
+ /* RBGB */
+ if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) {
+ adg->rate_of_48khz_div_6 = rate / 6;
+ ckr |= brg_table[i] << 16;
+ }
+ }
+
+ rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr);
+ rsnd_priv_write(priv, BRRA, 0x00000002); /* 1/6 */
+ rsnd_priv_write(priv, BRRB, 0x00000002); /* 1/6 */
+}
+
+int rsnd_adg_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ int i;
+
+ adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
+ if (!adg) {
+ dev_err(dev, "ADG allocate failed\n");
+ return -ENOMEM;
+ }
+
+ adg->clk[CLKA] = clk_get(NULL, "audio_clk_a");
+ adg->clk[CLKB] = clk_get(NULL, "audio_clk_b");
+ adg->clk[CLKC] = clk_get(NULL, "audio_clk_c");
+ adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal");
+ for_each_rsnd_clk(clk, adg, i) {
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Audio clock failed\n");
+ return -EIO;
+ }
+ }
+
+ rsnd_adg_ssi_clk_init(priv, adg);
+
+ priv->adg = adg;
+
+ dev_dbg(dev, "adg probed\n");
+
+ return 0;
+}
+
+void rsnd_adg_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
+ struct clk *clk;
+ int i;
+
+ for_each_rsnd_clk(clk, adg, i)
+ clk_put(clk);
+}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
new file mode 100644
index 000000000000..a35706028514
--- /dev/null
+++ b/sound/soc/sh/rcar/core.c
@@ -0,0 +1,861 @@
+/*
+ * Renesas R-Car SRU/SCU/SSIU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on fsi.c
+ * Kuninori Morimoto <morimoto.kuninori@renesas.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.
+ */
+
+/*
+ * Renesas R-Car sound device structure
+ *
+ * Gen1
+ *
+ * SRU : Sound Routing Unit
+ * - SRC : Sampling Rate Converter
+ * - CMD
+ * - CTU : Channel Count Conversion Unit
+ * - MIX : Mixer
+ * - DVC : Digital Volume and Mute Function
+ * - SSI : Serial Sound Interface
+ *
+ * Gen2
+ *
+ * SCU : Sampling Rate Converter Unit
+ * - SRC : Sampling Rate Converter
+ * - CMD
+ * - CTU : Channel Count Conversion Unit
+ * - MIX : Mixer
+ * - DVC : Digital Volume and Mute Function
+ * SSIU : Serial Sound Interface Unit
+ * - SSI : Serial Sound Interface
+ */
+
+/*
+ * driver data Image
+ *
+ * rsnd_priv
+ * |
+ * | ** this depends on Gen1/Gen2
+ * |
+ * +- gen
+ * |
+ * | ** these depend on data path
+ * | ** gen and platform data control it
+ * |
+ * +- rdai[0]
+ * | | sru ssiu ssi
+ * | +- playback -> [mod] -> [mod] -> [mod] -> ...
+ * | |
+ * | | sru ssiu ssi
+ * | +- capture -> [mod] -> [mod] -> [mod] -> ...
+ * |
+ * +- rdai[1]
+ * | | sru ssiu ssi
+ * | +- playback -> [mod] -> [mod] -> [mod] -> ...
+ * | |
+ * | | sru ssiu ssi
+ * | +- capture -> [mod] -> [mod] -> [mod] -> ...
+ * ...
+ * |
+ * | ** these control ssi
+ * |
+ * +- ssi
+ * | |
+ * | +- ssi[0]
+ * | +- ssi[1]
+ * | +- ssi[2]
+ * | ...
+ * |
+ * | ** these control scu
+ * |
+ * +- scu
+ * |
+ * +- scu[0]
+ * +- scu[1]
+ * +- scu[2]
+ * ...
+ *
+ *
+ * for_each_rsnd_dai(xx, priv, xx)
+ * rdai[0] => rdai[1] => rdai[2] => ...
+ *
+ * for_each_rsnd_mod(xx, rdai, xx)
+ * [mod] => [mod] => [mod] => ...
+ *
+ * rsnd_dai_call(xxx, fn )
+ * [mod]->fn() -> [mod]->fn() -> [mod]->fn()...
+ *
+ */
+#include <linux/pm_runtime.h>
+#include "rsnd.h"
+
+#define RSND_RATES SNDRV_PCM_RATE_8000_96000
+#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/*
+ * rsnd_platform functions
+ */
+#define rsnd_platform_call(priv, dai, func, param...) \
+ (!(priv->info->func) ? -ENODEV : \
+ priv->info->func(param))
+
+
+/*
+ * basic function
+ */
+u32 rsnd_read(struct rsnd_priv *priv,
+ struct rsnd_mod *mod, enum rsnd_reg reg)
+{
+ void __iomem *base = rsnd_gen_reg_get(priv, mod, reg);
+
+ BUG_ON(!base);
+
+ return ioread32(base);
+}
+
+void rsnd_write(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ enum rsnd_reg reg, u32 data)
+{
+ void __iomem *base = rsnd_gen_reg_get(priv, mod, reg);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ BUG_ON(!base);
+
+ dev_dbg(dev, "w %p : %08x\n", base, data);
+
+ iowrite32(data, base);
+}
+
+void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
+ enum rsnd_reg reg, u32 mask, u32 data)
+{
+ void __iomem *base = rsnd_gen_reg_get(priv, mod, reg);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 val;
+
+ BUG_ON(!base);
+
+ val = ioread32(base);
+ val &= ~mask;
+ val |= data & mask;
+ iowrite32(val, base);
+
+ dev_dbg(dev, "s %p : %08x\n", base, val);
+}
+
+/*
+ * rsnd_mod functions
+ */
+char *rsnd_mod_name(struct rsnd_mod *mod)
+{
+ if (!mod || !mod->ops)
+ return "unknown";
+
+ return mod->ops->name;
+}
+
+void rsnd_mod_init(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_mod_ops *ops,
+ int id)
+{
+ mod->priv = priv;
+ mod->id = id;
+ mod->ops = ops;
+ INIT_LIST_HEAD(&mod->list);
+}
+
+/*
+ * rsnd_dma functions
+ */
+static void rsnd_dma_continue(struct rsnd_dma *dma)
+{
+ /* push next A or B plane */
+ dma->submit_loop = 1;
+ schedule_work(&dma->work);
+}
+
+void rsnd_dma_start(struct rsnd_dma *dma)
+{
+ /* push both A and B plane*/
+ dma->submit_loop = 2;
+ schedule_work(&dma->work);
+}
+
+void rsnd_dma_stop(struct rsnd_dma *dma)
+{
+ dma->submit_loop = 0;
+ cancel_work_sync(&dma->work);
+ dmaengine_terminate_all(dma->chan);
+}
+
+static void rsnd_dma_complete(void *data)
+{
+ struct rsnd_dma *dma = (struct rsnd_dma *)data;
+ struct rsnd_priv *priv = dma->priv;
+ unsigned long flags;
+
+ rsnd_lock(priv, flags);
+
+ dma->complete(dma);
+
+ if (dma->submit_loop)
+ rsnd_dma_continue(dma);
+
+ rsnd_unlock(priv, flags);
+}
+
+static void rsnd_dma_do_work(struct work_struct *work)
+{
+ struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work);
+ struct rsnd_priv *priv = dma->priv;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct dma_async_tx_descriptor *desc;
+ dma_addr_t buf;
+ size_t len;
+ int i;
+
+ for (i = 0; i < dma->submit_loop; i++) {
+
+ if (dma->inquiry(dma, &buf, &len) < 0)
+ return;
+
+ desc = dmaengine_prep_slave_single(
+ dma->chan, buf, len, dma->dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
+ return;
+ }
+
+ desc->callback = rsnd_dma_complete;
+ desc->callback_param = dma;
+
+ if (dmaengine_submit(desc) < 0) {
+ dev_err(dev, "dmaengine_submit() fail\n");
+ return;
+ }
+
+ }
+
+ dma_async_issue_pending(dma->chan);
+}
+
+int rsnd_dma_available(struct rsnd_dma *dma)
+{
+ return !!dma->chan;
+}
+
+static bool rsnd_dma_filter(struct dma_chan *chan, void *param)
+{
+ chan->private = param;
+
+ return true;
+}
+
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
+ int is_play, int id,
+ int (*inquiry)(struct rsnd_dma *dma,
+ dma_addr_t *buf, int *len),
+ int (*complete)(struct rsnd_dma *dma))
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ dma_cap_mask_t mask;
+
+ if (dma->chan) {
+ dev_err(dev, "it already has dma channel\n");
+ return -EIO;
+ }
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ dma->slave.shdma_slave.slave_id = id;
+
+ dma->chan = dma_request_channel(mask, rsnd_dma_filter,
+ &dma->slave.shdma_slave);
+ if (!dma->chan) {
+ dev_err(dev, "can't get dma channel\n");
+ return -EIO;
+ }
+
+ dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ dma->priv = priv;
+ dma->inquiry = inquiry;
+ dma->complete = complete;
+ INIT_WORK(&dma->work, rsnd_dma_do_work);
+
+ return 0;
+}
+
+void rsnd_dma_quit(struct rsnd_priv *priv,
+ struct rsnd_dma *dma)
+{
+ if (dma->chan)
+ dma_release_channel(dma->chan);
+
+ dma->chan = NULL;
+}
+
+/*
+ * rsnd_dai functions
+ */
+#define rsnd_dai_call(rdai, io, fn) \
+({ \
+ struct rsnd_mod *mod, *n; \
+ int ret = 0; \
+ for_each_rsnd_mod(mod, n, io) { \
+ ret = rsnd_mod_call(mod, fn, rdai, io); \
+ if (ret < 0) \
+ break; \
+ } \
+ ret; \
+})
+
+int rsnd_dai_connect(struct rsnd_dai *rdai,
+ struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ if (!mod) {
+ dev_err(dev, "NULL mod\n");
+ return -EIO;
+ }
+
+ if (!list_empty(&mod->list)) {
+ dev_err(dev, "%s%d is not empty\n",
+ rsnd_mod_name(mod),
+ rsnd_mod_id(mod));
+ return -EIO;
+ }
+
+ list_add_tail(&mod->list, &io->head);
+
+ return 0;
+}
+
+int rsnd_dai_disconnect(struct rsnd_mod *mod)
+{
+ list_del_init(&mod->list);
+
+ return 0;
+}
+
+int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
+{
+ int id = rdai - priv->rdai;
+
+ if ((id < 0) || (id >= rsnd_dai_nr(priv)))
+ return -EINVAL;
+
+ return id;
+}
+
+struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id)
+{
+ return priv->rdai + id;
+}
+
+static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
+{
+ struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ return rsnd_dai_get(priv, dai->id);
+}
+
+int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io)
+{
+ return &rdai->playback == io;
+}
+
+/*
+ * rsnd_soc_dai functions
+ */
+int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
+{
+ struct snd_pcm_substream *substream = io->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int pos = io->byte_pos + additional;
+
+ pos %= (runtime->periods * io->byte_per_period);
+
+ return pos;
+}
+
+void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
+{
+ io->byte_pos += byte;
+
+ if (io->byte_pos >= io->next_period_byte) {
+ struct snd_pcm_substream *substream = io->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ io->period_pos++;
+ io->next_period_byte += io->byte_per_period;
+
+ if (io->period_pos >= runtime->periods) {
+ io->byte_pos = 0;
+ io->period_pos = 0;
+ io->next_period_byte = io->byte_per_period;
+ }
+
+ snd_pcm_period_elapsed(substream);
+ }
+}
+
+static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (!list_empty(&io->head))
+ return -EIO;
+
+ INIT_LIST_HEAD(&io->head);
+ io->substream = substream;
+ io->byte_pos = 0;
+ io->period_pos = 0;
+ io->byte_per_period = runtime->period_size *
+ runtime->channels *
+ samples_to_bytes(runtime, 1);
+ io->next_period_byte = io->byte_per_period;
+
+ return 0;
+}
+
+static
+struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ return rtd->cpu_dai;
+}
+
+static
+struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
+ struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return &rdai->playback;
+ else
+ return &rdai->capture;
+}
+
+static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv,
+ rsnd_dai_id(priv, rdai),
+ rsnd_dai_is_play(rdai, io));
+ int ssi_id = rsnd_mod_id(mod);
+ int ret;
+ unsigned long flags;
+
+ rsnd_lock(priv, flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = rsnd_dai_stream_init(io, substream);
+ if (ret < 0)
+ goto dai_trigger_end;
+
+ ret = rsnd_platform_call(priv, dai, start, ssi_id);
+ if (ret < 0)
+ goto dai_trigger_end;
+
+ ret = rsnd_gen_path_init(priv, rdai, io);
+ if (ret < 0)
+ goto dai_trigger_end;
+
+ ret = rsnd_dai_call(rdai, io, init);
+ if (ret < 0)
+ goto dai_trigger_end;
+
+ ret = rsnd_dai_call(rdai, io, start);
+ if (ret < 0)
+ goto dai_trigger_end;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = rsnd_dai_call(rdai, io, stop);
+ if (ret < 0)
+ goto dai_trigger_end;
+
+ ret = rsnd_dai_call(rdai, io, quit);
+ if (ret < 0)
+ goto dai_trigger_end;
+
+ ret = rsnd_gen_path_exit(priv, rdai, io);
+ if (ret < 0)
+ goto dai_trigger_end;
+
+ ret = rsnd_platform_call(priv, dai, stop, ssi_id);
+ if (ret < 0)
+ goto dai_trigger_end;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+dai_trigger_end:
+ rsnd_unlock(priv, flags);
+
+ return ret;
+}
+
+static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rdai->clk_master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ rdai->clk_master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ rdai->bit_clk_inv = 0;
+ rdai->frm_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ rdai->bit_clk_inv = 1;
+ rdai->frm_clk_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ rdai->bit_clk_inv = 1;
+ rdai->frm_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ default:
+ rdai->bit_clk_inv = 0;
+ rdai->frm_clk_inv = 0;
+ break;
+ }
+
+ /* set format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ rdai->sys_delay = 0;
+ rdai->data_alignment = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ rdai->sys_delay = 1;
+ rdai->data_alignment = 0;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ rdai->sys_delay = 1;
+ rdai->data_alignment = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
+ .trigger = rsnd_soc_dai_trigger,
+ .set_fmt = rsnd_soc_dai_set_fmt,
+};
+
+static int rsnd_dai_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv)
+{
+ struct snd_soc_dai_driver *drv;
+ struct rsnd_dai *rdai;
+ struct rsnd_mod *pmod, *cmod;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int dai_nr;
+ int i;
+
+ /* get max dai nr */
+ for (dai_nr = 0; dai_nr < 32; dai_nr++) {
+ pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1);
+ cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0);
+
+ if (!pmod && !cmod)
+ break;
+ }
+
+ if (!dai_nr) {
+ dev_err(dev, "no dai\n");
+ return -EIO;
+ }
+
+ drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL);
+ rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL);
+ if (!drv || !rdai) {
+ dev_err(dev, "dai allocate failed\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dai_nr; i++) {
+
+ pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1);
+ cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0);
+
+ /*
+ * init rsnd_dai
+ */
+ INIT_LIST_HEAD(&rdai[i].playback.head);
+ INIT_LIST_HEAD(&rdai[i].capture.head);
+
+ snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);
+
+ /*
+ * init snd_soc_dai_driver
+ */
+ drv[i].name = rdai[i].name;
+ drv[i].ops = &rsnd_soc_dai_ops;
+ if (pmod) {
+ drv[i].playback.rates = RSND_RATES;
+ drv[i].playback.formats = RSND_FMTS;
+ drv[i].playback.channels_min = 2;
+ drv[i].playback.channels_max = 2;
+ }
+ if (cmod) {
+ drv[i].capture.rates = RSND_RATES;
+ drv[i].capture.formats = RSND_FMTS;
+ drv[i].capture.channels_min = 2;
+ drv[i].capture.channels_max = 2;
+ }
+
+ dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name,
+ pmod ? "play" : " -- ",
+ cmod ? "capture" : " -- ");
+ }
+
+ priv->dai_nr = dai_nr;
+ priv->daidrv = drv;
+ priv->rdai = rdai;
+
+ return 0;
+}
+
+static void rsnd_dai_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+}
+
+/*
+ * pcm ops
+ */
+static struct snd_pcm_hardware rsnd_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = RSND_FMTS,
+ .rates = RSND_RATES,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 256,
+};
+
+static int rsnd_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
+ return ret;
+}
+
+static int rsnd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+ return bytes_to_frames(runtime, io->byte_pos);
+}
+
+static struct snd_pcm_ops rsnd_pcm_ops = {
+ .open = rsnd_pcm_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = rsnd_hw_params,
+ .hw_free = snd_pcm_lib_free_pages,
+ .pointer = rsnd_pointer,
+};
+
+/*
+ * snd_soc_platform
+ */
+
+#define PREALLOC_BUFFER (32 * 1024)
+#define PREALLOC_BUFFER_MAX (32 * 1024)
+
+static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ return snd_pcm_lib_preallocate_pages_for_all(
+ rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ rtd->card->snd_card->dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+}
+
+static void rsnd_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static struct snd_soc_platform_driver rsnd_soc_platform = {
+ .ops = &rsnd_pcm_ops,
+ .pcm_new = rsnd_pcm_new,
+ .pcm_free = rsnd_pcm_free,
+};
+
+static const struct snd_soc_component_driver rsnd_soc_component = {
+ .name = "rsnd",
+};
+
+/*
+ * rsnd probe
+ */
+static int rsnd_probe(struct platform_device *pdev)
+{
+ struct rcar_snd_info *info;
+ struct rsnd_priv *priv;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ info = pdev->dev.platform_data;
+ if (!info) {
+ dev_err(dev, "driver needs R-Car sound information\n");
+ return -ENODEV;
+ }
+
+ /*
+ * init priv data
+ */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(dev, "priv allocate failed\n");
+ return -ENODEV;
+ }
+
+ priv->dev = dev;
+ priv->info = info;
+ spin_lock_init(&priv->lock);
+
+ /*
+ * init each module
+ */
+ ret = rsnd_gen_probe(pdev, info, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_scu_probe(pdev, info, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_adg_probe(pdev, info, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_ssi_probe(pdev, info, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_dai_probe(pdev, info, priv);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * asoc register
+ */
+ ret = snd_soc_register_platform(dev, &rsnd_soc_platform);
+ if (ret < 0) {
+ dev_err(dev, "cannot snd soc register\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_component(dev, &rsnd_soc_component,
+ priv->daidrv, rsnd_dai_nr(priv));
+ if (ret < 0) {
+ dev_err(dev, "cannot snd dai register\n");
+ goto exit_snd_soc;
+ }
+
+ dev_set_drvdata(dev, priv);
+
+ pm_runtime_enable(dev);
+
+ dev_info(dev, "probed\n");
+ return ret;
+
+exit_snd_soc:
+ snd_soc_unregister_platform(dev);
+
+ return ret;
+}
+
+static int rsnd_remove(struct platform_device *pdev)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ /*
+ * remove each module
+ */
+ rsnd_ssi_remove(pdev, priv);
+ rsnd_adg_remove(pdev, priv);
+ rsnd_scu_remove(pdev, priv);
+ rsnd_dai_remove(pdev, priv);
+ rsnd_gen_remove(pdev, priv);
+
+ return 0;
+}
+
+static struct platform_driver rsnd_driver = {
+ .driver = {
+ .name = "rcar_sound",
+ },
+ .probe = rsnd_probe,
+ .remove = rsnd_remove,
+};
+module_platform_driver(rsnd_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_ALIAS("platform:rcar-pcm-audio");
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
new file mode 100644
index 000000000000..babb203b43b7
--- /dev/null
+++ b/sound/soc/sh/rcar/gen.c
@@ -0,0 +1,280 @@
+/*
+ * Renesas R-Car Gen1 SRU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 "rsnd.h"
+
+struct rsnd_gen_ops {
+ int (*path_init)(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+ int (*path_exit)(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+};
+
+struct rsnd_gen_reg_map {
+ int index; /* -1 : not supported */
+ u32 offset_id; /* offset of ssi0, ssi1, ssi2... */
+ u32 offset_adr; /* offset of SSICR, SSISR, ... */
+};
+
+struct rsnd_gen {
+ void __iomem *base[RSND_BASE_MAX];
+
+ struct rsnd_gen_reg_map reg_map[RSND_REG_MAX];
+ struct rsnd_gen_ops *ops;
+};
+
+#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
+
+/*
+ * Gen2
+ * will be filled in the future
+ */
+
+/*
+ * Gen1
+ */
+static int rsnd_gen1_path_init(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *mod;
+ int ret;
+ int id;
+
+ /*
+ * Gen1 is created by SRU/SSI, and this SRU is base module of
+ * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
+ *
+ * Easy image is..
+ * Gen1 SRU = Gen2 SCU + SSIU + etc
+ *
+ * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
+ * using fixed path.
+ *
+ * Then, SSI id = SCU id here
+ */
+
+ /* get SSI's ID */
+ mod = rsnd_ssi_mod_get_frm_dai(priv,
+ rsnd_dai_id(priv, rdai),
+ rsnd_dai_is_play(rdai, io));
+ id = rsnd_mod_id(mod);
+
+ /* SSI */
+ mod = rsnd_ssi_mod_get(priv, id);
+ ret = rsnd_dai_connect(rdai, mod, io);
+ if (ret < 0)
+ return ret;
+
+ /* SCU */
+ mod = rsnd_scu_mod_get(priv, id);
+ ret = rsnd_dai_connect(rdai, mod, io);
+
+ return ret;
+}
+
+static int rsnd_gen1_path_exit(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *mod, *n;
+ int ret = 0;
+
+ /*
+ * remove all mod from rdai
+ */
+ for_each_rsnd_mod(mod, n, io)
+ ret |= rsnd_dai_disconnect(mod);
+
+ return ret;
+}
+
+static struct rsnd_gen_ops rsnd_gen1_ops = {
+ .path_init = rsnd_gen1_path_init,
+ .path_exit = rsnd_gen1_path_exit,
+};
+
+#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \
+ do { \
+ (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \
+ (g)->reg_map[RSND_REG_##i].offset_id = oi; \
+ (g)->reg_map[RSND_REG_##i].offset_adr = oa; \
+ } while (0)
+
+static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen)
+{
+ RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00);
+ RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08);
+ RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c);
+ RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10);
+ RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0);
+ RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0);
+ RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4);
+ RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20);
+ RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214);
+
+ RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00);
+ RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04);
+ RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08);
+ RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c);
+ RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10);
+ RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18);
+ RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c);
+ RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20);
+
+ RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00);
+ RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04);
+ RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08);
+ RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c);
+ RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20);
+}
+
+static int rsnd_gen1_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+ struct resource *sru_res;
+ struct resource *adg_res;
+ struct resource *ssi_res;
+
+ /*
+ * map address
+ */
+ sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU);
+ adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG);
+ ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI);
+
+ gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res);
+ gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res);
+ gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res);
+ if (IS_ERR(gen->base[RSND_GEN1_SRU]) ||
+ IS_ERR(gen->base[RSND_GEN1_ADG]) ||
+ IS_ERR(gen->base[RSND_GEN1_SSI]))
+ return -ENODEV;
+
+ gen->ops = &rsnd_gen1_ops;
+ rsnd_gen1_reg_map_init(gen);
+
+ dev_dbg(dev, "Gen1 device probed\n");
+ dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start,
+ gen->base[RSND_GEN1_SRU]);
+ dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start,
+ gen->base[RSND_GEN1_ADG]);
+ dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start,
+ gen->base[RSND_GEN1_SSI]);
+
+ return 0;
+
+}
+
+static void rsnd_gen1_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+}
+
+/*
+ * Gen
+ */
+int rsnd_gen_path_init(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ return gen->ops->path_init(priv, rdai, io);
+}
+
+int rsnd_gen_path_exit(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ return gen->ops->path_exit(priv, rdai, io);
+}
+
+void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ enum rsnd_reg reg)
+{
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int index;
+ u32 offset_id, offset_adr;
+
+ if (reg >= RSND_REG_MAX) {
+ dev_err(dev, "rsnd_reg reg error\n");
+ return NULL;
+ }
+
+ index = gen->reg_map[reg].index;
+ offset_id = gen->reg_map[reg].offset_id;
+ offset_adr = gen->reg_map[reg].offset_adr;
+
+ if (index < 0) {
+ dev_err(dev, "unsupported reg access %d\n", reg);
+ return NULL;
+ }
+
+ if (offset_id && mod)
+ offset_id *= rsnd_mod_id(mod);
+
+ /*
+ * index/offset were set on gen1/gen2
+ */
+
+ return gen->base[index] + offset_id + offset_adr;
+}
+
+int rsnd_gen_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen;
+ int i;
+
+ gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
+ if (!gen) {
+ dev_err(dev, "GEN allocate failed\n");
+ return -ENOMEM;
+ }
+
+ priv->gen = gen;
+
+ /*
+ * see
+ * rsnd_reg_get()
+ * rsnd_gen_probe()
+ */
+ for (i = 0; i < RSND_REG_MAX; i++)
+ gen->reg_map[i].index = -1;
+
+ /*
+ * init each module
+ */
+ if (rsnd_is_gen1(priv))
+ return rsnd_gen1_probe(pdev, info, priv);
+
+ dev_err(dev, "unknown generation R-Car sound device\n");
+
+ return -ENODEV;
+}
+
+void rsnd_gen_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ if (rsnd_is_gen1(priv))
+ rsnd_gen1_remove(pdev, priv);
+}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
new file mode 100644
index 000000000000..9cc6986a8cfb
--- /dev/null
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -0,0 +1,302 @@
+/*
+ * Renesas R-Car
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 RSND_H
+#define RSND_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/sh_dma.h>
+#include <linux/workqueue.h>
+#include <sound/rcar_snd.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+/*
+ * pseudo register
+ *
+ * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different.
+ * This driver uses pseudo register in order to hide it.
+ * see gen1/gen2 for detail
+ */
+enum rsnd_reg {
+ /* SRU/SCU */
+ RSND_REG_SRC_ROUTE_SEL,
+ RSND_REG_SRC_TMG_SEL0,
+ RSND_REG_SRC_TMG_SEL1,
+ RSND_REG_SRC_TMG_SEL2,
+ RSND_REG_SRC_CTRL,
+ RSND_REG_SSI_MODE0,
+ RSND_REG_SSI_MODE1,
+ RSND_REG_BUSIF_MODE,
+ RSND_REG_BUSIF_ADINR,
+
+ /* ADG */
+ RSND_REG_BRRA,
+ RSND_REG_BRRB,
+ RSND_REG_SSICKR,
+ RSND_REG_AUDIO_CLK_SEL0,
+ RSND_REG_AUDIO_CLK_SEL1,
+ RSND_REG_AUDIO_CLK_SEL2,
+ RSND_REG_AUDIO_CLK_SEL3,
+ RSND_REG_AUDIO_CLK_SEL4,
+ RSND_REG_AUDIO_CLK_SEL5,
+
+ /* SSI */
+ RSND_REG_SSICR,
+ RSND_REG_SSISR,
+ RSND_REG_SSITDR,
+ RSND_REG_SSIRDR,
+ RSND_REG_SSIWSR,
+
+ RSND_REG_MAX,
+};
+
+struct rsnd_priv;
+struct rsnd_mod;
+struct rsnd_dai;
+struct rsnd_dai_stream;
+
+/*
+ * R-Car basic functions
+ */
+#define rsnd_mod_read(m, r) \
+ rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
+#define rsnd_mod_write(m, r, d) \
+ rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
+#define rsnd_mod_bset(m, r, s, d) \
+ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
+
+#define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r)
+#define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d)
+#define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d)
+
+u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg);
+void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
+ enum rsnd_reg reg, u32 data);
+void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
+ u32 mask, u32 data);
+
+/*
+ * R-Car DMA
+ */
+struct rsnd_dma {
+ struct rsnd_priv *priv;
+ struct sh_dmae_slave slave;
+ struct work_struct work;
+ struct dma_chan *chan;
+ enum dma_data_direction dir;
+ int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len);
+ int (*complete)(struct rsnd_dma *dma);
+
+ int submit_loop;
+};
+
+void rsnd_dma_start(struct rsnd_dma *dma);
+void rsnd_dma_stop(struct rsnd_dma *dma);
+int rsnd_dma_available(struct rsnd_dma *dma);
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
+ int is_play, int id,
+ int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len),
+ int (*complete)(struct rsnd_dma *dma));
+void rsnd_dma_quit(struct rsnd_priv *priv,
+ struct rsnd_dma *dma);
+
+
+/*
+ * R-Car sound mod
+ */
+
+struct rsnd_mod_ops {
+ char *name;
+ int (*init)(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+ int (*quit)(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+ int (*start)(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+ int (*stop)(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+};
+
+struct rsnd_mod {
+ int id;
+ struct rsnd_priv *priv;
+ struct rsnd_mod_ops *ops;
+ struct list_head list; /* connect to rsnd_dai playback/capture */
+ struct rsnd_dma dma;
+};
+
+#define rsnd_mod_to_priv(mod) ((mod)->priv)
+#define rsnd_mod_to_dma(mod) (&(mod)->dma)
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
+#define rsnd_mod_id(mod) ((mod)->id)
+#define for_each_rsnd_mod(pos, n, io) \
+ list_for_each_entry_safe(pos, n, &(io)->head, list)
+#define rsnd_mod_call(mod, func, rdai, io) \
+ (!(mod) ? -ENODEV : \
+ !((mod)->ops->func) ? 0 : \
+ (mod)->ops->func(mod, rdai, io))
+
+void rsnd_mod_init(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_mod_ops *ops,
+ int id);
+char *rsnd_mod_name(struct rsnd_mod *mod);
+
+/*
+ * R-Car sound DAI
+ */
+#define RSND_DAI_NAME_SIZE 16
+struct rsnd_dai_stream {
+ struct list_head head; /* head of rsnd_mod list */
+ struct snd_pcm_substream *substream;
+ int byte_pos;
+ int period_pos;
+ int byte_per_period;
+ int next_period_byte;
+};
+
+struct rsnd_dai {
+ char name[RSND_DAI_NAME_SIZE];
+ struct rsnd_dai_platform_info *info; /* rcar_snd.h */
+ struct rsnd_dai_stream playback;
+ struct rsnd_dai_stream capture;
+
+ int clk_master:1;
+ int bit_clk_inv:1;
+ int frm_clk_inv:1;
+ int sys_delay:1;
+ int data_alignment:1;
+};
+
+#define rsnd_dai_nr(priv) ((priv)->dai_nr)
+#define for_each_rsnd_dai(rdai, priv, i) \
+ for (i = 0, (rdai) = rsnd_dai_get(priv, i); \
+ i < rsnd_dai_nr(priv); \
+ i++, (rdai) = rsnd_dai_get(priv, i))
+
+struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
+int rsnd_dai_disconnect(struct rsnd_mod *mod);
+int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io);
+int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
+int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
+#define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
+#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
+
+void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
+int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
+
+/*
+ * R-Car Gen1/Gen2
+ */
+int rsnd_gen_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv);
+void rsnd_gen_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+int rsnd_gen_path_init(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+int rsnd_gen_path_exit(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io);
+void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ enum rsnd_reg reg);
+#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1)
+#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2)
+
+/*
+ * R-Car ADG
+ */
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
+int rsnd_adg_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv);
+void rsnd_adg_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+
+/*
+ * R-Car sound priv
+ */
+struct rsnd_priv {
+
+ struct device *dev;
+ struct rcar_snd_info *info;
+ spinlock_t lock;
+
+ /*
+ * below value will be filled on rsnd_gen_probe()
+ */
+ void *gen;
+
+ /*
+ * below value will be filled on rsnd_scu_probe()
+ */
+ void *scu;
+ int scu_nr;
+
+ /*
+ * below value will be filled on rsnd_adg_probe()
+ */
+ void *adg;
+
+ /*
+ * below value will be filled on rsnd_ssi_probe()
+ */
+ void *ssiu;
+
+ /*
+ * below value will be filled on rsnd_dai_probe()
+ */
+ struct snd_soc_dai_driver *daidrv;
+ struct rsnd_dai *rdai;
+ int dai_nr;
+};
+
+#define rsnd_priv_to_dev(priv) ((priv)->dev)
+#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
+#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
+
+/*
+ * R-Car SCU
+ */
+int rsnd_scu_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv);
+void rsnd_scu_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_scu_nr(priv) ((priv)->scu_nr)
+
+/*
+ * R-Car SSI
+ */
+int rsnd_ssi_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv);
+void rsnd_ssi_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
+struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
+ int dai_id, int is_play);
+
+#endif
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c
new file mode 100644
index 000000000000..184d9008cecd
--- /dev/null
+++ b/sound/soc/sh/rcar/scu.c
@@ -0,0 +1,236 @@
+/*
+ * Renesas R-Car SCU support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 "rsnd.h"
+
+struct rsnd_scu {
+ struct rsnd_scu_platform_info *info; /* rcar_snd.h */
+ struct rsnd_mod mod;
+};
+
+#define rsnd_scu_mode_flags(p) ((p)->info->flags)
+
+/*
+ * ADINR
+ */
+#define OTBL_24 (0 << 16)
+#define OTBL_22 (2 << 16)
+#define OTBL_20 (4 << 16)
+#define OTBL_18 (6 << 16)
+#define OTBL_16 (8 << 16)
+
+
+#define rsnd_mod_to_scu(_mod) \
+ container_of((_mod), struct rsnd_scu, mod)
+
+#define for_each_rsnd_scu(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_scu_nr(priv)) && \
+ ((pos) = (struct rsnd_scu *)(priv)->scu + i); \
+ i++)
+
+static int rsnd_scu_set_route(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct scu_route_config {
+ u32 mask;
+ int shift;
+ } routes[] = {
+ { 0xF, 0, }, /* 0 */
+ { 0xF, 4, }, /* 1 */
+ { 0xF, 8, }, /* 2 */
+ { 0x7, 12, }, /* 3 */
+ { 0x7, 16, }, /* 4 */
+ { 0x7, 20, }, /* 5 */
+ { 0x7, 24, }, /* 6 */
+ { 0x3, 28, }, /* 7 */
+ { 0x3, 30, }, /* 8 */
+ };
+
+ u32 mask;
+ u32 val;
+ int shift;
+ int id;
+
+ /*
+ * Gen1 only
+ */
+ if (!rsnd_is_gen1(priv))
+ return 0;
+
+ id = rsnd_mod_id(mod);
+ if (id < 0 || id > ARRAY_SIZE(routes))
+ return -EIO;
+
+ /*
+ * SRC_ROUTE_SELECT
+ */
+ val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2;
+ val = val << routes[id].shift;
+ mask = routes[id].mask << routes[id].shift;
+
+ rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val);
+
+ /*
+ * SRC_TIMING_SELECT
+ */
+ shift = (id % 4) * 8;
+ mask = 0x1F << shift;
+ if (8 == id) /* SRU8 is very special */
+ val = id << shift;
+ else
+ val = (id + 1) << shift;
+
+ switch (id / 4) {
+ case 0:
+ rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val);
+ break;
+ case 1:
+ rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val);
+ break;
+ case 2:
+ rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val);
+ break;
+ }
+
+ return 0;
+}
+
+static int rsnd_scu_set_mode(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ int id = rsnd_mod_id(mod);
+ u32 val;
+
+ if (rsnd_is_gen1(priv)) {
+ val = (1 << id);
+ rsnd_mod_bset(mod, SRC_CTRL, val, val);
+ }
+
+ return 0;
+}
+
+static int rsnd_scu_set_hpbif(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ u32 adinr = runtime->channels;
+
+ switch (runtime->sample_bits) {
+ case 16:
+ adinr |= OTBL_16;
+ break;
+ case 32:
+ adinr |= OTBL_24;
+ break;
+ default:
+ return -EIO;
+ }
+
+ rsnd_mod_write(mod, BUSIF_MODE, 1);
+ rsnd_mod_write(mod, BUSIF_ADINR, adinr);
+
+ return 0;
+}
+
+static int rsnd_scu_start(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 flags = rsnd_scu_mode_flags(scu);
+ int ret;
+
+ /*
+ * SCU will be used if it has RSND_SCU_USB_HPBIF flags
+ */
+ if (!(flags & RSND_SCU_USB_HPBIF)) {
+ /* it use PIO transter */
+ dev_dbg(dev, "%s%d is not used\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ return 0;
+ }
+
+ /* it use DMA transter */
+ ret = rsnd_scu_set_route(priv, mod, rdai, io);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_scu_set_mode(priv, mod, rdai, io);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_scu_set_hpbif(priv, mod, rdai, io);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_scu_ops = {
+ .name = "scu",
+ .start = rsnd_scu_start,
+};
+
+struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id)
+{
+ BUG_ON(id < 0 || id >= rsnd_scu_nr(priv));
+
+ return &((struct rsnd_scu *)(priv->scu) + id)->mod;
+}
+
+int rsnd_scu_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_scu *scu;
+ int i, nr;
+
+ /*
+ * init SCU
+ */
+ nr = info->scu_info_nr;
+ scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL);
+ if (!scu) {
+ dev_err(dev, "SCU allocate failed\n");
+ return -ENOMEM;
+ }
+
+ priv->scu_nr = nr;
+ priv->scu = scu;
+
+ for_each_rsnd_scu(scu, priv, i) {
+ rsnd_mod_init(priv, &scu->mod,
+ &rsnd_scu_ops, i);
+ scu->info = &info->scu_info[i];
+
+ dev_dbg(dev, "SCU%d probed\n", i);
+ }
+ dev_dbg(dev, "scu probed\n");
+
+ return 0;
+}
+
+void rsnd_scu_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
new file mode 100644
index 000000000000..fae26d3f79d2
--- /dev/null
+++ b/sound/soc/sh/rcar/ssi.c
@@ -0,0 +1,728 @@
+/*
+ * Renesas R-Car SSIU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on fsi.c
+ * Kuninori Morimoto <morimoto.kuninori@renesas.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/delay.h>
+#include "rsnd.h"
+#define RSND_SSI_NAME_SIZE 16
+
+/*
+ * SSICR
+ */
+#define FORCE (1 << 31) /* Fixed */
+#define DMEN (1 << 28) /* DMA Enable */
+#define UIEN (1 << 27) /* Underflow Interrupt Enable */
+#define OIEN (1 << 26) /* Overflow Interrupt Enable */
+#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
+#define DIEN (1 << 24) /* Data Interrupt Enable */
+
+#define DWL_8 (0 << 19) /* Data Word Length */
+#define DWL_16 (1 << 19) /* Data Word Length */
+#define DWL_18 (2 << 19) /* Data Word Length */
+#define DWL_20 (3 << 19) /* Data Word Length */
+#define DWL_22 (4 << 19) /* Data Word Length */
+#define DWL_24 (5 << 19) /* Data Word Length */
+#define DWL_32 (6 << 19) /* Data Word Length */
+
+#define SWL_32 (3 << 16) /* R/W System Word Length */
+#define SCKD (1 << 15) /* Serial Bit Clock Direction */
+#define SWSD (1 << 14) /* Serial WS Direction */
+#define SCKP (1 << 13) /* Serial Bit Clock Polarity */
+#define SWSP (1 << 12) /* Serial WS Polarity */
+#define SDTA (1 << 10) /* Serial Data Alignment */
+#define DEL (1 << 8) /* Serial Data Delay */
+#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
+#define TRMD (1 << 1) /* Transmit/Receive Mode Select */
+#define EN (1 << 0) /* SSI Module Enable */
+
+/*
+ * SSISR
+ */
+#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */
+#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */
+#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
+#define DIRQ (1 << 24) /* Data Interrupt Status Flag */
+
+/*
+ * SSIWSR
+ */
+#define CONT (1 << 8) /* WS Continue Function */
+
+struct rsnd_ssi {
+ struct clk *clk;
+ struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
+ struct rsnd_ssi *parent;
+ struct rsnd_mod mod;
+
+ struct rsnd_dai *rdai;
+ struct rsnd_dai_stream *io;
+ u32 cr_own;
+ u32 cr_clk;
+ u32 cr_etc;
+ int err;
+ int dma_offset;
+ unsigned int usrcnt;
+ unsigned int rate;
+};
+
+struct rsnd_ssiu {
+ u32 ssi_mode0;
+ u32 ssi_mode1;
+
+ int ssi_nr;
+ struct rsnd_ssi *ssi;
+};
+
+#define for_each_rsnd_ssi(pos, priv, i) \
+ for (i = 0; \
+ (i < rsnd_ssi_nr(priv)) && \
+ ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \
+ i++)
+
+#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr)
+#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
+#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_dma_available(ssi) \
+ rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
+#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
+#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
+#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
+#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
+#define rsnd_ssi_to_ssiu(ssi)\
+ (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1)
+
+static void rsnd_ssi_mode_init(struct rsnd_priv *priv,
+ struct rsnd_ssiu *ssiu)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_ssi *ssi;
+ u32 flags;
+ u32 val;
+ int i;
+
+ /*
+ * SSI_MODE0
+ */
+ ssiu->ssi_mode0 = 0;
+ for_each_rsnd_ssi(ssi, priv, i) {
+ flags = rsnd_ssi_mode_flags(ssi);
+
+ /* see also BUSIF_MODE */
+ if (!(flags & RSND_SSI_DEPENDENT)) {
+ ssiu->ssi_mode0 |= (1 << i);
+ dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i);
+ } else {
+ dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i);
+ }
+ }
+
+ /*
+ * SSI_MODE1
+ */
+#define ssi_parent_set(p, sync, adg, ext) \
+ do { \
+ ssi->parent = ssiu->ssi + p; \
+ if (flags & RSND_SSI_CLK_FROM_ADG) \
+ val = adg; \
+ else \
+ val = ext; \
+ if (flags & RSND_SSI_SYNC) \
+ val |= sync; \
+ } while (0)
+
+ ssiu->ssi_mode1 = 0;
+ for_each_rsnd_ssi(ssi, priv, i) {
+ flags = rsnd_ssi_mode_flags(ssi);
+
+ if (!(flags & RSND_SSI_CLK_PIN_SHARE))
+ continue;
+
+ val = 0;
+ switch (i) {
+ case 1:
+ ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0));
+ break;
+ case 2:
+ ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2));
+ break;
+ case 4:
+ ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16));
+ break;
+ case 8:
+ ssi_parent_set(7, 0, 0, 0);
+ break;
+ }
+
+ ssiu->ssi_mode1 |= val;
+ }
+}
+
+static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi)
+{
+ struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi);
+
+ rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0);
+ rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1);
+}
+
+static void rsnd_ssi_status_check(struct rsnd_mod *mod,
+ u32 bit)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 status;
+ int i;
+
+ for (i = 0; i < 1024; i++) {
+ status = rsnd_mod_read(mod, SSISR);
+ if (status & bit)
+ return;
+
+ udelay(50);
+ }
+
+ dev_warn(dev, "status check failed\n");
+}
+
+static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
+ unsigned int rate)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int i, j, ret;
+ int adg_clk_div_table[] = {
+ 1, 6, /* see adg.c */
+ };
+ int ssi_clk_mul_table[] = {
+ 1, 2, 4, 8, 16, 6, 12,
+ };
+ unsigned int main_rate;
+
+ /*
+ * Find best clock, and try to start ADG
+ */
+ for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) {
+ for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+ /*
+ * this driver is assuming that
+ * system word is 64fs (= 2 x 32bit)
+ * see rsnd_ssi_start()
+ */
+ main_rate = rate / adg_clk_div_table[i]
+ * 32 * 2 * ssi_clk_mul_table[j];
+
+ ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
+ if (0 == ret) {
+ ssi->rate = rate;
+ ssi->cr_clk = FORCE | SWL_32 |
+ SCKD | SWSD | CKDV(j);
+
+ dev_dbg(dev, "ssi%d outputs %u Hz\n",
+ rsnd_mod_id(&ssi->mod), rate);
+
+ return 0;
+ }
+ }
+ }
+
+ dev_err(dev, "unsupported clock rate\n");
+ return -EIO;
+}
+
+static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
+{
+ ssi->rate = 0;
+ ssi->cr_clk = 0;
+ rsnd_adg_ssi_clk_stop(&ssi->mod);
+}
+
+static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 cr;
+
+ if (0 == ssi->usrcnt) {
+ clk_enable(ssi->clk);
+
+ if (rsnd_rdai_is_clk_master(rdai)) {
+ struct snd_pcm_runtime *runtime;
+
+ runtime = rsnd_io_to_runtime(io);
+
+ if (rsnd_ssi_clk_from_parent(ssi))
+ rsnd_ssi_hw_start(ssi->parent, rdai, io);
+ else
+ rsnd_ssi_master_clk_start(ssi, runtime->rate);
+ }
+ }
+
+ cr = ssi->cr_own |
+ ssi->cr_clk |
+ ssi->cr_etc |
+ EN;
+
+ rsnd_mod_write(&ssi->mod, SSICR, cr);
+
+ ssi->usrcnt++;
+
+ dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
+}
+
+static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
+ struct rsnd_dai *rdai)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 cr;
+
+ if (0 == ssi->usrcnt) /* stop might be called without start */
+ return;
+
+ ssi->usrcnt--;
+
+ if (0 == ssi->usrcnt) {
+ /*
+ * disable all IRQ,
+ * and, wait all data was sent
+ */
+ cr = ssi->cr_own |
+ ssi->cr_clk;
+
+ rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
+ rsnd_ssi_status_check(&ssi->mod, DIRQ);
+
+ /*
+ * disable SSI,
+ * and, wait idle state
+ */
+ rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */
+ rsnd_ssi_status_check(&ssi->mod, IIRQ);
+
+ if (rsnd_rdai_is_clk_master(rdai)) {
+ if (rsnd_ssi_clk_from_parent(ssi))
+ rsnd_ssi_hw_stop(ssi->parent, rdai);
+ else
+ rsnd_ssi_master_clk_stop(ssi);
+ }
+
+ clk_disable(ssi->clk);
+ }
+
+ dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
+}
+
+/*
+ * SSI mod common functions
+ */
+static int rsnd_ssi_init(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ u32 cr;
+
+ cr = FORCE;
+
+ /*
+ * always use 32bit system word for easy clock calculation.
+ * see also rsnd_ssi_master_clk_enable()
+ */
+ cr |= SWL_32;
+
+ /*
+ * init clock settings for SSICR
+ */
+ switch (runtime->sample_bits) {
+ case 16:
+ cr |= DWL_16;
+ break;
+ case 32:
+ cr |= DWL_24;
+ break;
+ default:
+ return -EIO;
+ }
+
+ if (rdai->bit_clk_inv)
+ cr |= SCKP;
+ if (rdai->frm_clk_inv)
+ cr |= SWSP;
+ if (rdai->data_alignment)
+ cr |= SDTA;
+ if (rdai->sys_delay)
+ cr |= DEL;
+ if (rsnd_dai_is_play(rdai, io))
+ cr |= TRMD;
+
+ /*
+ * set ssi parameter
+ */
+ ssi->rdai = rdai;
+ ssi->io = io;
+ ssi->cr_own = cr;
+ ssi->err = -1; /* ignore 1st error */
+
+ rsnd_ssi_mode_set(ssi);
+
+ dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ return 0;
+}
+
+static int rsnd_ssi_quit(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ if (ssi->err > 0)
+ dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err);
+
+ ssi->rdai = NULL;
+ ssi->io = NULL;
+ ssi->cr_own = 0;
+ ssi->err = 0;
+
+ return 0;
+}
+
+static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
+{
+ /* under/over flow error */
+ if (status & (UIRQ | OIRQ)) {
+ ssi->err++;
+
+ /* clear error status */
+ rsnd_mod_write(&ssi->mod, SSISR, 0);
+ }
+}
+
+/*
+ * SSI PIO
+ */
+static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+{
+ struct rsnd_ssi *ssi = data;
+ struct rsnd_dai_stream *io = ssi->io;
+ u32 status = rsnd_mod_read(&ssi->mod, SSISR);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (io && (status & DIRQ)) {
+ struct rsnd_dai *rdai = ssi->rdai;
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ u32 *buf = (u32 *)(runtime->dma_area +
+ rsnd_dai_pointer_offset(io, 0));
+
+ rsnd_ssi_record_error(ssi, status);
+
+ /*
+ * 8/16/32 data can be assesse to TDR/RDR register
+ * directly as 32bit data
+ * see rsnd_ssi_init()
+ */
+ if (rsnd_dai_is_play(rdai, io))
+ rsnd_mod_write(&ssi->mod, SSITDR, *buf);
+ else
+ *buf = rsnd_mod_read(&ssi->mod, SSIRDR);
+
+ rsnd_dai_pointer_update(io, sizeof(*buf));
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ /* enable PIO IRQ */
+ ssi->cr_etc = UIEN | OIEN | DIEN;
+
+ rsnd_ssi_hw_start(ssi, rdai, io);
+
+ dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ return 0;
+}
+
+static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ ssi->cr_etc = 0;
+
+ rsnd_ssi_hw_stop(ssi, rdai);
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
+ .name = "ssi (pio)",
+ .init = rsnd_ssi_init,
+ .quit = rsnd_ssi_quit,
+ .start = rsnd_ssi_pio_start,
+ .stop = rsnd_ssi_pio_stop,
+};
+
+static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len)
+{
+ struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
+ struct rsnd_dai_stream *io = ssi->io;
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+ *len = io->byte_per_period;
+ *buf = runtime->dma_addr +
+ rsnd_dai_pointer_offset(io, ssi->dma_offset + *len);
+ ssi->dma_offset = *len; /* it cares A/B plane */
+
+ return 0;
+}
+
+static int rsnd_ssi_dma_complete(struct rsnd_dma *dma)
+{
+ struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
+ struct rsnd_dai_stream *io = ssi->io;
+ u32 status = rsnd_mod_read(&ssi->mod, SSISR);
+
+ rsnd_ssi_record_error(ssi, status);
+
+ rsnd_dai_pointer_update(ssi->io, io->byte_per_period);
+
+ return 0;
+}
+
+static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
+
+ /* enable DMA transfer */
+ ssi->cr_etc = DMEN;
+ ssi->dma_offset = 0;
+
+ rsnd_dma_start(dma);
+
+ rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+
+ /* enable WS continue */
+ if (rsnd_rdai_is_clk_master(rdai))
+ rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+ return 0;
+}
+
+static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
+
+ ssi->cr_etc = 0;
+
+ rsnd_ssi_hw_stop(ssi, rdai);
+
+ rsnd_dma_stop(dma);
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
+ .name = "ssi (dma)",
+ .init = rsnd_ssi_init,
+ .quit = rsnd_ssi_quit,
+ .start = rsnd_ssi_dma_start,
+ .stop = rsnd_ssi_dma_stop,
+};
+
+/*
+ * Non SSI
+ */
+static int rsnd_ssi_non(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssi_non_ops = {
+ .name = "ssi (non)",
+ .init = rsnd_ssi_non,
+ .quit = rsnd_ssi_non,
+ .start = rsnd_ssi_non,
+ .stop = rsnd_ssi_non,
+};
+
+/*
+ * ssi mod function
+ */
+struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
+ int dai_id, int is_play)
+{
+ struct rsnd_ssi *ssi;
+ int i, has_play;
+
+ is_play = !!is_play;
+
+ for_each_rsnd_ssi(ssi, priv, i) {
+ if (rsnd_ssi_dai_id(ssi) != dai_id)
+ continue;
+
+ has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY);
+
+ if (is_play == has_play)
+ return &ssi->mod;
+ }
+
+ return NULL;
+}
+
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
+{
+ BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv));
+
+ return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod;
+}
+
+int rsnd_ssi_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi_platform_info *pinfo;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mod_ops *ops;
+ struct clk *clk;
+ struct rsnd_ssiu *ssiu;
+ struct rsnd_ssi *ssi;
+ char name[RSND_SSI_NAME_SIZE];
+ int i, nr, ret;
+
+ /*
+ * init SSI
+ */
+ nr = info->ssi_info_nr;
+ ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr),
+ GFP_KERNEL);
+ if (!ssiu) {
+ dev_err(dev, "SSI allocate failed\n");
+ return -ENOMEM;
+ }
+
+ priv->ssiu = ssiu;
+ ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1);
+ ssiu->ssi_nr = nr;
+
+ for_each_rsnd_ssi(ssi, priv, i) {
+ pinfo = &info->ssi_info[i];
+
+ snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i);
+
+ clk = clk_get(dev, name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ssi->info = pinfo;
+ ssi->clk = clk;
+
+ ops = &rsnd_ssi_non_ops;
+
+ /*
+ * SSI DMA case
+ */
+ if (pinfo->dma_id > 0) {
+ ret = rsnd_dma_init(
+ priv, rsnd_mod_to_dma(&ssi->mod),
+ (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY),
+ pinfo->dma_id,
+ rsnd_ssi_dma_inquiry,
+ rsnd_ssi_dma_complete);
+ if (ret < 0)
+ dev_info(dev, "SSI DMA failed. try PIO transter\n");
+ else
+ ops = &rsnd_ssi_dma_ops;
+
+ dev_dbg(dev, "SSI%d use DMA transfer\n", i);
+ }
+
+ /*
+ * SSI PIO case
+ */
+ if (!rsnd_ssi_dma_available(ssi) &&
+ rsnd_ssi_pio_available(ssi)) {
+ ret = devm_request_irq(dev, pinfo->pio_irq,
+ &rsnd_ssi_pio_interrupt,
+ IRQF_SHARED,
+ dev_name(dev), ssi);
+ if (ret) {
+ dev_err(dev, "SSI request interrupt failed\n");
+ return ret;
+ }
+
+ ops = &rsnd_ssi_pio_ops;
+
+ dev_dbg(dev, "SSI%d use PIO transfer\n", i);
+ }
+
+ rsnd_mod_init(priv, &ssi->mod, ops, i);
+ }
+
+ rsnd_ssi_mode_init(priv, ssiu);
+
+ dev_dbg(dev, "ssi probed\n");
+
+ return 0;
+}
+
+void rsnd_ssi_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi;
+ int i;
+
+ for_each_rsnd_ssi(ssi, priv, i) {
+ clk_put(ssi->clk);
+ if (rsnd_ssi_dma_available(ssi))
+ rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod));
+ }
+
+}
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 06a8000aa07b..53c9ecdd119f 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -149,8 +149,9 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
SND_SOC_DAPM_STREAM_STOP);
} else {
rtd->pop_wait = 1;
- schedule_delayed_work(&rtd->delayed_work,
- msecs_to_jiffies(rtd->pmdown_time));
+ queue_delayed_work(system_power_efficient_wq,
+ &rtd->delayed_work,
+ msecs_to_jiffies(rtd->pmdown_time));
}
} else {
/* capture streams can be powered down now */
@@ -334,7 +335,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
return ret;
}
-static int sst_compr_set_metadata(struct snd_compr_stream *cstream,
+static int soc_compr_set_metadata(struct snd_compr_stream *cstream,
struct snd_compr_metadata *metadata)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -347,7 +348,7 @@ static int sst_compr_set_metadata(struct snd_compr_stream *cstream,
return ret;
}
-static int sst_compr_get_metadata(struct snd_compr_stream *cstream,
+static int soc_compr_get_metadata(struct snd_compr_stream *cstream,
struct snd_compr_metadata *metadata)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -364,8 +365,8 @@ static struct snd_compr_ops soc_compr_ops = {
.open = soc_compr_open,
.free = soc_compr_free,
.set_params = soc_compr_set_params,
- .set_metadata = sst_compr_set_metadata,
- .get_metadata = sst_compr_get_metadata,
+ .set_metadata = soc_compr_set_metadata,
+ .get_metadata = soc_compr_get_metadata,
.get_params = soc_compr_get_params,
.trigger = soc_compr_trigger,
.pointer = soc_compr_pointer,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index d82ee386eab5..4d0561312f3b 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -30,9 +30,12 @@
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <sound/ac97_codec.h>
#include <sound/core.h>
#include <sound/jack.h>
@@ -47,8 +50,6 @@
#define NAME_SIZE 32
-static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
-
#ifdef CONFIG_DEBUG_FS
struct dentry *snd_soc_debugfs_root;
EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
@@ -69,6 +70,16 @@ static int pmdown_time = 5000;
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
+struct snd_ac97_reset_cfg {
+ struct pinctrl *pctl;
+ struct pinctrl_state *pstate_reset;
+ struct pinctrl_state *pstate_warm_reset;
+ struct pinctrl_state *pstate_run;
+ int gpio_sdata;
+ int gpio_sync;
+ int gpio_reset;
+};
+
/* returns the minimum number of bytes needed to represent
* a particular given value */
static int min_bytes_needed(unsigned long val)
@@ -192,7 +203,7 @@ static ssize_t pmdown_time_set(struct device *dev,
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
int ret;
- ret = strict_strtol(buf, 10, &rtd->pmdown_time);
+ ret = kstrtol(buf, 10, &rtd->pmdown_time);
if (ret)
return ret;
@@ -237,6 +248,7 @@ static ssize_t codec_reg_write_file(struct file *file,
char *start = buf;
unsigned long reg, value;
struct snd_soc_codec *codec = file->private_data;
+ int ret;
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
@@ -248,8 +260,9 @@ static ssize_t codec_reg_write_file(struct file *file,
reg = simple_strtoul(start, &start, 16);
while (*start == ' ')
start++;
- if (strict_strtoul(start, 16, &value))
- return -EINVAL;
+ ret = kstrtoul(start, 16, &value);
+ if (ret)
+ return ret;
/* Userspace has been fiddling around behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE);
@@ -530,6 +543,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
}
#endif
+static void codec2codec_close_delayed_work(struct work_struct *work)
+{
+ /* Currently nothing to do for c2c links
+ * Since c2c links are internal nodes in the DAPM graph and
+ * don't interface with the outside world or application layer
+ * we don't have to do any special handling on close.
+ */
+}
+
#ifdef CONFIG_PM_SLEEP
/* powers down audio subsystem for suspend */
int snd_soc_suspend(struct device *dev)
@@ -1223,9 +1245,6 @@ static int soc_post_component_init(struct snd_soc_card *card,
}
rtd->card = card;
- /* Make sure all DAPM widgets are instantiated */
- snd_soc_dapm_new_widgets(&codec->dapm);
-
/* machine controls, routes and widgets are not prefixed */
temp = codec->name_prefix;
codec->name_prefix = NULL;
@@ -1428,6 +1447,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
return ret;
}
} else {
+ INIT_DELAYED_WORK(&rtd->delayed_work,
+ codec2codec_close_delayed_work);
+
/* link the DAI widgets */
play_w = codec_dai->playback_widget;
capture_w = cpu_dai->capture_widget;
@@ -1718,8 +1740,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
- snd_soc_dapm_new_widgets(&card->dapm);
-
for (i = 0; i < card->num_links; i++) {
dai_link = &card->dai_link[i];
dai_fmt = dai_link->dai_fmt;
@@ -1798,12 +1818,12 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
- snd_soc_dapm_new_widgets(&card->dapm);
-
if (card->fully_routed)
list_for_each_entry(codec, &card->codec_dev_list, card_list)
snd_soc_dapm_auto_nc_codec_pins(codec);
+ snd_soc_dapm_new_widgets(card);
+
ret = snd_card_register(card->snd_card);
if (ret < 0) {
dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
@@ -2080,6 +2100,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+ udelay(10);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+ msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+ udelay(10);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+ msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+ struct snd_ac97_reset_cfg *cfg)
+{
+ struct pinctrl *p;
+ struct pinctrl_state *state;
+ int gpio;
+ int ret;
+
+ p = devm_pinctrl_get(dev);
+ if (IS_ERR(p)) {
+ dev_err(dev, "Failed to get pinctrl\n");
+ return PTR_RET(p);
+ }
+ cfg->pctl = p;
+
+ state = pinctrl_lookup_state(p, "ac97-reset");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+ return PTR_RET(state);
+ }
+ cfg->pstate_reset = state;
+
+ state = pinctrl_lookup_state(p, "ac97-warm-reset");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+ return PTR_RET(state);
+ }
+ cfg->pstate_warm_reset = state;
+
+ state = pinctrl_lookup_state(p, "ac97-running");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-running\n");
+ return PTR_RET(state);
+ }
+ cfg->pstate_run = state;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-sync gpio\n");
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-sync gpio\n");
+ return ret;
+ }
+ cfg->gpio_sync = gpio;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+ return ret;
+ }
+ cfg->gpio_sdata = gpio;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-reset gpio\n");
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-reset gpio\n");
+ return ret;
+ }
+ cfg->gpio_reset = gpio;
+
+ return 0;
+}
+
struct snd_ac97_bus_ops *soc_ac97_ops;
EXPORT_SYMBOL_GPL(soc_ac97_ops);
@@ -2098,6 +2229,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_ac97_reset_cfg cfg;
+ int ret;
+
+ ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_set_ac97_ops(ops);
+ if (ret)
+ return ret;
+
+ ops->warm_reset = snd_soc_ac97_warm_reset;
+ ops->reset = snd_soc_ac97_reset;
+
+ snd_ac97_rst_cfg = cfg;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
+
+/**
* snd_soc_free_ac97_codec - free AC97 codec device
* @codec: audio codec
*
@@ -2299,6 +2459,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
return 0;
}
+struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
+ const char *name)
+{
+ struct snd_card *card = soc_card->snd_card;
+ struct snd_kcontrol *kctl;
+
+ if (unlikely(!name))
+ return NULL;
+
+ list_for_each_entry(kctl, &card->controls, list)
+ if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
+ return kctl;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
+
/**
* snd_soc_add_codec_controls - add an array of controls to a codec.
* Convenience function to add a list of controls. Many codecs were
@@ -2541,59 +2717,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
/**
- * snd_soc_info_enum_ext - external enumerated single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about an external enumerated
- * single mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = e->max;
-
- if (uinfo->value.enumerated.item > e->max - 1)
- uinfo->value.enumerated.item = e->max - 1;
- strcpy(uinfo->value.enumerated.name,
- e->texts[uinfo->value.enumerated.item]);
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
-
-/**
- * snd_soc_info_volsw_ext - external single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single external mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int max = kcontrol->private_value;
-
- if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- else
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = max;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
-
-/**
* snd_soc_info_volsw - single mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 4375c9f2b791..c17c14c394df 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -47,6 +47,15 @@
#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
+static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
+ const char *control,
+ int (*connected)(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink));
+static struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_widget *widget);
+
/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
[snd_soc_dapm_pre] = 0,
@@ -73,16 +82,18 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_hp] = 10,
[snd_soc_dapm_spk] = 10,
[snd_soc_dapm_line] = 10,
- [snd_soc_dapm_post] = 11,
+ [snd_soc_dapm_kcontrol] = 11,
+ [snd_soc_dapm_post] = 12,
};
static int dapm_down_seq[] = {
[snd_soc_dapm_pre] = 0,
- [snd_soc_dapm_adc] = 1,
- [snd_soc_dapm_hp] = 2,
- [snd_soc_dapm_spk] = 2,
- [snd_soc_dapm_line] = 2,
- [snd_soc_dapm_out_drv] = 2,
+ [snd_soc_dapm_kcontrol] = 1,
+ [snd_soc_dapm_adc] = 2,
+ [snd_soc_dapm_hp] = 3,
+ [snd_soc_dapm_spk] = 3,
+ [snd_soc_dapm_line] = 3,
+ [snd_soc_dapm_out_drv] = 3,
[snd_soc_dapm_pga] = 4,
[snd_soc_dapm_switch] = 5,
[snd_soc_dapm_mixer_named_ctl] = 5,
@@ -174,35 +185,177 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
}
-/* get snd_card from DAPM context */
-static inline struct snd_card *dapm_get_snd_card(
- struct snd_soc_dapm_context *dapm)
+struct dapm_kcontrol_data {
+ unsigned int value;
+ struct snd_soc_dapm_widget *widget;
+ struct list_head paths;
+ struct snd_soc_dapm_widget_list *wlist;
+};
+
+static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kcontrol)
{
- if (dapm->codec)
- return dapm->codec->card->snd_card;
- else if (dapm->platform)
- return dapm->platform->card->snd_card;
- else
- BUG();
+ struct dapm_kcontrol_data *data;
+ struct soc_mixer_control *mc;
- /* unreachable */
- return NULL;
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(widget->dapm->dev,
+ "ASoC: can't allocate kcontrol data for %s\n",
+ widget->name);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&data->paths);
+
+ switch (widget->id) {
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
+ mc = (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (mc->autodisable) {
+ struct snd_soc_dapm_widget template;
+
+ memset(&template, 0, sizeof(template));
+ template.reg = mc->reg;
+ template.mask = (1 << fls(mc->max)) - 1;
+ template.shift = mc->shift;
+ if (mc->invert)
+ template.off_val = mc->max;
+ else
+ template.off_val = 0;
+ template.on_val = template.off_val;
+ template.id = snd_soc_dapm_kcontrol;
+ template.name = kcontrol->id.name;
+
+ data->value = template.on_val;
+
+ data->widget = snd_soc_dapm_new_control(widget->dapm,
+ &template);
+ if (!data->widget) {
+ kfree(data);
+ return -ENOMEM;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ kcontrol->private_data = data;
+
+ return 0;
}
-/* get soc_card from DAPM context */
-static inline struct snd_soc_card *dapm_get_soc_card(
- struct snd_soc_dapm_context *dapm)
+static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
{
- if (dapm->codec)
- return dapm->codec->card;
- else if (dapm->platform)
- return dapm->platform->card;
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl);
+ kfree(data->widget);
+ kfree(data->wlist);
+ kfree(data);
+}
+
+static struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist(
+ const struct snd_kcontrol *kcontrol)
+{
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+ return data->wlist;
+}
+
+static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol,
+ struct snd_soc_dapm_widget *widget)
+{
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *new_wlist;
+ unsigned int n;
+
+ if (data->wlist)
+ n = data->wlist->num_widgets + 1;
else
- BUG();
+ n = 1;
- /* unreachable */
- return NULL;
+ new_wlist = krealloc(data->wlist,
+ sizeof(*new_wlist) + sizeof(widget) * n, GFP_KERNEL);
+ if (!new_wlist)
+ return -ENOMEM;
+
+ new_wlist->widgets[n - 1] = widget;
+ new_wlist->num_widgets = n;
+
+ data->wlist = new_wlist;
+
+ return 0;
+}
+
+static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,
+ struct snd_soc_dapm_path *path)
+{
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+ list_add_tail(&path->list_kcontrol, &data->paths);
+
+ if (data->widget) {
+ snd_soc_dapm_add_path(data->widget->dapm, data->widget,
+ path->source, NULL, NULL);
+ }
+}
+
+static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol)
+{
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+ if (!data->widget)
+ return true;
+
+ return data->widget->power;
+}
+
+static struct list_head *dapm_kcontrol_get_path_list(
+ const struct snd_kcontrol *kcontrol)
+{
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+ return &data->paths;
+}
+
+#define dapm_kcontrol_for_each_path(path, kcontrol) \
+ list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \
+ list_kcontrol)
+
+static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
+{
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+ return data->value;
+}
+
+static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
+ unsigned int value)
+{
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+ if (data->value == value)
+ return false;
+
+ if (data->widget)
+ data->widget->on_val = value;
+
+ data->value = value;
+
+ return true;
+}
+
+/**
+ * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
+ * @kcontrol: The kcontrol
+ */
+struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol)
+{
+ return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->codec;
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
static void dapm_reset(struct snd_soc_card *card)
{
@@ -211,6 +364,7 @@ static void dapm_reset(struct snd_soc_card *card)
memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
list_for_each_entry(w, &card->widgets, list) {
+ w->new_power = w->power;
w->power_checked = false;
w->inputs = -1;
w->outputs = -1;
@@ -428,6 +582,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_spk:
case snd_soc_dapm_line:
case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_kcontrol:
p->connect = 1;
break;
/* does affect routing - dynamically connected */
@@ -507,17 +662,12 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
return 0;
}
-static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
-{
- kfree(kctl->private_data);
-}
-
/*
* Determine if a kcontrol is shared. If it is, look it up. If it isn't,
* create it. Either way, add the widget into the control's widget list
*/
static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
- int kci, struct snd_soc_dapm_path *path)
+ int kci)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_card *card = dapm->card->snd_card;
@@ -525,9 +675,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
size_t prefix_len;
int shared;
struct snd_kcontrol *kcontrol;
- struct snd_soc_dapm_widget_list *wlist;
- int wlistentries;
- size_t wlistsize;
bool wname_in_long_name, kcname_in_long_name;
char *long_name;
const char *name;
@@ -546,25 +693,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci],
&kcontrol);
- if (kcontrol) {
- wlist = kcontrol->private_data;
- wlistentries = wlist->num_widgets + 1;
- } else {
- wlist = NULL;
- wlistentries = 1;
- }
-
- wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
- wlistentries * sizeof(struct snd_soc_dapm_widget *);
- wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
- if (wlist == NULL) {
- dev_err(dapm->dev, "ASoC: can't allocate widget list for %s\n",
- w->name);
- return -ENOMEM;
- }
- wlist->num_widgets = wlistentries;
- wlist->widgets[wlistentries - 1] = w;
-
if (!kcontrol) {
if (shared) {
wname_in_long_name = false;
@@ -587,7 +715,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
kcname_in_long_name = false;
break;
default:
- kfree(wlist);
return -EINVAL;
}
}
@@ -602,10 +729,8 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
long_name = kasprintf(GFP_KERNEL, "%s %s",
w->name + prefix_len,
w->kcontrol_news[kci].name);
- if (long_name == NULL) {
- kfree(wlist);
+ if (long_name == NULL)
return -ENOMEM;
- }
name = long_name;
} else if (wname_in_long_name) {
@@ -616,23 +741,33 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
name = w->kcontrol_news[kci].name;
}
- kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name,
+ kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name,
prefix);
- kcontrol->private_free = dapm_kcontrol_free;
kfree(long_name);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->private_free = dapm_kcontrol_free;
+
+ ret = dapm_kcontrol_data_alloc(w, kcontrol);
+ if (ret) {
+ snd_ctl_free_one(kcontrol);
+ return ret;
+ }
+
ret = snd_ctl_add(card, kcontrol);
if (ret < 0) {
dev_err(dapm->dev,
"ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
w->name, name, ret);
- kfree(wlist);
return ret;
}
}
- kcontrol->private_data = wlist;
+ ret = dapm_kcontrol_add_widget(kcontrol, w);
+ if (ret)
+ return ret;
+
w->kcontrols[kci] = kcontrol;
- path->kcontrol = kcontrol;
return 0;
}
@@ -652,13 +787,15 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
continue;
if (w->kcontrols[i]) {
- path->kcontrol = w->kcontrols[i];
+ dapm_kcontrol_add_path(w->kcontrols[i], path);
continue;
}
- ret = dapm_create_or_share_mixmux_kcontrol(w, i, path);
+ ret = dapm_create_or_share_mixmux_kcontrol(w, i);
if (ret < 0)
return ret;
+
+ dapm_kcontrol_add_path(w->kcontrols[i], path);
}
}
@@ -684,15 +821,12 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
return -EINVAL;
}
- path = list_first_entry(&w->sources, struct snd_soc_dapm_path,
- list_sink);
-
- ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path);
+ ret = dapm_create_or_share_mixmux_kcontrol(w, 0);
if (ret < 0)
return ret;
list_for_each_entry(path, &w->sources, list_sink)
- path->kcontrol = w->kcontrols[0];
+ dapm_kcontrol_add_path(w->kcontrols[0], path);
return 0;
}
@@ -813,6 +947,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
+ case snd_soc_dapm_kcontrol:
return 0;
default:
break;
@@ -908,6 +1043,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
+ case snd_soc_dapm_kcontrol:
return 0;
default:
break;
@@ -1062,7 +1198,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
int ret;
if (SND_SOC_DAPM_EVENT_ON(event)) {
- if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+ if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
ret = regulator_allow_bypass(w->regulator, false);
if (ret != 0)
dev_warn(w->dapm->dev,
@@ -1072,7 +1208,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
return regulator_enable(w->regulator);
} else {
- if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+ if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
ret = regulator_allow_bypass(w->regulator, true);
if (ret != 0)
dev_warn(w->dapm->dev,
@@ -1244,10 +1380,9 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
list_add_tail(&new_widget->power_list, list);
}
-static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm,
+static void dapm_seq_check_event(struct snd_soc_card *card,
struct snd_soc_dapm_widget *w, int event)
{
- struct snd_soc_card *card = dapm->card;
const char *ev_name;
int power, ret;
@@ -1281,55 +1416,50 @@ static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm,
return;
}
- if (w->power != power)
+ if (w->new_power != power)
return;
if (w->event && (w->event_flags & event)) {
- pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n",
+ pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n",
w->name, ev_name);
trace_snd_soc_dapm_widget_event_start(w, event);
ret = w->event(w, NULL, event);
trace_snd_soc_dapm_widget_event_done(w, event);
if (ret < 0)
- dev_err(dapm->dev, "ASoC: %s: %s event failed: %d\n",
+ dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n",
ev_name, w->name, ret);
}
}
/* Apply the coalesced changes from a DAPM sequence */
-static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
+static void dapm_seq_run_coalesced(struct snd_soc_card *card,
struct list_head *pending)
{
- struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
- int reg, power;
+ int reg;
unsigned int value = 0;
unsigned int mask = 0;
- unsigned int cur_mask;
reg = list_first_entry(pending, struct snd_soc_dapm_widget,
power_list)->reg;
list_for_each_entry(w, pending, power_list) {
- cur_mask = 1 << w->shift;
BUG_ON(reg != w->reg);
+ w->power = w->new_power;
- if (w->invert)
- power = !w->power;
+ mask |= w->mask << w->shift;
+ if (w->power)
+ value |= w->on_val << w->shift;
else
- power = w->power;
+ value |= w->off_val << w->shift;
- mask |= cur_mask;
- if (power)
- value |= cur_mask;
-
- pop_dbg(dapm->dev, card->pop_time,
+ pop_dbg(w->dapm->dev, card->pop_time,
"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
w->name, reg, value, mask);
/* Check for events */
- dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU);
- dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD);
+ dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU);
+ dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD);
}
if (reg >= 0) {
@@ -1339,7 +1469,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
w = list_first_entry(pending, struct snd_soc_dapm_widget,
power_list);
- pop_dbg(dapm->dev, card->pop_time,
+ pop_dbg(w->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);
@@ -1347,8 +1477,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
}
list_for_each_entry(w, pending, power_list) {
- dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU);
- dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD);
+ dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU);
+ dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD);
}
}
@@ -1360,8 +1490,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
* Currently anything that requires more than a single write is not
* handled.
*/
-static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
- struct list_head *list, int event, bool power_up)
+static void dapm_seq_run(struct snd_soc_card *card,
+ struct list_head *list, int event, bool power_up)
{
struct snd_soc_dapm_widget *w, *n;
LIST_HEAD(pending);
@@ -1384,7 +1514,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
if (sort[w->id] != cur_sort || w->reg != cur_reg ||
w->dapm != cur_dapm || w->subseq != cur_subseq) {
if (!list_empty(&pending))
- dapm_seq_run_coalesced(cur_dapm, &pending);
+ dapm_seq_run_coalesced(card, &pending);
if (cur_dapm && cur_dapm->seq_notifier) {
for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
@@ -1444,7 +1574,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
}
if (!list_empty(&pending))
- dapm_seq_run_coalesced(cur_dapm, &pending);
+ dapm_seq_run_coalesced(card, &pending);
if (cur_dapm && cur_dapm->seq_notifier) {
for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
@@ -1454,37 +1584,48 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
}
}
-static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
+static void dapm_widget_update(struct snd_soc_card *card)
{
- struct snd_soc_dapm_update *update = dapm->update;
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_update *update = card->update;
+ struct snd_soc_dapm_widget_list *wlist;
+ struct snd_soc_dapm_widget *w = NULL;
+ unsigned int wi;
int ret;
- if (!update)
+ if (!update || !dapm_kcontrol_is_powered(update->kcontrol))
return;
- w = update->widget;
+ wlist = dapm_kcontrol_get_wlist(update->kcontrol);
- if (w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_REG)) {
- ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);
- if (ret != 0)
- dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n",
- w->name, ret);
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ w = wlist->widgets[wi];
+
+ if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) {
+ ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);
+ if (ret != 0)
+ dev_err(w->dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n",
+ w->name, ret);
+ }
}
+ if (!w)
+ return;
+
ret = soc_widget_update_bits_locked(w, update->reg, update->mask,
update->val);
if (ret < 0)
- dev_err(dapm->dev, "ASoC: %s DAPM update failed: %d\n",
+ dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
w->name, ret);
- if (w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_REG)) {
- ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);
- if (ret != 0)
- dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n",
- w->name, ret);
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ w = wlist->widgets[wi];
+
+ if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) {
+ ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);
+ if (ret != 0)
+ dev_err(w->dapm->dev, "ASoC: %s DAPM post-event failed: %d\n",
+ w->name, ret);
+ }
}
}
@@ -1596,6 +1737,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
+ case snd_soc_dapm_kcontrol:
/* Supplies can't affect their outputs, only their inputs */
break;
default:
@@ -1612,8 +1754,6 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
dapm_seq_insert(w, up_list, true);
else
dapm_seq_insert(w, down_list, false);
-
- w->power = power;
}
static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
@@ -1647,9 +1787,8 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
* o Input pin to Output pin (bypass, sidetone)
* o DAC to ADC (loopback).
*/
-static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
+static int dapm_power_widgets(struct snd_soc_card *card, int event)
{
- struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
struct snd_soc_dapm_context *d;
LIST_HEAD(up_list);
@@ -1689,7 +1828,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
break;
}
- if (w->power) {
+ if (w->new_power) {
d = w->dapm;
/* Supplies and micbiases only bring the
@@ -1731,29 +1870,29 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
trace_snd_soc_dapm_walk_done(card);
/* Run all the bias changes in parallel */
- list_for_each_entry(d, &dapm->card->dapm_list, list)
+ list_for_each_entry(d, &card->dapm_list, list)
async_schedule_domain(dapm_pre_sequence_async, d,
&async_domain);
async_synchronize_full_domain(&async_domain);
list_for_each_entry(w, &down_list, power_list) {
- dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMD);
+ dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD);
}
list_for_each_entry(w, &up_list, power_list) {
- dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMU);
+ dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU);
}
/* Power down widgets first; try to avoid amplifying pops. */
- dapm_seq_run(dapm, &down_list, event, false);
+ dapm_seq_run(card, &down_list, event, false);
- dapm_widget_update(dapm);
+ dapm_widget_update(card);
/* Now power up. */
- dapm_seq_run(dapm, &up_list, event, true);
+ dapm_seq_run(card, &up_list, event, true);
/* Run all the bias changes in parallel */
- list_for_each_entry(d, &dapm->card->dapm_list, list)
+ list_for_each_entry(d, &card->dapm_list, list)
async_schedule_domain(dapm_post_sequence_async, d,
&async_domain);
async_synchronize_full_domain(&async_domain);
@@ -1764,7 +1903,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
d->stream_event(d, event);
}
- pop_dbg(dapm->dev, card->pop_time,
+ pop_dbg(card->dev, card->pop_time,
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
pop_wait(card->pop_time);
@@ -1799,8 +1938,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if (w->reg >= 0)
ret += snprintf(buf + ret, PAGE_SIZE - ret,
- " - R%d(0x%x) bit %d",
- w->reg, w->reg, w->shift);
+ " - R%d(0x%x) mask 0x%x",
+ w->reg, w->reg, w->mask << w->shift);
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
@@ -1937,22 +2076,14 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif
/* test and update the power status of a mux widget */
-static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mux_update_power(struct snd_soc_card *card,
struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
- if (widget->id != snd_soc_dapm_mux &&
- widget->id != snd_soc_dapm_virt_mux &&
- widget->id != snd_soc_dapm_value_mux)
- return -ENODEV;
-
/* find dapm widget path assoc with kcontrol */
- list_for_each_entry(path, &widget->dapm->card->paths, list) {
- if (path->kcontrol != kcontrol)
- continue;
-
+ dapm_kcontrol_for_each_path(path, kcontrol) {
if (!path->name || !e->texts[mux])
continue;
@@ -1967,73 +2098,68 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
"mux disconnection");
path->connect = 0; /* old connection must be powered down */
}
+ dapm_mark_dirty(path->sink, "mux change");
}
- if (found) {
- dapm_mark_dirty(widget, "mux change");
- dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
- }
+ if (found)
+ dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
return found;
}
-int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
+ struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e,
+ struct snd_soc_dapm_update *update)
{
- struct snd_soc_card *card = widget->dapm->card;
+ struct snd_soc_card *card = dapm->card;
int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ card->update = update;
+ ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);
+ card->update = NULL;
mutex_unlock(&card->dapm_mutex);
if (ret > 0)
- soc_dpcm_runtime_update(widget);
+ soc_dpcm_runtime_update(card);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
/* test and update the power status of a mixer or switch widget */
-static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
struct snd_kcontrol *kcontrol, int connect)
{
struct snd_soc_dapm_path *path;
int found = 0;
- if (widget->id != snd_soc_dapm_mixer &&
- widget->id != snd_soc_dapm_mixer_named_ctl &&
- widget->id != snd_soc_dapm_switch)
- return -ENODEV;
-
/* find dapm widget path assoc with kcontrol */
- list_for_each_entry(path, &widget->dapm->card->paths, list) {
- if (path->kcontrol != kcontrol)
- continue;
-
- /* found, now check type */
+ dapm_kcontrol_for_each_path(path, kcontrol) {
found = 1;
path->connect = connect;
dapm_mark_dirty(path->source, "mixer connection");
+ dapm_mark_dirty(path->sink, "mixer update");
}
- if (found) {
- dapm_mark_dirty(widget, "mixer update");
- dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
- }
+ if (found)
+ dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
return found;
}
-int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *kcontrol, int connect)
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
+ struct snd_kcontrol *kcontrol, int connect,
+ struct snd_soc_dapm_update *update)
{
- struct snd_soc_card *card = widget->dapm->card;
+ struct snd_soc_card *card = dapm->card;
int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
+ card->update = update;
+ ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+ card->update = NULL;
mutex_unlock(&card->dapm_mutex);
if (ret > 0)
- soc_dpcm_runtime_update(widget);
+ soc_dpcm_runtime_update(card);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
@@ -2112,6 +2238,7 @@ static void dapm_free_path(struct snd_soc_dapm_path *path)
{
list_del(&path->list_sink);
list_del(&path->list_source);
+ list_del(&path->list_kcontrol);
list_del(&path->list);
kfree(path);
}
@@ -2206,70 +2333,20 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
return 0;
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+ ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);
mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
-static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
- const struct snd_soc_dapm_route *route)
+static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
+ const char *control,
+ int (*connected)(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink))
{
struct snd_soc_dapm_path *path;
- struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
- struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
- const char *sink;
- const char *control = route->control;
- const char *source;
- char prefixed_sink[80];
- char prefixed_source[80];
- int ret = 0;
-
- if (dapm->codec && dapm->codec->name_prefix) {
- snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
- dapm->codec->name_prefix, route->sink);
- sink = prefixed_sink;
- snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
- dapm->codec->name_prefix, route->source);
- source = prefixed_source;
- } else {
- sink = route->sink;
- source = route->source;
- }
-
- /*
- * find src and dest widgets over all widgets but favor a widget from
- * current DAPM context
- */
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (!wsink && !(strcmp(w->name, sink))) {
- wtsink = w;
- if (w->dapm == dapm)
- wsink = w;
- continue;
- }
- if (!wsource && !(strcmp(w->name, source))) {
- wtsource = w;
- if (w->dapm == dapm)
- wsource = w;
- }
- }
- /* use widget from another DAPM context if not found from this */
- if (!wsink)
- wsink = wtsink;
- if (!wsource)
- wsource = wtsource;
-
- if (wsource == NULL) {
- dev_err(dapm->dev, "ASoC: no source widget found for %s\n",
- route->source);
- return -ENODEV;
- }
- if (wsink == NULL) {
- dev_err(dapm->dev, "ASoC: no sink widget found for %s\n",
- route->sink);
- return -ENODEV;
- }
+ int ret;
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if (!path)
@@ -2277,8 +2354,9 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
path->source = wsource;
path->sink = wsink;
- path->connected = route->connected;
+ path->connected = connected;
INIT_LIST_HEAD(&path->list);
+ INIT_LIST_HEAD(&path->list_kcontrol);
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
@@ -2298,6 +2376,9 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
wsource->ext = 1;
}
+ dapm_mark_dirty(wsource, "Route added");
+ dapm_mark_dirty(wsink, "Route added");
+
/* connect static paths */
if (control == NULL) {
list_add(&path->list, &dapm->card->paths);
@@ -2328,6 +2409,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_kcontrol:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
@@ -2359,15 +2441,78 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
return 0;
}
- dapm_mark_dirty(wsource, "Route added");
- dapm_mark_dirty(wsink, "Route added");
-
return 0;
+err:
+ kfree(path);
+ return ret;
+}
+
+static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_route *route)
+{
+ struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+ struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
+ const char *sink;
+ const char *source;
+ char prefixed_sink[80];
+ char prefixed_source[80];
+ int ret;
+
+ if (dapm->codec && dapm->codec->name_prefix) {
+ snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
+ dapm->codec->name_prefix, route->sink);
+ sink = prefixed_sink;
+ snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
+ dapm->codec->name_prefix, route->source);
+ source = prefixed_source;
+ } else {
+ sink = route->sink;
+ source = route->source;
+ }
+
+ /*
+ * find src and dest widgets over all widgets but favor a widget from
+ * current DAPM context
+ */
+ list_for_each_entry(w, &dapm->card->widgets, list) {
+ if (!wsink && !(strcmp(w->name, sink))) {
+ wtsink = w;
+ if (w->dapm == dapm)
+ wsink = w;
+ continue;
+ }
+ if (!wsource && !(strcmp(w->name, source))) {
+ wtsource = w;
+ if (w->dapm == dapm)
+ wsource = w;
+ }
+ }
+ /* use widget from another DAPM context if not found from this */
+ if (!wsink)
+ wsink = wtsink;
+ if (!wsource)
+ wsource = wtsource;
+ if (wsource == NULL) {
+ dev_err(dapm->dev, "ASoC: no source widget found for %s\n",
+ route->source);
+ return -ENODEV;
+ }
+ if (wsink == NULL) {
+ dev_err(dapm->dev, "ASoC: no sink widget found for %s\n",
+ route->sink);
+ return -ENODEV;
+ }
+
+ ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
+ route->connected);
+ if (ret)
+ goto err;
+
+ return 0;
err:
dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n",
- source, control, sink);
- kfree(path);
+ source, route->control, sink);
return ret;
}
@@ -2569,14 +2714,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
*
* Returns 0 for success.
*/
-int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
+int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
{
struct snd_soc_dapm_widget *w;
unsigned int val;
- mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
- list_for_each_entry(w, &dapm->card->widgets, list)
+ list_for_each_entry(w, &card->widgets, list)
{
if (w->new)
continue;
@@ -2586,7 +2731,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
sizeof(struct snd_kcontrol *),
GFP_KERNEL);
if (!w->kcontrols) {
- mutex_unlock(&dapm->card->dapm_mutex);
+ mutex_unlock(&card->dapm_mutex);
return -ENOMEM;
}
}
@@ -2612,12 +2757,9 @@ 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 = soc_widget_read(w, w->reg);
- val &= 1 << w->shift;
- if (w->invert)
- val = !val;
-
- if (val)
+ val = soc_widget_read(w, w->reg) >> w->shift;
+ val &= w->mask;
+ if (val == w->on_val)
w->power = 1;
}
@@ -2627,8 +2769,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
dapm_debugfs_add_widget(w);
}
- dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
- mutex_unlock(&dapm->card->dapm_mutex);
+ dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
+ mutex_unlock(&card->dapm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -2645,8 +2787,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_card *card = codec->card;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -2654,17 +2796,24 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
+ unsigned int val;
if (snd_soc_volsw_is_stereo(mc))
- dev_warn(widget->dapm->dev,
+ dev_warn(codec->dapm.dev,
"ASoC: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name);
- ucontrol->value.integer.value[0] =
- (snd_soc_read(widget->codec, reg) >> shift) & mask;
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ if (dapm_kcontrol_is_powered(kcontrol))
+ val = (snd_soc_read(codec, reg) >> shift) & mask;
+ else
+ val = dapm_kcontrol_get_value(kcontrol);
+ mutex_unlock(&card->dapm_mutex);
+
if (invert)
- ucontrol->value.integer.value[0] =
- max - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[0] = max - val;
+ else
+ ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -2682,9 +2831,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct snd_soc_card *card = codec->card;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
@@ -2696,10 +2843,9 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
- int wi;
if (snd_soc_volsw_is_stereo(mc))
- dev_warn(widget->dapm->dev,
+ dev_warn(codec->dapm.dev,
"ASoC: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name);
@@ -2708,29 +2854,26 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
if (invert)
val = max - val;
- mask = mask << shift;
- val = val << shift;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = snd_soc_test_bits(widget->codec, reg, mask, val);
- if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
+ dapm_kcontrol_set_value(kcontrol, val);
- widget->value = val;
+ mask = mask << shift;
+ val = val << shift;
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ change = snd_soc_test_bits(codec, reg, mask, val);
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
- soc_dapm_mixer_update_power(widget, kcontrol, connect);
+ card->update = &update;
- widget->dapm->update = NULL;
- }
+ soc_dapm_mixer_update_power(card, kcontrol, connect);
+
+ card->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
@@ -2750,12 +2893,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
- val = snd_soc_read(widget->codec, e->reg);
+ val = snd_soc_read(codec, e->reg);
ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask;
if (e->shift_l != e->shift_r)
ucontrol->value.enumerated.item[1] =
@@ -2777,15 +2919,12 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct snd_soc_card *card = codec->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
- int wi;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -2801,24 +2940,17 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ change = snd_soc_test_bits(codec, e->reg, mask, val);
if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
-
- widget->value = val;
+ update.kcontrol = kcontrol;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ card->update = &update;
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ soc_dapm_mux_update_power(card, kcontrol, mux, e);
- soc_dapm_mux_update_power(widget, kcontrol, mux, e);
-
- widget->dapm->update = NULL;
- }
+ card->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
@@ -2836,11 +2968,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-
- ucontrol->value.enumerated.item[0] = widget->value;
-
+ ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
@@ -2855,30 +2983,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct snd_soc_card *card = codec->card;
+ unsigned int value;
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
- int wi;
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = widget->value != ucontrol->value.enumerated.item[0];
- if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
-
- widget->value = ucontrol->value.enumerated.item[0];
-
- soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
- }
- }
+ value = ucontrol->value.enumerated.item[0];
+ change = dapm_kcontrol_set_value(kcontrol, value);
+ if (change)
+ soc_dapm_mux_update_power(card, kcontrol, value, e);
mutex_unlock(&card->dapm_mutex);
return change;
@@ -2901,12 +3021,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_val, val, mux;
- reg_val = snd_soc_read(widget->codec, e->reg);
+ reg_val = snd_soc_read(codec, e->reg);
val = (reg_val >> e->shift_l) & e->mask;
for (mux = 0; mux < e->max; mux++) {
if (val == e->values[mux])
@@ -2942,15 +3061,12 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct snd_soc_card *card = codec->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
- int wi;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -2966,24 +3082,17 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ change = snd_soc_test_bits(codec, e->reg, mask, val);
if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
-
- widget->value = val;
+ update.kcontrol = kcontrol;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ card->update = &update;
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ soc_dapm_mux_update_power(card, kcontrol, mux, e);
- soc_dapm_mux_update_power(widget, kcontrol, mux, e);
-
- widget->dapm->update = NULL;
- }
+ card->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
@@ -3080,7 +3189,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
return NULL;
}
- if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+ if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
ret = regulator_allow_bypass(w->regulator, true);
if (ret != 0)
dev_warn(w->dapm->dev,
@@ -3127,16 +3236,16 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_value_mux:
w->power_check = dapm_generic_check_power;
break;
- case snd_soc_dapm_adc:
- case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai_out:
w->power_check = dapm_adc_check_power;
break;
- case snd_soc_dapm_dac:
- case snd_soc_dapm_aif_in:
case snd_soc_dapm_dai_in:
w->power_check = dapm_dac_check_power;
break;
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_aif_in:
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_input:
@@ -3152,6 +3261,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
+ case snd_soc_dapm_kcontrol:
w->power_check = dapm_supply_check_power;
break;
default:
@@ -3416,9 +3526,6 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
{
struct snd_soc_dapm_widget *dai_w, *w;
struct snd_soc_dai *dai;
- struct snd_soc_dapm_route r;
-
- memset(&r, 0, sizeof(r));
/* For each DAI widget... */
list_for_each_entry(dai_w, &card->widgets, list) {
@@ -3445,29 +3552,27 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
break;
}
- if (!w->sname)
+ if (!w->sname || !strstr(w->sname, dai_w->name))
continue;
if (dai->driver->playback.stream_name &&
strstr(w->sname,
dai->driver->playback.stream_name)) {
- r.source = dai->playback_widget->name;
- r.sink = w->name;
dev_dbg(dai->dev, "%s -> %s\n",
- r.source, r.sink);
+ dai->playback_widget->name, w->name);
- snd_soc_dapm_add_route(w->dapm, &r);
+ snd_soc_dapm_add_path(w->dapm,
+ dai->playback_widget, w, NULL, NULL);
}
if (dai->driver->capture.stream_name &&
strstr(w->sname,
dai->driver->capture.stream_name)) {
- r.source = w->name;
- r.sink = dai->capture_widget->name;
dev_dbg(dai->dev, "%s -> %s\n",
- r.source, r.sink);
+ w->name, dai->capture_widget->name);
- snd_soc_dapm_add_route(w->dapm, &r);
+ snd_soc_dapm_add_path(w->dapm, w,
+ dai->capture_widget, NULL, NULL);
}
}
}
@@ -3529,7 +3634,7 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
}
}
- dapm_power_widgets(&rtd->card->dapm, event);
+ dapm_power_widgets(rtd->card, event);
}
/**
@@ -3798,7 +3903,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
if (dapm->bias_level == SND_SOC_BIAS_ON)
snd_soc_dapm_set_bias_level(dapm,
SND_SOC_BIAS_PREPARE);
- dapm_seq_run(dapm, &down_list, 0, false);
+ dapm_seq_run(card, &down_list, 0, false);
if (dapm->bias_level == SND_SOC_BIAS_PREPARE)
snd_soc_dapm_set_bias_level(dapm,
SND_SOC_BIAS_STANDBY);
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 0bb5cccd7766..71358e3b54d9 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -183,8 +183,6 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
list_add(&(pins[i].list), &jack->pins);
}
- snd_soc_dapm_new_widgets(&jack->codec->card->dapm);
-
/* Update to reflect the last reported status; canned jack
* implementations are likely to set their state before the
* card has an opportunity to associate pins.
@@ -263,7 +261,7 @@ static irqreturn_t gpio_handler(int irq, void *data)
if (device_may_wakeup(dev))
pm_wakeup_event(dev, gpio->debounce_time + 50);
- schedule_delayed_work(&gpio->work,
+ queue_delayed_work(system_power_efficient_wq, &gpio->work,
msecs_to_jiffies(gpio->debounce_time));
return IRQ_HANDLED;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index b6c640332a17..330c9a6b5cb5 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -411,8 +411,9 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
} else {
/* start delayed pop wq here for playback streams */
rtd->pop_wait = 1;
- schedule_delayed_work(&rtd->delayed_work,
- msecs_to_jiffies(rtd->pmdown_time));
+ queue_delayed_work(system_power_efficient_wq,
+ &rtd->delayed_work,
+ msecs_to_jiffies(rtd->pmdown_time));
}
} else {
/* capture streams can be powered down now */
@@ -1832,18 +1833,10 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
* any DAI links.
*/
-int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget)
+int soc_dpcm_runtime_update(struct snd_soc_card *card)
{
- struct snd_soc_card *card;
int i, old, new, paths;
- if (widget->codec)
- card = widget->codec->card;
- else if (widget->platform)
- card = widget->platform->card;
- else
- return -EINVAL;
-
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dapm_widget_list *list;
@@ -2027,6 +2020,16 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
capture = 1;
}
+ if (rtd->dai_link->playback_only) {
+ playback = 1;
+ capture = 0;
+ }
+
+ if (rtd->dai_link->capture_only) {
+ playback = 0;
+ capture = 1;
+ }
+
/* create the PCM */
if (rtd->dai_link->no_pcm) {
snprintf(new_name, sizeof(new_name), "(%s)",
diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig
index 3567d73b218e..0a53053495f3 100644
--- a/sound/soc/spear/Kconfig
+++ b/sound/soc/spear/Kconfig
@@ -1,6 +1,6 @@
config SND_SPEAR_SOC
tristate
- select SND_SOC_DMAENGINE_PCM
+ select SND_DMAENGINE_PCM
config SND_SPEAR_SPDIF_OUT
tristate
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 995b120c2cd0..8fc653ca3ab4 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -1,8 +1,8 @@
config SND_SOC_TEGRA
tristate "SoC Audio for the Tegra System-on-Chip"
- depends on ARCH_TEGRA && TEGRA20_APB_DMA
+ depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
select REGMAP_MMIO
- select SND_SOC_GENERIC_DMAENGINE_PCM if TEGRA20_APB_DMA
+ select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M here if you want support for SoC audio on Tegra.
@@ -61,7 +61,7 @@ config SND_SOC_TEGRA30_I2S
config SND_SOC_TEGRA_RT5640
tristate "SoC Audio support for Tegra boards using an RT5640 codec"
- depends on SND_SOC_TEGRA && I2C
+ depends on SND_SOC_TEGRA && I2C && GPIOLIB
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
select SND_SOC_RT5640
@@ -71,7 +71,7 @@ config SND_SOC_TEGRA_RT5640
config SND_SOC_TEGRA_WM8753
tristate "SoC Audio support for Tegra boards using a WM8753 codec"
- depends on SND_SOC_TEGRA && I2C
+ depends on SND_SOC_TEGRA && I2C && GPIOLIB
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
select SND_SOC_WM8753
@@ -81,7 +81,7 @@ config SND_SOC_TEGRA_WM8753
config SND_SOC_TEGRA_WM8903
tristate "SoC Audio support for Tegra boards using a WM8903 codec"
- depends on SND_SOC_TEGRA && I2C
+ depends on SND_SOC_TEGRA && I2C && GPIOLIB
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
select SND_SOC_WM8903
@@ -92,7 +92,7 @@ config SND_SOC_TEGRA_WM8903
config SND_SOC_TEGRA_WM9712
tristate "SoC Audio support for Tegra boards using a WM9712 codec"
- depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+ depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC && GPIOLIB
select SND_SOC_TEGRA20_AC97
select SND_SOC_WM9712
help
@@ -110,7 +110,7 @@ config SND_SOC_TEGRA_TRIMSLICE
config SND_SOC_TEGRA_ALC5632
tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
- depends on SND_SOC_TEGRA && I2C
+ depends on SND_SOC_TEGRA && I2C && GPIOLIB
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
select SND_SOC_ALC5632
help
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 6c486625321b..ae27bcd586d2 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -334,12 +334,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
-
regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
@@ -432,8 +426,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
return 0;
-err_unregister_pcm:
- tegra_pcm_platform_unregister(&pdev->dev);
err_unregister_component:
snd_soc_unregister_component(&pdev->dev);
err_asoc_utils_fini:
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index 48d05d9e1002..c61ea3a1030f 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -13,8 +13,6 @@
* published by the Free Software Foundation.
*/
-#include <asm/mach-types.h>
-
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index 08794f915a94..4511c5a875ec 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -99,6 +99,7 @@ static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = {
static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
};
static const struct snd_kcontrol_new tegra_rt5640_controls[] = {
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
index f87fc53e9b8c..8e774d1a243c 100644
--- a/sound/soc/tegra/tegra_wm8753.c
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -28,8 +28,6 @@
*
*/
-#include <asm/mach-types.h>
-
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index 05c68aab5cf0..734bfcd21148 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -24,8 +24,6 @@
*
*/
-#include <asm/mach-types.h>
-
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 4bcce8a3cded..e0305a148568 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -184,9 +184,6 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -EBUSY;
-
drvdata->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(drvdata->base))
return PTR_ERR(drvdata->base);
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index 8f5cd00a6e46..178d1bad6259 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -52,6 +52,7 @@ static struct snd_soc_dai_link mop500_dai_links[] = {
static struct snd_soc_card mop500_card = {
.name = "MOP500-card",
+ .owner = THIS_MODULE,
.probe = NULL,
.dai_link = mop500_dai_links,
.num_links = ARRAY_SIZE(mop500_dai_links),
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
index b9defcdeb7ef..780bf3f62d28 100644
--- a/sound/usb/6fire/firmware.c
+++ b/sound/usb/6fire/firmware.c
@@ -346,10 +346,10 @@ static int usb6fire_fw_check(u8 *version)
if (!memcmp(version, known_fw_versions + i, 2))
return 0;
- snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %*ph. "
+ snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %4ph. "
"please reconnect to power. if this failure "
"still happens, check your firmware installation.",
- 4, version);
+ version);
return -EINVAL;
}
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 659950e5b94f..93e970f2b3c0 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -418,6 +418,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep;
int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+ if (WARN_ON(!alts))
+ return NULL;
+
mutex_lock(&chip->mutex);
list_for_each_entry(ep, &chip->ep_list, list) {
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 15b151ed4899..b375d58871e7 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -327,6 +327,137 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
return 0;
}
+static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
+ struct usb_device *dev,
+ struct usb_interface_descriptor *altsd,
+ unsigned int attr)
+{
+ struct usb_host_interface *alts;
+ struct usb_interface *iface;
+ unsigned int ep;
+
+ /* Implicit feedback sync EPs consumers are always playback EPs */
+ if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ switch (subs->stream->chip->usb_id) {
+ case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
+ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
+ ep = 0x81;
+ iface = usb_ifnum_to_if(dev, 3);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
+ break;
+ case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+ case USB_ID(0x0763, 0x2081):
+ ep = 0x81;
+ iface = usb_ifnum_to_if(dev, 2);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
+ }
+ if (attr == USB_ENDPOINT_SYNC_ASYNC &&
+ altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+ altsd->bInterfaceProtocol == 2 &&
+ altsd->bNumEndpoints == 1 &&
+ USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
+ search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
+ altsd->bAlternateSetting,
+ &alts, &ep) >= 0) {
+ goto add_sync_ep;
+ }
+
+ /* No quirk */
+ return 0;
+
+add_sync_ep:
+ subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+ alts, ep, !subs->direction,
+ SND_USB_ENDPOINT_TYPE_DATA);
+ if (!subs->sync_endpoint)
+ return -EINVAL;
+
+ subs->data_endpoint->sync_master = subs->sync_endpoint;
+
+ return 0;
+}
+
+static int set_sync_endpoint(struct snd_usb_substream *subs,
+ struct audioformat *fmt,
+ struct usb_device *dev,
+ struct usb_host_interface *alts,
+ struct usb_interface_descriptor *altsd)
+{
+ int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int ep, attr;
+ bool implicit_fb;
+ int err;
+
+ /* we need a sync pipe in async OUT or adaptive IN mode */
+ /* check the number of EP, since some devices have broken
+ * descriptors which fool us. if it has only one EP,
+ * assume it as adaptive-out or sync-in.
+ */
+ attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+
+ err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr);
+ if (err < 0)
+ return err;
+
+ if (altsd->bNumEndpoints < 2)
+ return 0;
+
+ if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) ||
+ (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE))
+ return 0;
+
+ /* check sync-pipe endpoint */
+ /* ... and check descriptor size before accessing bSynchAddress
+ because there is a version of the SB Audigy 2 NX firmware lacking
+ the audio fields in the endpoint descriptors */
+ if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
+ (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+ get_endpoint(alts, 1)->bSynchAddress != 0)) {
+ snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
+ dev->devnum, fmt->iface, fmt->altsetting,
+ get_endpoint(alts, 1)->bmAttributes,
+ get_endpoint(alts, 1)->bLength,
+ get_endpoint(alts, 1)->bSynchAddress);
+ return -EINVAL;
+ }
+ ep = get_endpoint(alts, 1)->bEndpointAddress;
+ if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+ ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
+ (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+ snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
+ dev->devnum, fmt->iface, fmt->altsetting,
+ is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
+ return -EINVAL;
+ }
+
+ implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
+ == USB_ENDPOINT_USAGE_IMPLICIT_FB;
+
+ subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+ alts, ep, !subs->direction,
+ implicit_fb ?
+ SND_USB_ENDPOINT_TYPE_DATA :
+ SND_USB_ENDPOINT_TYPE_SYNC);
+ if (!subs->sync_endpoint)
+ return -EINVAL;
+
+ subs->data_endpoint->sync_master = subs->sync_endpoint;
+
+ return 0;
+}
+
/*
* find a matching format and set up the interface
*/
@@ -336,9 +467,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
struct usb_interface *iface;
- unsigned int ep, attr;
- int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
- int err, implicit_fb = 0;
+ int err;
iface = usb_ifnum_to_if(dev, fmt->iface);
if (WARN_ON(!iface))
@@ -383,118 +512,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
alts, fmt->endpoint, subs->direction,
SND_USB_ENDPOINT_TYPE_DATA);
+
if (!subs->data_endpoint)
return -EINVAL;
- /* we need a sync pipe in async OUT or adaptive IN mode */
- /* check the number of EP, since some devices have broken
- * descriptors which fool us. if it has only one EP,
- * assume it as adaptive-out or sync-in.
- */
- attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
-
- switch (subs->stream->chip->usb_id) {
- case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
- case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
- if (is_playback) {
- implicit_fb = 1;
- ep = 0x81;
- iface = usb_ifnum_to_if(dev, 3);
-
- if (!iface || iface->num_altsetting == 0)
- return -EINVAL;
-
- alts = &iface->altsetting[1];
- goto add_sync_ep;
- }
- break;
- case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
- case USB_ID(0x0763, 0x2081):
- if (is_playback) {
- implicit_fb = 1;
- ep = 0x81;
- iface = usb_ifnum_to_if(dev, 2);
-
- if (!iface || iface->num_altsetting == 0)
- return -EINVAL;
-
- alts = &iface->altsetting[1];
- goto add_sync_ep;
- }
- }
- if (is_playback &&
- attr == USB_ENDPOINT_SYNC_ASYNC &&
- altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
- altsd->bInterfaceProtocol == 2 &&
- altsd->bNumEndpoints == 1 &&
- USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
- search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
- altsd->bAlternateSetting,
- &alts, &ep) >= 0) {
- implicit_fb = 1;
- goto add_sync_ep;
- }
-
- if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
- (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
- altsd->bNumEndpoints >= 2) {
- /* check sync-pipe endpoint */
- /* ... and check descriptor size before accessing bSynchAddress
- because there is a version of the SB Audigy 2 NX firmware lacking
- the audio fields in the endpoint descriptors */
- if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
- (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
- get_endpoint(alts, 1)->bSynchAddress != 0 &&
- !implicit_fb)) {
- snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
- dev->devnum, fmt->iface, fmt->altsetting,
- get_endpoint(alts, 1)->bmAttributes,
- get_endpoint(alts, 1)->bLength,
- get_endpoint(alts, 1)->bSynchAddress);
- return -EINVAL;
- }
- ep = get_endpoint(alts, 1)->bEndpointAddress;
- if (!implicit_fb &&
- get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
- (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
- (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
- snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
- dev->devnum, fmt->iface, fmt->altsetting,
- is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
- return -EINVAL;
- }
-
- implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
- == USB_ENDPOINT_USAGE_IMPLICIT_FB;
-
-add_sync_ep:
- subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
- alts, ep, !subs->direction,
- implicit_fb ?
- SND_USB_ENDPOINT_TYPE_DATA :
- SND_USB_ENDPOINT_TYPE_SYNC);
- if (!subs->sync_endpoint)
- return -EINVAL;
-
- subs->data_endpoint->sync_master = subs->sync_endpoint;
- }
+ err = set_sync_endpoint(subs, fmt, dev, alts, altsd);
+ if (err < 0)
+ return err;
- if ((err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt)) < 0)
+ err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt);
+ if (err < 0)
return err;
subs->cur_audiofmt = fmt;
snd_usb_set_format_quirk(subs, fmt);
-#if 0
- printk(KERN_DEBUG
- "setting done: format = %d, rate = %d..%d, channels = %d\n",
- fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
- printk(KERN_DEBUG
- " datapipe = 0x%0x, syncpipe = 0x%0x\n",
- subs->datapipe, subs->syncpipe);
-#endif
-
return 0;
}
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 1f9bbd55553f..5a51b18c50fe 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -305,11 +305,9 @@ static void usX2Y_unlinkSeq(struct snd_usX2Y_AsyncSeq *S)
{
int i;
for (i = 0; i < URBS_AsyncSeq; ++i) {
- if (S[i].urb) {
- usb_kill_urb(S->urb[i]);
- usb_free_urb(S->urb[i]);
- S->urb[i] = NULL;
- }
+ usb_kill_urb(S->urb[i]);
+ usb_free_urb(S->urb[i]);
+ S->urb[i] = NULL;
}
kfree(S->buffer);
}