summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/arm/pxa2xx-ac97.c13
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c201
-rw-r--r--sound/arm/pxa2xx-pcm.c12
-rw-r--r--sound/arm/pxa2xx-pcm.h2
-rw-r--r--sound/core/Kconfig13
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/oss/mixer_oss.c3
-rw-r--r--sound/core/pcm.c3
-rw-r--r--sound/core/pcm_lib.c24
-rw-r--r--sound/core/pcm_native.c42
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c6
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.c4
-rw-r--r--sound/firewire/Kconfig27
-rw-r--r--sound/firewire/Makefile5
-rw-r--r--sound/firewire/amdtp-am824.c465
-rw-r--r--sound/firewire/amdtp-am824.h52
-rw-r--r--sound/firewire/amdtp-stream.c (renamed from sound/firewire/amdtp.c)379
-rw-r--r--sound/firewire/amdtp-stream.h (renamed from sound/firewire/amdtp.h)116
-rw-r--r--sound/firewire/bebob/Makefile2
-rw-r--r--sound/firewire/bebob/bebob.c9
-rw-r--r--sound/firewire/bebob/bebob.h34
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c26
-rw-r--r--sound/firewire/bebob/bebob_maudio.c34
-rw-r--r--sound/firewire/bebob/bebob_midi.c16
-rw-r--r--sound/firewire/bebob/bebob_pcm.c16
-rw-r--r--sound/firewire/bebob/bebob_proc.c6
-rw-r--r--sound/firewire/bebob/bebob_stream.c40
-rw-r--r--sound/firewire/bebob/bebob_terratec.c10
-rw-r--r--sound/firewire/bebob/bebob_yamaha.c6
-rw-r--r--sound/firewire/dice/Makefile2
-rw-r--r--sound/firewire/dice/dice-midi.c12
-rw-r--r--sound/firewire/dice/dice-pcm.c12
-rw-r--r--sound/firewire/dice/dice-stream.c34
-rw-r--r--sound/firewire/dice/dice.c3
-rw-r--r--sound/firewire/dice/dice.h2
-rw-r--r--sound/firewire/digi00x/Makefile4
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c442
-rw-r--r--sound/firewire/digi00x/digi00x-hwdep.c200
-rw-r--r--sound/firewire/digi00x/digi00x-midi.c223
-rw-r--r--sound/firewire/digi00x/digi00x-pcm.c373
-rw-r--r--sound/firewire/digi00x/digi00x-proc.c99
-rw-r--r--sound/firewire/digi00x/digi00x-stream.c422
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c137
-rw-r--r--sound/firewire/digi00x/digi00x.c170
-rw-r--r--sound/firewire/digi00x/digi00x.h157
-rw-r--r--sound/firewire/fcp.c2
-rw-r--r--sound/firewire/fireworks/Makefile2
-rw-r--r--sound/firewire/fireworks/fireworks.c12
-rw-r--r--sound/firewire/fireworks/fireworks.h2
-rw-r--r--sound/firewire/fireworks/fireworks_command.c2
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c12
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c12
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c8
-rw-r--r--sound/firewire/lib.c142
-rw-r--r--sound/firewire/lib.h56
-rw-r--r--sound/firewire/oxfw/Makefile2
-rw-r--r--sound/firewire/oxfw/oxfw-midi.c40
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c10
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c53
-rw-r--r--sound/firewire/oxfw/oxfw.c46
-rw-r--r--sound/firewire/oxfw/oxfw.h3
-rw-r--r--sound/firewire/tascam/Makefile4
-rw-r--r--sound/firewire/tascam/amdtp-tascam.c243
-rw-r--r--sound/firewire/tascam/tascam-hwdep.c201
-rw-r--r--sound/firewire/tascam/tascam-midi.c135
-rw-r--r--sound/firewire/tascam/tascam-pcm.c312
-rw-r--r--sound/firewire/tascam/tascam-proc.c88
-rw-r--r--sound/firewire/tascam/tascam-stream.c496
-rw-r--r--sound/firewire/tascam/tascam-transaction.c302
-rw-r--r--sound/firewire/tascam/tascam.c209
-rw-r--r--sound/firewire/tascam/tascam.h147
-rw-r--r--sound/hda/ext/hdac_ext_stream.c9
-rw-r--r--sound/hda/hda_bus_type.c13
-rw-r--r--sound/hda/hdac_bus.c14
-rw-r--r--sound/hda/hdac_device.c124
-rw-r--r--sound/hda/hdac_i915.c70
-rw-r--r--sound/hda/hdac_regmap.c10
-rw-r--r--sound/hda/hdac_stream.c3
-rw-r--r--sound/hda/hdac_sysfs.c8
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c9
-rw-r--r--sound/pci/hda/hda_bind.c69
-rw-r--r--sound/pci/hda/hda_codec.c44
-rw-r--r--sound/pci/hda/hda_codec.h44
-rw-r--r--sound/pci/hda/hda_controller.c1
-rw-r--r--sound/pci/hda/hda_generic.c7
-rw-r--r--sound/pci/hda/hda_local.h7
-rw-r--r--sound/pci/hda/hda_sysfs.c3
-rw-r--r--sound/pci/hda/patch_analog.c37
-rw-r--r--sound/pci/hda/patch_ca0110.c15
-rw-r--r--sound/pci/hda/patch_ca0132.c15
-rw-r--r--sound/pci/hda/patch_cirrus.c22
-rw-r--r--sound/pci/hda/patch_cmedia.c15
-rw-r--r--sound/pci/hda/patch_conexant.c118
-rw-r--r--sound/pci/hda/patch_hdmi.c220
-rw-r--r--sound/pci/hda/patch_realtek.c134
-rw-r--r--sound/pci/hda/patch_si3054.c39
-rw-r--r--sound/pci/hda/patch_sigmatel.c214
-rw-r--r--sound/pci/hda/patch_via.c171
-rw-r--r--sound/pci/korg1212/korg1212.c8
-rw-r--r--sound/pci/lx6464es/lx6464es.c4
-rw-r--r--sound/pci/maestro3.c25
-rw-r--r--sound/pci/rme32.c4
-rw-r--r--sound/pci/rme96.c8
-rw-r--r--sound/pci/rme9652/hdsp.c1
-rw-r--r--sound/pci/rme9652/hdspm.c9
-rw-r--r--sound/soc/Kconfig6
-rw-r--r--sound/soc/Makefile4
-rw-r--r--sound/soc/atmel/Kconfig9
-rw-r--r--sound/soc/atmel/Makefile2
-rw-r--r--sound/soc/atmel/atmel-classd.c679
-rw-r--r--sound/soc/atmel/atmel-classd.h120
-rw-r--r--sound/soc/atmel/atmel_wm8904.c1
-rw-r--r--sound/soc/au1x/db1000.c10
-rw-r--r--sound/soc/au1x/db1200.c10
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c11
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1373.c12
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1701.c12
-rw-r--r--sound/soc/blackfin/bfin-eval-adav80x.c12
-rw-r--r--sound/soc/codecs/Kconfig23
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ad193x-i2c.c8
-rw-r--r--sound/soc/codecs/ad193x-spi.c16
-rw-r--r--sound/soc/codecs/ad193x.c128
-rw-r--r--sound/soc/codecs/ad193x.h9
-rw-r--r--sound/soc/codecs/adav80x.c4
-rw-r--r--sound/soc/codecs/ak4613.c497
-rw-r--r--sound/soc/codecs/ak4642.c153
-rw-r--r--sound/soc/codecs/arizona.c49
-rw-r--r--sound/soc/codecs/arizona.h14
-rw-r--r--sound/soc/codecs/da7213.c190
-rw-r--r--sound/soc/codecs/da7213.h8
-rw-r--r--sound/soc/codecs/da7219-aad.c823
-rw-r--r--sound/soc/codecs/da7219-aad.h212
-rw-r--r--sound/soc/codecs/da7219.c1955
-rw-r--r--sound/soc/codecs/da7219.h820
-rw-r--r--sound/soc/codecs/es8328.c2
-rw-r--r--sound/soc/codecs/hdmi.c109
-rw-r--r--sound/soc/codecs/nau8825.c1309
-rw-r--r--sound/soc/codecs/nau8825.h341
-rw-r--r--sound/soc/codecs/rl6347a.c19
-rw-r--r--sound/soc/codecs/rl6347a.h2
-rw-r--r--sound/soc/codecs/rt286.c9
-rw-r--r--sound/soc/codecs/rt298.c11
-rw-r--r--sound/soc/codecs/rt5640.c22
-rw-r--r--sound/soc/codecs/rt5645.c172
-rw-r--r--sound/soc/codecs/rt5645.h30
-rw-r--r--sound/soc/codecs/ssm2518.c6
-rw-r--r--sound/soc/codecs/tlv320aic3x.c30
-rw-r--r--sound/soc/codecs/twl4030.c13
-rw-r--r--sound/soc/codecs/uda134x.c6
-rw-r--r--sound/soc/codecs/wl1273.c9
-rw-r--r--sound/soc/codecs/wm2000.c4
-rw-r--r--sound/soc/codecs/wm5110.c187
-rw-r--r--sound/soc/codecs/wm8731.c2
-rw-r--r--sound/soc/codecs/wm8903.c2
-rw-r--r--sound/soc/codecs/wm8904.c2
-rw-r--r--sound/soc/codecs/wm8955.c2
-rw-r--r--sound/soc/codecs/wm8960.c2
-rw-r--r--sound/soc/codecs/wm8998.c1430
-rw-r--r--sound/soc/codecs/wm8998.h23
-rw-r--r--sound/soc/davinci/davinci-mcasp.c305
-rw-r--r--sound/soc/dwc/designware_i2s.c123
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c141
-rw-r--r--sound/soc/fsl/fsl_esai.c88
-rw-r--r--sound/soc/fsl/fsl_sai.c38
-rw-r--r--sound/soc/fsl/fsl_spdif.c73
-rw-r--r--sound/soc/fsl/fsl_ssi.c102
-rw-r--r--sound/soc/fsl/imx-spdif.c1
-rw-r--r--sound/soc/generic/simple-card.c8
-rw-r--r--sound/soc/intel/Kconfig15
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c19
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/broadwell.c9
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c14
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c14
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c14
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c14
-rw-r--r--sound/soc/intel/boards/skl_rt286.c259
-rw-r--r--sound/soc/intel/common/Makefile6
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h1
-rw-r--r--sound/soc/intel/common/sst-dsp.c2
-rw-r--r--sound/soc/intel/common/sst-dsp.h2
-rw-r--r--sound/soc/intel/common/sst-firmware.c10
-rw-r--r--sound/soc/intel/skylake/Makefile3
-rw-r--r--sound/soc/intel/skylake/skl-messages.c44
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c10
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c176
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c7
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c12
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h1
-rw-r--r--sound/soc/intel/skylake/skl-sst.c56
-rw-r--r--sound/soc/intel/skylake/skl-topology.c1252
-rw-r--r--sound/soc/intel/skylake/skl-topology.h36
-rw-r--r--sound/soc/intel/skylake/skl-tplg-interface.h83
-rw-r--r--sound/soc/intel/skylake/skl.c32
-rw-r--r--sound/soc/intel/skylake/skl.h17
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c1
-rw-r--r--sound/soc/kirkwood/armada-370-db.c1
-rw-r--r--sound/soc/mediatek/mt8173-max98090.c11
-rw-r--r--sound/soc/mediatek/mt8173-rt5650-rt5676.c11
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c6
-rw-r--r--sound/soc/omap/n810.c3
-rw-r--r--sound/soc/omap/rx51.c5
-rw-r--r--sound/soc/pxa/brownstone.c9
-rw-r--r--sound/soc/pxa/corgi.c11
-rw-r--r--sound/soc/pxa/e740_wm9705.c5
-rw-r--r--sound/soc/pxa/e750_wm9705.c5
-rw-r--r--sound/soc/pxa/e800_wm9712.c5
-rw-r--r--sound/soc/pxa/hx4700.c4
-rw-r--r--sound/soc/pxa/imote2.c11
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c11
-rw-r--r--sound/soc/pxa/palm27x.c9
-rw-r--r--sound/soc/pxa/poodle.c11
-rw-r--r--sound/soc/pxa/pxa-ssp.c1
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c49
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c3
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c33
-rw-r--r--sound/soc/pxa/spitz.c5
-rw-r--r--sound/soc/pxa/tosa.c5
-rw-r--r--sound/soc/pxa/ttc-dkb.c12
-rw-r--r--sound/soc/qcom/lpass-cpu.c3
-rw-r--r--sound/soc/rockchip/Kconfig12
-rw-r--r--sound/soc/rockchip/Makefile6
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c48
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h16
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c405
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h63
-rw-r--r--sound/soc/samsung/h1940_uda1380.c5
-rw-r--r--sound/soc/samsung/rx1950_uda1380.c5
-rw-r--r--sound/soc/sh/Kconfig3
-rw-r--r--sound/soc/sh/rcar/adg.c306
-rw-r--r--sound/soc/sh/rcar/core.c66
-rw-r--r--sound/soc/sh/rcar/ctu.c16
-rw-r--r--sound/soc/sh/rcar/dma.c2
-rw-r--r--sound/soc/sh/rcar/dvc.c16
-rw-r--r--sound/soc/sh/rcar/gen.c14
-rw-r--r--sound/soc/sh/rcar/mix.c16
-rw-r--r--sound/soc/sh/rcar/rcar_snd.h117
-rw-r--r--sound/soc/sh/rcar/rsnd.h29
-rw-r--r--sound/soc/sh/rcar/src.c27
-rw-r--r--sound/soc/sh/rcar/ssi.c113
-rw-r--r--sound/soc/sh/siu_dai.c85
-rw-r--r--sound/soc/soc-compress.c12
-rw-r--r--sound/soc/soc-core.c29
-rw-r--r--sound/soc/soc-dapm.c59
-rw-r--r--sound/soc/soc-ops.c8
-rw-r--r--sound/soc/soc-pcm.c59
-rw-r--r--sound/soc/soc-topology.c4
-rw-r--r--sound/soc/sunxi/Kconfig11
-rw-r--r--sound/soc/sunxi/Makefile2
-rw-r--r--sound/soc/sunxi/sun4i-codec.c712
-rw-r--r--sound/soc/ux500/mop500.c1
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c5
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/endpoint.c92
-rw-r--r--sound/usb/midi.c11
-rw-r--r--sound/usb/mixer_quirks.c2
-rw-r--r--sound/usb/pcm.c74
-rw-r--r--sound/usb/quirks-table.h23
-rw-r--r--sound/usb/quirks.c3
-rw-r--r--sound/usb/stream.c1
-rw-r--r--sound/usb/usbaudio.h1
262 files changed, 20305 insertions, 2841 deletions
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 38590b322c54..fbd5dad0c484 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_reset,
};
-static unsigned long pxa2xx_ac97_pcm_out_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 12,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
.filter_data = &pxa2xx_ac97_pcm_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_in_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 11,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 01f8fdc42b1b..e9b98af6b52c 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -15,8 +16,6 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include <mach/dma.h>
-
#include "pxa2xx-pcm.h"
static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
@@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.period_bytes_min = 32,
.period_bytes_max = 8192 - 32,
.periods_min = 1,
- .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
+ .periods_max = 256,
.buffer_bytes_max = 128 * 1024,
.fifo_size = 32,
};
@@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- size_t totsize = params_buffer_bytes(params);
- 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;
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ struct dma_slave_config config;
+ int ret;
- /* 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;
- }
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!dma_params)
+ return 0;
- switch (rtd->params->maxburst) {
- case 8:
- dcmd |= DCMD_BURST8;
- break;
- case 16:
- dcmd |= DCMD_BURST16;
- break;
- case 32:
- dcmd |= DCMD_BURST32;
- break;
- }
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
+ if (ret)
+ return ret;
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = totsize;
+ snd_dmaengine_pcm_set_config_from_dai_data(substream,
+ snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
+ &config);
- dma_desc = rtd->dma_desc_array;
- next_desc_phys = rtd->dma_desc_array_phys;
- dma_buff_phys = runtime->dma_addr;
- do {
- next_desc_phys += sizeof(pxa_dma_desc);
- dma_desc->ddadr = next_desc_phys;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_desc->dsadr = dma_buff_phys;
- dma_desc->dtadr = rtd->params->addr;
- } else {
- dma_desc->dsadr = rtd->params->addr;
- dma_desc->dtadr = dma_buff_phys;
- }
- if (period > totsize)
- period = totsize;
- dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
- dma_desc++;
- dma_buff_phys += period;
- } while (totsize -= period);
- dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+ ret = dmaengine_slave_config(chan, &config);
+ if (ret)
+ return ret;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
@@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
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->filter_data) {
- unsigned long req = *(unsigned long *) rtd->params->filter_data;
- DRCMR(req) = 0;
- }
-
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
@@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) = DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_RESUME:
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
+ return snd_dmaengine_pcm_trigger(substream, cmd);
}
EXPORT_SYMBOL(pxa2xx_pcm_trigger);
snd_pcm_uframes_t
pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
- dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
- snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-
- if (x == runtime->buffer_size)
- x = 0;
- return x;
+ return snd_dmaengine_pcm_pointer(substream);
}
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;
-
- if (prtd->dma_ch == -1)
- return -EINVAL;
-
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- DCSR(prtd->dma_ch) = 0;
- DCMD(prtd->dma_ch) = 0;
- req = *(unsigned long *) prtd->params->filter_data;
- DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
-
return 0;
}
EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
-void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- int dcsr;
-
- dcsr = DCSR(dma_ch);
- DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
- if (dcsr & DCSR_ENDINTR) {
- snd_pcm_period_elapsed(substream);
- } else {
- printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
- dma_ch, dcsr);
- snd_pcm_stop_xrun(substream);
- }
-}
-EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
-
int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd;
+ struct snd_dmaengine_dai_dma_data *dma_params;
int ret;
runtime->hw = pxa2xx_pcm_hardware;
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!dma_params)
+ return 0;
+
/*
* For mysterious reasons (and despite what the manual says)
* playback samples are lost if the DMA count is not a multiple
@@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
- goto out;
-
- ret = -ENOMEM;
- rtd = kzalloc(sizeof(*rtd), GFP_KERNEL);
- if (!rtd)
- goto out;
- rtd->dma_desc_array =
- dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- &rtd->dma_desc_array_phys, GFP_KERNEL);
- if (!rtd->dma_desc_array)
- goto err1;
+ return ret;
- rtd->dma_ch = -1;
- runtime->private_data = rtd;
- return 0;
-
- err1:
- kfree(rtd);
- out:
- return ret;
+ return snd_dmaengine_pcm_open_request_chan(substream,
+ pxad_filter_fn,
+ dma_params->filter_data);
}
EXPORT_SYMBOL(__pxa2xx_pcm_open);
int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
-
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- kfree(rtd);
- return 0;
+ return snd_dmaengine_pcm_close_release_chan(substream);
}
EXPORT_SYMBOL(__pxa2xx_pcm_close);
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
index 83be8e3f095e..83fcfac97739 100644
--- a/sound/arm/pxa2xx-pcm.c
+++ b/sound/arm/pxa2xx-pcm.c
@@ -46,17 +46,13 @@ 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("dma", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- goto err2;
- rtd->dma_ch = ret;
ret = client->startup(substream);
if (!ret)
- goto out;
+ goto err2;
+
+ return 0;
- pxa_free_dma(rtd->dma_ch);
err2:
__pxa2xx_pcm_close(substream);
out:
@@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
struct pxa2xx_pcm_client *client = substream->private_data;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- pxa_free_dma(rtd->dma_ch);
client->shutdown(substream);
return __pxa2xx_pcm_close(substream);
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
index 00330985beec..8fa2b7c9e6b8 100644
--- a/sound/arm/pxa2xx-pcm.h
+++ b/sound/arm/pxa2xx-pcm.h
@@ -13,8 +13,6 @@
struct pxa2xx_runtime_data {
int dma_ch;
struct snd_dmaengine_dai_dma_data *params;
- struct pxa_dma_desc *dma_desc_array;
- dma_addr_t dma_desc_array_phys;
};
struct pxa2xx_pcm_client {
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 6c96feeaf01e..e3e949126a56 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -4,7 +4,7 @@ config SND_TIMER
config SND_PCM
tristate
- select SND_TIMER
+ select SND_TIMER if SND_PCM_TIMER
config SND_PCM_ELD
bool
@@ -93,6 +93,17 @@ config SND_PCM_OSS_PLUGINS
support conversion of channels, formats and rates. It will
behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
+config SND_PCM_TIMER
+ bool "PCM timer interface" if EXPERT
+ default y
+ help
+ If you disable this option, pcm timer will be inavailable, so
+ those stubs used pcm timer (e.g. dmix, dsnoop & co) may work
+ incorrectlly.
+
+ For some embedded device, we may disable it to reduce memory
+ footprint, about 20KB on x86_64 platform.
+
config SND_SEQUENCER_OSS
bool "OSS Sequencer API"
depends on SND_SEQUENCER
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 3354f91e003a..48ab4b8f8279 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -13,8 +13,9 @@ snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
-snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
+snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
pcm_memory.o memalloc.o
+snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index a99f7200ff3f..7a8c79dd9734 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1177,7 +1177,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
struct snd_mixer_oss *mixer = entry->private_data;
char line[128], str[32], idxstr[16];
const char *cptr;
- int ch, idx;
+ unsigned int idx;
+ int ch;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 02bd96954dc4..308c9ecf73db 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1014,9 +1014,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
snd_free_pages((void*)runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- kfree(runtime->hwptr_log);
-#endif
kfree(runtime);
substream->runtime = NULL;
put_pid(substream->pid);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 7d45645f10ba..6b5a811e01a5 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -801,7 +801,7 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
* negative error code.
*/
int snd_interval_ratnum(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratnum *rats,
+ unsigned int rats_count, const struct snd_ratnum *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_den;
@@ -920,7 +920,8 @@ EXPORT_SYMBOL(snd_interval_ratnum);
* negative error code.
*/
static int snd_interval_ratden(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratden *rats,
+ unsigned int rats_count,
+ const struct snd_ratden *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_diff, best_den;
@@ -1339,7 +1340,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratnums *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratnums *r = rule->private;
unsigned int num = 0, den = 0;
int err;
err = snd_interval_ratnum(hw_param_interval(params, rule->var),
@@ -1363,10 +1364,10 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratnums *r)
+ const struct snd_pcm_hw_constraint_ratnums *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratnums, r,
+ snd_pcm_hw_rule_ratnums, (void *)r,
var, -1);
}
@@ -1375,7 +1376,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratdens *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratdens *r = rule->private;
unsigned int num = 0, den = 0;
int err = snd_interval_ratden(hw_param_interval(params, rule->var),
r->nrats, r->rats, &num, &den);
@@ -1398,10 +1399,10 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratdens *r)
+ const struct snd_pcm_hw_constraint_ratdens *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratdens, r,
+ snd_pcm_hw_rule_ratdens, (void *)r,
var, -1);
}
@@ -1875,20 +1876,17 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
return;
runtime = substream->runtime;
- if (runtime->transfer_ack_begin)
- runtime->transfer_ack_begin(substream);
-
snd_pcm_stream_lock_irqsave(substream, flags);
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
+#ifdef CONFIG_SND_PCM_TIMER
if (substream->timer_running)
snd_timer_interrupt(substream->timer, 1);
+#endif
_end:
snd_pcm_stream_unlock_irqrestore(substream, flags);
- if (runtime->transfer_ack_end)
- runtime->transfer_ack_end(substream);
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
}
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 75888dd38a7f..a8b27cdc2844 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -486,6 +486,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
snd_pcm_stream_unlock_irq(substream);
}
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+ int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+ if (substream->timer)
+ snd_timer_notify(substream->timer, event,
+ &substream->runtime->trigger_tstamp);
+#endif
+}
+
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -650,7 +660,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
}
snd_pcm_stream_unlock_irq(substream);
- if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ if (params->tstamp_mode < 0 ||
+ params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
return -EINVAL;
if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
@@ -1042,9 +1053,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
}
static struct action_ops snd_pcm_action_start = {
@@ -1092,9 +1101,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
if (runtime->status->state != state) {
snd_pcm_trigger_tstamp(substream);
runtime->status->state = state;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
}
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
@@ -1208,18 +1215,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
snd_pcm_trigger_tstamp(substream);
if (push) {
runtime->status->state = SNDRV_PCM_STATE_PAUSED;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MPAUSE,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
} else {
runtime->status->state = SNDRV_PCM_STATE_RUNNING;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MCONTINUE,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
}
}
@@ -1267,9 +1268,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
snd_pcm_trigger_tstamp(substream);
runtime->status->suspended_state = runtime->status->state;
runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
@@ -1373,9 +1372,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
runtime->status->state = runtime->status->suspended_state;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
}
static struct action_ops snd_pcm_action_resume = {
@@ -2226,7 +2223,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
snd_pcm_drop(substream);
if (substream->hw_opened) {
- if (substream->ops->hw_free != NULL)
+ if (substream->ops->hw_free &&
+ substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
substream->ops->hw_free(substream);
substream->ops->close(substream);
substream->hw_opened = 0;
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
index ccd893566f1d..046cb586fb2f 100644
--- a/sound/core/seq/oss/seq_oss_readq.c
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -91,8 +91,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
q->head = q->tail = 0;
}
/* if someone sleeping, wake'em up */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
q->input_time = (unsigned long)-1;
}
@@ -138,8 +137,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
q->qlen++;
/* wake up sleeper */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
spin_unlock_irqrestore(&q->lock, flags);
diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
index d50338bbc21f..1f6788a18444 100644
--- a/sound/core/seq/oss/seq_oss_writeq.c
+++ b/sound/core/seq/oss/seq_oss_writeq.c
@@ -138,9 +138,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
spin_lock_irqsave(&q->sync_lock, flags);
q->sync_time = time;
q->sync_event_put = 0;
- if (waitqueue_active(&q->sync_sleep)) {
- wake_up(&q->sync_sleep);
- }
+ wake_up(&q->sync_sleep);
spin_unlock_irqrestore(&q->sync_lock, flags);
}
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 8850b7de1d38..bee0e5f1a116 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -120,4 +120,31 @@ config SND_BEBOB
To compile this driver as a module, choose M here: the module
will be called snd-bebob.
+config SND_FIREWIRE_DIGI00X
+ tristate "Digidesign Digi 002/003 family support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for Digidesign Digi 002/003 family.
+ * Digi 002 Console
+ * Digi 002 Rack
+ * Digi 003 Console
+ * Digi 003 Rack
+ * Digi 003 Rack+
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-digi00x.
+
+config SND_FIREWIRE_TASCAM
+ tristate "TASCAM FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for TASCAM.
+ * FW-1884
+ * FW-1082
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-tascam.
+
endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 8b37f084b2ab..f5fb62551c60 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,6 +1,5 @@
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
- fcp.o cmp.o amdtp.o
-snd-oxfw-objs := oxfw.o
+ fcp.o cmp.o amdtp-stream.o amdtp-am824.o
snd-isight-objs := isight.o
snd-scs1x-objs := scs1x.o
@@ -11,3 +10,5 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
new file mode 100644
index 000000000000..bebddc60fde8
--- /dev/null
+++ b/sound/firewire/amdtp-am824.c
@@ -0,0 +1,465 @@
+/*
+ * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/slab.h>
+
+#include "amdtp-am824.h"
+
+#define CIP_FMT_AM 0x10
+
+/* "Clock-based rate control mode" is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+struct amdtp_am824 {
+ struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ int midi_fifo_limit;
+ int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+
+ u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
+ u8 midi_position;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+
+ unsigned int frame_multiplier;
+};
+
+/**
+ * amdtp_am824_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ * as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @double_pcm_frames: one data block transfers two PCM frames
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int midi_channels;
+ unsigned int i;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EINVAL;
+
+ if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
+ return -EINVAL;
+
+ midi_channels = DIV_ROUND_UP(midi_ports, 8);
+ if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -EINVAL;
+
+ if (WARN_ON(amdtp_stream_running(s)) ||
+ WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
+ WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
+ return -EINVAL;
+
+ err = amdtp_stream_set_parameters(s, rate,
+ pcm_channels + midi_channels);
+ if (err < 0)
+ return err;
+
+ s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+ p->midi_ports = midi_ports;
+
+ /*
+ * In IEC 61883-6, one data block represents one event. In ALSA, one
+ * event equals to one PCM frame. But Dice has a quirk at higher
+ * sampling rate to transfer two PCM frames in one data block.
+ */
+ if (double_pcm_frames)
+ p->frame_multiplier = 2;
+ else
+ p->frame_multiplier = 1;
+
+ /* init the position map for PCM and MIDI channels */
+ for (i = 0; i < pcm_channels; i++)
+ p->pcm_positions[i] = i;
+ p->midi_position = p->pcm_channels;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
+
+/**
+ * amdtp_am824_set_pcm_position - set an index of data channel for a channel
+ * of PCM frame
+ * @s: the AMDTP stream
+ * @index: the index of data channel in an data block
+ * @position: the channel of PCM frame
+ */
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (index < p->pcm_channels)
+ p->pcm_positions[index] = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
+
+/**
+ * amdtp_am824_set_midi_position - set a index of data channel for MIDI
+ * conformant data channel
+ * @s: the AMDTP stream
+ * @position: the index of data channel in an data block
+ */
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_position = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src >> 8) | 0x40000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src << 8) | 0x42000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/**
+ * amdtp_am824_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
+ * @format: the format of the ALSA PCM device
+ *
+ * The sample format must be set after the other parameters (rate/PCM channels/
+ * MIDI) and before the stream is started, and must not be changed while the
+ * stream is running.
+ */
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format);
+
+/**
+ * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream for AM824 data block, must be initialized.
+ * @runtime: the PCM substream runtime
+ *
+ */
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ if (err < 0)
+ return err;
+
+ /* AM824 in IEC 61883-6 can deliver 24bit data. */
+ return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
+
+/**
+ * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data. This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (port < p->midi_ports)
+ ACCESS_ONCE(p->midi[port]) = midi;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
+
+/*
+ * To avoid sending MIDI bytes at too high a rate, assume that the receiving
+ * device has a FIFO, and track how much it is filled. This values increases
+ * by one whenever we send one byte in a packet, but the FIFO empties at
+ * a constant rate independent of our packet rate. One packet has syt_interval
+ * samples, so the number of bytes that empty out of the FIFO, per packet(!),
+ * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
+ * fractional values, the values in midi_fifo_used[] are measured in bytes
+ * multiplied by the sample rate.
+ */
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0) /* common shortcut */
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ b = (u8 *)&buffer[p->midi_position];
+
+ port = (s->data_block_counter + f) % 8;
+ if (f < MAX_MIDI_RX_BLOCKS &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL &&
+ snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
+ midi_rate_use_one_byte(s, port);
+ b[0] = 0x81;
+ } else {
+ b[0] = 0x80;
+ b[1] = 0;
+ }
+ b[2] = 0;
+ b[3] = 0;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ port = (s->data_block_counter + f) % 8;
+ b = (u8 *)&buffer[p->midi_position];
+
+ len = b[0] - 0x80;
+ if ((1 <= len) && (len <= 3) && (p->midi[port]))
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int *syt)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks * p->frame_multiplier;
+ } else {
+ write_pcm_silence(s, buffer, data_blocks);
+ pcm_frames = 0;
+ }
+
+ if (p->midi_ports)
+ write_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int *syt)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks * p->frame_multiplier;
+ } else {
+ pcm_frames = 0;
+ }
+
+ if (p->midi_ports)
+ read_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+/**
+ * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
+ * data block
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the packet transmission method to use
+ */
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, enum cip_flags flags)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_data_blocks = process_tx_data_blocks;
+ else
+ process_data_blocks = process_rx_data_blocks;
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_data_blocks,
+ sizeof(struct amdtp_am824));
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_init);
diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h
new file mode 100644
index 000000000000..73b07b3109db
--- /dev/null
+++ b/sound/firewire/amdtp-am824.h
@@ -0,0 +1,52 @@
+#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#include "amdtp-stream.h"
+
+#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
+#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
+ SNDRV_PCM_FMTBIT_S32)
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AM824_MAX_CHANNELS_FOR_PCM 64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AM824_MAX_CHANNELS_FOR_MIDI 1
+
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames);
+
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position);
+
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position);
+
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s,
+ snd_pcm_format_t format);
+
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, enum cip_flags flags);
+#endif
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp-stream.c
index 2a153d260836..ed2902609a4c 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp-stream.c
@@ -11,28 +11,14 @@
#include <linux/firewire.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/sched.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-#include <sound/rawmidi.h>
-#include "amdtp.h"
+#include "amdtp-stream.h"
#define TICKS_PER_CYCLE 3072
#define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
-/*
- * Nominally 3125 bytes/second, but the MIDI port's clock might be
- * 1% too slow, and the bus clock 100 ppm too fast.
- */
-#define MIDI_BYTES_PER_SECOND 3093
-
-/*
- * Several devices look only at the first eight data blocks.
- * In any case, this is more than enough for the MIDI data rate.
- */
-#define MAX_MIDI_RX_BLOCKS 8
-
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
/* isochronous header parameters */
@@ -55,12 +41,8 @@
#define CIP_SYT_MASK 0x0000ffff
#define CIP_SYT_NO_INFO 0xffff
-/*
- * Audio and Music transfer protocol specific parameters
- * only "Clock-based rate control mode" is supported
- */
-#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT)
-#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3))
+/* Audio and Music transfer protocol specific parameters */
+#define CIP_FMT_AM 0x10
#define AMDTP_FDF_NO_DATA 0xff
/* TODO: make these configurable */
@@ -78,10 +60,23 @@ static void pcm_period_tasklet(unsigned long data);
* @unit: the target of the stream
* @dir: the direction of stream
* @flags: the packet transmission method to use
+ * @fmt: the value of fmt field in CIP header
+ * @process_data_blocks: callback handler to process data blocks
+ * @protocol_size: the size to allocate newly for protocol
*/
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
- enum amdtp_stream_direction dir, enum cip_flags flags)
+ enum amdtp_stream_direction dir, enum cip_flags flags,
+ unsigned int fmt,
+ amdtp_stream_process_data_blocks_t process_data_blocks,
+ unsigned int protocol_size)
{
+ if (process_data_blocks == NULL)
+ return -EINVAL;
+
+ s->protocol = kzalloc(protocol_size, GFP_KERNEL);
+ if (!s->protocol)
+ return -ENOMEM;
+
s->unit = unit;
s->direction = dir;
s->flags = flags;
@@ -94,6 +89,9 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
s->callbacked = false;
s->sync_slave = NULL;
+ s->fmt = fmt;
+ s->process_data_blocks = process_data_blocks;
+
return 0;
}
EXPORT_SYMBOL(amdtp_stream_init);
@@ -105,6 +103,7 @@ EXPORT_SYMBOL(amdtp_stream_init);
void amdtp_stream_destroy(struct amdtp_stream *s)
{
WARN_ON(amdtp_stream_running(s));
+ kfree(s->protocol);
mutex_destroy(&s->mutex);
}
EXPORT_SYMBOL(amdtp_stream_destroy);
@@ -141,11 +140,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
{
int err;
- /* AM824 in IEC 61883-6 can deliver 24bit data */
- err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
- if (err < 0)
- goto end;
-
/*
* Currently firewire-lib processes 16 packets in one software
* interrupt callback. This equals to 2msec but actually the
@@ -190,39 +184,25 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
* amdtp_stream_set_parameters - set stream parameters
* @s: the AMDTP stream to configure
* @rate: the sample rate
- * @pcm_channels: the number of PCM samples in each data block, to be encoded
- * as AM824 multi-bit linear audio
- * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @data_block_quadlets: the size of a data block in quadlet unit
*
* The parameters must be set before the stream is started, and must not be
* changed while the stream is running.
*/
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports)
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets)
{
- unsigned int i, sfc, midi_channels;
+ unsigned int sfc;
- midi_channels = DIV_ROUND_UP(midi_ports, 8);
-
- if (WARN_ON(amdtp_stream_running(s)) |
- WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
- WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
- return;
-
- for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
+ for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
if (amdtp_rate_table[sfc] == rate)
- goto sfc_found;
- WARN_ON(1);
- return;
+ break;
+ }
+ if (sfc == ARRAY_SIZE(amdtp_rate_table))
+ return -EINVAL;
-sfc_found:
- s->pcm_channels = pcm_channels;
s->sfc = sfc;
- s->data_block_quadlets = s->pcm_channels + midi_channels;
- s->midi_ports = midi_ports;
-
+ s->data_block_quadlets = data_block_quadlets;
s->syt_interval = amdtp_syt_intervals[sfc];
/* default buffering in the device */
@@ -231,18 +211,7 @@ sfc_found:
/* additional buffering needed to adjust for no-data packets */
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
- /* init the position map for PCM and MIDI channels */
- for (i = 0; i < pcm_channels; i++)
- s->pcm_positions[i] = i;
- s->midi_position = s->pcm_channels;
-
- /*
- * We do not know the actual MIDI FIFO size of most devices. Just
- * assume two bytes, i.e., one byte can be received over the bus while
- * the previous one is transmitted over MIDI.
- * (The value here is adjusted for midi_ratelimit_per_packet().)
- */
- s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+ return 0;
}
EXPORT_SYMBOL(amdtp_stream_set_parameters);
@@ -264,52 +233,6 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-static void write_pcm_s16(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-static void write_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-
-/**
- * amdtp_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP stream to configure
- * @format: the format of the ALSA PCM device
- *
- * The sample format must be set after the other parameters (rate/PCM channels/
- * MIDI) and before the stream is started, and must not be changed while the
- * stream is running.
- */
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
- snd_pcm_format_t format)
-{
- if (WARN_ON(amdtp_stream_pcm_running(s)))
- return;
-
- switch (format) {
- default:
- WARN_ON(1);
- /* fall through */
- case SNDRV_PCM_FORMAT_S16:
- if (s->direction == AMDTP_OUT_STREAM) {
- s->transfer_samples = write_pcm_s16;
- break;
- }
- WARN_ON(1);
- /* fall through */
- case SNDRV_PCM_FORMAT_S32:
- if (s->direction == AMDTP_OUT_STREAM)
- s->transfer_samples = write_pcm_s32;
- else
- s->transfer_samples = read_pcm_s32;
- break;
- }
-}
-EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
-
/**
* amdtp_stream_pcm_prepare - prepare PCM device for running
* @s: the AMDTP stream
@@ -412,182 +335,12 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
}
}
-static void write_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
-{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, i, c;
- const u32 *src;
-
- channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- buffer[s->pcm_positions[c]] =
- cpu_to_be32((*src >> 8) | 0x40000000);
- src++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- src = (void *)runtime->dma_area;
- }
-}
-
-static void write_pcm_s16(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
-{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, i, c;
- const u16 *src;
-
- channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- buffer[s->pcm_positions[c]] =
- cpu_to_be32((*src << 8) | 0x42000000);
- src++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- src = (void *)runtime->dma_area;
- }
-}
-
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
-{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, i, c;
- u32 *dst;
-
- channels = s->pcm_channels;
- dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
- dst++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- dst = (void *)runtime->dma_area;
- }
-}
-
-static void write_pcm_silence(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int i, c;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < s->pcm_channels; ++c)
- buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
- buffer += s->data_block_quadlets;
- }
-}
-
-/*
- * To avoid sending MIDI bytes at too high a rate, assume that the receiving
- * device has a FIFO, and track how much it is filled. This values increases
- * by one whenever we send one byte in a packet, but the FIFO empties at
- * a constant rate independent of our packet rate. One packet has syt_interval
- * samples, so the number of bytes that empty out of the FIFO, per packet(!),
- * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
- * fractional values, the values in midi_fifo_used[] are measured in bytes
- * multiplied by the sample rate.
- */
-static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
-{
- int used;
-
- used = s->midi_fifo_used[port];
- if (used == 0) /* common shortcut */
- return true;
-
- used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
- used = max(used, 0);
- s->midi_fifo_used[port] = used;
-
- return used < s->midi_fifo_limit;
-}
-
-static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
-{
- s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
-}
-
-static void write_midi_messages(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int f, port;
- u8 *b;
-
- for (f = 0; f < frames; f++) {
- b = (u8 *)&buffer[s->midi_position];
-
- port = (s->data_block_counter + f) % 8;
- if (f < MAX_MIDI_RX_BLOCKS &&
- midi_ratelimit_per_packet(s, port) &&
- s->midi[port] != NULL &&
- snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
- midi_rate_use_one_byte(s, port);
- b[0] = 0x81;
- } else {
- b[0] = 0x80;
- b[1] = 0;
- }
- b[2] = 0;
- b[3] = 0;
-
- buffer += s->data_block_quadlets;
- }
-}
-
-static void read_midi_messages(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int f, port;
- int len;
- u8 *b;
-
- for (f = 0; f < frames; f++) {
- port = (s->data_block_counter + f) % 8;
- b = (u8 *)&buffer[s->midi_position];
-
- len = b[0] - 0x80;
- if ((1 <= len) && (len <= 3) && (s->midi[port]))
- snd_rawmidi_receive(s->midi[port], b + 1, len);
-
- buffer += s->data_block_quadlets;
- }
-}
-
static void update_pcm_pointers(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
unsigned int frames)
{
unsigned int ptr;
- /*
- * In IEC 61883-6, one data block represents one event. In ALSA, one
- * event equals to one PCM frame. But Dice has a quirk to transfer
- * two PCM frames in one data block.
- */
- if (s->double_pcm_frames)
- frames *= 2;
-
ptr = s->pcm_buffer_pointer + frames;
if (ptr >= pcm->runtime->buffer_size)
ptr -= pcm->runtime->buffer_size;
@@ -656,23 +409,19 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
{
__be32 *buffer;
unsigned int payload_length;
+ unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
buffer = s->buffer.packets[s->packet_index].buffer;
+ pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
s->data_block_counter);
- buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
- (s->sfc << CIP_FDF_SHIFT) | syt);
- buffer += 2;
-
- pcm = ACCESS_ONCE(s->pcm);
- if (pcm)
- s->transfer_samples(s, pcm, buffer, data_blocks);
- else
- write_pcm_silence(s, buffer, data_blocks);
- if (s->midi_ports)
- write_midi_messages(s, buffer, data_blocks);
+ buffer[1] = cpu_to_be32(CIP_EOH |
+ ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
+ ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
+ (syt & CIP_SYT_MASK));
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
@@ -680,8 +429,9 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
if (queue_out_packet(s, payload_length, false) < 0)
return -EIO;
- if (pcm)
- update_pcm_pointers(s, pcm, data_blocks);
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
/* No need to return the number of handled data blocks. */
return 0;
@@ -689,11 +439,13 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
static int handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets, __be32 *buffer,
- unsigned int *data_blocks)
+ unsigned int *data_blocks, unsigned int syt)
{
u32 cip_header[2];
+ unsigned int fmt, fdf;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
- struct snd_pcm_substream *pcm = NULL;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
bool lost;
cip_header[0] = be32_to_cpu(buffer[0]);
@@ -704,19 +456,30 @@ static int handle_in_packet(struct amdtp_stream *s,
* For convenience, also check FMT field is AM824 or not.
*/
if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
- ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
- ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
*data_blocks = 0;
+ pcm_frames = 0;
+ goto end;
+ }
+
+ /* Check valid protocol or not. */
+ fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
+ if (fmt != s->fmt) {
+ dev_info_ratelimited(&s->unit->device,
+ "Detect unexpected protocol: %08x %08x\n",
+ cip_header[0], cip_header[1]);
+ *data_blocks = 0;
+ pcm_frames = 0;
goto end;
}
/* Calculate data blocks */
+ fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
if (payload_quadlets < 3 ||
- ((cip_header[1] & CIP_FDF_MASK) ==
- (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
+ (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
*data_blocks = 0;
} else {
data_block_quadlets =
@@ -763,16 +526,7 @@ static int handle_in_packet(struct amdtp_stream *s,
return -EIO;
}
- if (*data_blocks > 0) {
- buffer += 2;
-
- pcm = ACCESS_ONCE(s->pcm);
- if (pcm)
- s->transfer_samples(s, pcm, buffer, *data_blocks);
-
- if (s->midi_ports)
- read_midi_messages(s, buffer, *data_blocks);
- }
+ pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter;
@@ -783,8 +537,9 @@ end:
if (queue_in_packet(s) < 0)
return -EIO;
- if (pcm)
- update_pcm_pointers(s, pcm, *data_blocks);
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
return 0;
}
@@ -854,15 +609,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
break;
}
+ syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
if (handle_in_packet(s, payload_quadlets, buffer,
- &data_blocks) < 0) {
+ &data_blocks, syt) < 0) {
s->packet_index = -1;
break;
}
/* Process sync slave stream */
if (s->sync_slave && s->sync_slave->callbacked) {
- syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
if (handle_out_packet(s->sync_slave,
data_blocks, syt) < 0) {
s->packet_index = -1;
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp-stream.h
index b2cf9e75693b..8775704a3665 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp-stream.h
@@ -4,6 +4,7 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
+#include <linux/sched.h>
#include <sound/asound.h>
#include "packets-buffer.h"
@@ -80,100 +81,78 @@ enum cip_sfc {
CIP_SFC_COUNT
};
-#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
-
-#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
- SNDRV_PCM_FMTBIT_S32)
-
-
-/*
- * This module supports maximum 64 PCM channels for one PCM stream
- * This is for our convenience.
- */
-#define AMDTP_MAX_CHANNELS_FOR_PCM 64
-
-/*
- * AMDTP packet can include channels for MIDI conformant data.
- * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
- * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
- *
- * This module supports maximum 1 MIDI conformant data channels.
- * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
- */
-#define AMDTP_MAX_CHANNELS_FOR_MIDI 1
-
struct fw_unit;
struct fw_iso_context;
struct snd_pcm_substream;
struct snd_pcm_runtime;
-struct snd_rawmidi_substream;
enum amdtp_stream_direction {
AMDTP_OUT_STREAM = 0,
AMDTP_IN_STREAM
};
+struct amdtp_stream;
+typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
+ struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt);
struct amdtp_stream {
struct fw_unit *unit;
enum cip_flags flags;
enum amdtp_stream_direction direction;
- struct fw_iso_context *context;
struct mutex mutex;
- enum cip_sfc sfc;
- unsigned int data_block_quadlets;
- unsigned int pcm_channels;
- unsigned int midi_ports;
- void (*transfer_samples)(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
- u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
- u8 midi_position;
-
- unsigned int syt_interval;
- unsigned int transfer_delay;
- unsigned int source_node_id_field;
+ /* For packet processing. */
+ struct fw_iso_context *context;
struct iso_packets_buffer buffer;
-
- struct snd_pcm_substream *pcm;
- struct tasklet_struct period_tasklet;
-
int packet_index;
+
+ /* For CIP headers. */
+ unsigned int source_node_id_field;
+ unsigned int data_block_quadlets;
unsigned int data_block_counter;
+ unsigned int fmt;
+ unsigned int fdf;
+ /* quirk: fixed interval of dbc between previos/current packets. */
+ unsigned int tx_dbc_interval;
+ /* quirk: indicate the value of dbc field in a first packet. */
+ unsigned int tx_first_dbc;
+ /* Internal flags. */
+ enum cip_sfc sfc;
+ unsigned int syt_interval;
+ unsigned int transfer_delay;
unsigned int data_block_state;
-
unsigned int last_syt_offset;
unsigned int syt_offset_state;
+ /* For a PCM substream processing. */
+ struct snd_pcm_substream *pcm;
+ struct tasklet_struct period_tasklet;
unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer;
bool pointer_flush;
- bool double_pcm_frames;
-
- struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
- int midi_fifo_limit;
- int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
-
- /* quirk: fixed interval of dbc between previos/current packets. */
- unsigned int tx_dbc_interval;
- /* quirk: indicate the value of dbc field in a first packet. */
- unsigned int tx_first_dbc;
+ /* To wait for first packet. */
bool callbacked;
wait_queue_head_t callback_wait;
struct amdtp_stream *sync_slave;
+
+ /* For backends to process data blocks. */
+ void *protocol;
+ amdtp_stream_process_data_blocks_t process_data_blocks;
};
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
- enum amdtp_stream_direction dir,
- enum cip_flags flags);
+ enum amdtp_stream_direction dir, enum cip_flags flags,
+ unsigned int fmt,
+ amdtp_stream_process_data_blocks_t process_data_blocks,
+ unsigned int protocol_size);
void amdtp_stream_destroy(struct amdtp_stream *s);
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports);
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
@@ -182,8 +161,7 @@ void amdtp_stream_stop(struct amdtp_stream *s);
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
- snd_pcm_format_t format);
+
void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
void amdtp_stream_pcm_abort(struct amdtp_stream *s);
@@ -240,24 +218,6 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
ACCESS_ONCE(s->pcm) = pcm;
}
-/**
- * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
- * @s: the AMDTP stream
- * @port: index of MIDI port
- * @midi: the MIDI device to be started, or %NULL to stop the current device
- *
- * Call this function on a running isochronous stream to enable the actual
- * transmission of MIDI data. This function should be called from the MIDI
- * device's .trigger callback.
- */
-static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
- unsigned int port,
- struct snd_rawmidi_substream *midi)
-{
- if (port < s->midi_ports)
- ACCESS_ONCE(s->midi[port]) = midi;
-}
-
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
{
return sfc & 1;
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 6cf470c80d1f..af7ed6643266 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,4 +1,4 @@
snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
bebob_focusrite.o bebob_maudio.o bebob.o
-obj-m += snd-bebob.o
+obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 27a04ac8ffee..091290d1f3ea 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -41,7 +41,8 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_EDIROL 0x000040ab
#define VEN_PRESONUS 0x00000a92
#define VEN_BRIDGECO 0x000007f5
-#define VEN_MACKIE 0x0000000f
+#define VEN_MACKIE1 0x0000000f
+#define VEN_MACKIE2 0x00000ff2
#define VEN_STANTON 0x00001260
#define VEN_TASCAM 0x0000022e
#define VEN_BEHRINGER 0x00001564
@@ -334,7 +335,7 @@ static void bebob_remove(struct fw_unit *unit)
snd_card_free_when_closed(bebob->card);
}
-static struct snd_bebob_rate_spec normal_rate_spec = {
+static const struct snd_bebob_rate_spec normal_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate
};
@@ -360,9 +361,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* BridgeCo, Audio5 */
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
- SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal),
/* Mackie, d.2 (Firewire Option) */
- SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
/* Stanton, ScratchAmp */
SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
/* Tascam, IF-FW DM */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index d23caca7f369..4d8fcc78e747 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -31,7 +31,7 @@
#include "../fcp.h"
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
/* basic register addresses on DM1000/DM1100/DM1500 */
@@ -70,9 +70,9 @@ struct snd_bebob_meter_spec {
int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
};
struct snd_bebob_spec {
- struct snd_bebob_clock_spec *clock;
- struct snd_bebob_rate_spec *rate;
- struct snd_bebob_meter_spec *meter;
+ const struct snd_bebob_clock_spec *clock;
+ const struct snd_bebob_rate_spec *rate;
+ const struct snd_bebob_meter_spec *meter;
};
struct snd_bebob {
@@ -235,19 +235,19 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
/* model specific operations */
-extern struct snd_bebob_spec phase88_rack_spec;
-extern struct snd_bebob_spec phase24_series_spec;
-extern struct snd_bebob_spec yamaha_go_spec;
-extern struct snd_bebob_spec saffirepro_26_spec;
-extern struct snd_bebob_spec saffirepro_10_spec;
-extern struct snd_bebob_spec saffire_le_spec;
-extern struct snd_bebob_spec saffire_spec;
-extern struct snd_bebob_spec maudio_fw410_spec;
-extern struct snd_bebob_spec maudio_audiophile_spec;
-extern struct snd_bebob_spec maudio_solo_spec;
-extern struct snd_bebob_spec maudio_ozonic_spec;
-extern struct snd_bebob_spec maudio_nrv10_spec;
-extern struct snd_bebob_spec maudio_special_spec;
+extern const struct snd_bebob_spec phase88_rack_spec;
+extern const struct snd_bebob_spec phase24_series_spec;
+extern const struct snd_bebob_spec yamaha_go_spec;
+extern const struct snd_bebob_spec saffirepro_26_spec;
+extern const struct snd_bebob_spec saffirepro_10_spec;
+extern const struct snd_bebob_spec saffire_le_spec;
+extern const struct snd_bebob_spec saffire_spec;
+extern const struct snd_bebob_spec maudio_fw410_spec;
+extern const struct snd_bebob_spec maudio_audiophile_spec;
+extern const struct snd_bebob_spec maudio_solo_spec;
+extern const struct snd_bebob_spec maudio_ozonic_spec;
+extern const struct snd_bebob_spec maudio_nrv10_spec;
+extern const struct snd_bebob_spec maudio_special_spec;
int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index a1a39494ea6c..f11090057949 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -200,7 +200,7 @@ end:
return err;
}
-struct snd_bebob_spec saffire_le_spec;
+const struct snd_bebob_spec saffire_le_spec;
static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
SND_BEBOB_CLOCK_TYPE_INTERNAL,
SND_BEBOB_CLOCK_TYPE_EXTERNAL,
@@ -229,7 +229,7 @@ static const char *const saffire_meter_labels[] = {
static int
saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int channels;
u64 offset;
int err;
@@ -260,60 +260,60 @@ saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
return err;
}
-static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
.get = &saffirepro_both_clk_freq_get,
.set = &saffirepro_both_clk_freq_set,
};
/* Saffire Pro 26 I/O */
-static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
.num = ARRAY_SIZE(saffirepro_26_clk_src_types),
.types = saffirepro_26_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
-struct snd_bebob_spec saffirepro_26_spec = {
+const struct snd_bebob_spec saffirepro_26_spec = {
.clock = &saffirepro_26_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
/* Saffire Pro 10 I/O */
-static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
.num = ARRAY_SIZE(saffirepro_10_clk_src_types),
.types = saffirepro_10_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
-struct snd_bebob_spec saffirepro_10_spec = {
+const struct snd_bebob_spec saffirepro_10_spec = {
.clock = &saffirepro_10_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
-static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-static struct snd_bebob_clock_spec saffire_both_clk_spec = {
+static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
.num = ARRAY_SIZE(saffire_both_clk_src_types),
.types = saffire_both_clk_src_types,
.get = &saffire_both_clk_src_get,
};
/* Saffire LE */
-static struct snd_bebob_meter_spec saffire_le_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
.num = ARRAY_SIZE(saffire_le_meter_labels),
.labels = saffire_le_meter_labels,
.get = &saffire_meter_get,
};
-struct snd_bebob_spec saffire_le_spec = {
+const struct snd_bebob_spec saffire_le_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_le_meter_spec
};
/* Saffire */
-static struct snd_bebob_meter_spec saffire_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_meter_spec = {
.num = ARRAY_SIZE(saffire_meter_labels),
.labels = saffire_meter_labels,
.get = &saffire_meter_get,
};
-struct snd_bebob_spec saffire_spec = {
+const struct snd_bebob_spec saffire_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_meter_spec
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index 057495d54ab0..07e5abdbceb5 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -628,7 +628,7 @@ static const char *const special_meter_labels[] = {
static int
special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
{
- u16 *buf;
+ __be16 *buf;
unsigned int i, c, channels;
int err;
@@ -687,7 +687,7 @@ static const char *const nrv10_meter_labels[] = {
static int
normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int c, channels;
int err;
@@ -712,85 +712,85 @@ end:
}
/* for special customized devices */
-static struct snd_bebob_rate_spec special_rate_spec = {
+static const struct snd_bebob_rate_spec special_rate_spec = {
.get = &special_get_rate,
.set = &special_set_rate,
};
-static struct snd_bebob_clock_spec special_clk_spec = {
+static const struct snd_bebob_clock_spec special_clk_spec = {
.num = ARRAY_SIZE(special_clk_types),
.types = special_clk_types,
.get = &special_clk_get,
};
-static struct snd_bebob_meter_spec special_meter_spec = {
+static const struct snd_bebob_meter_spec special_meter_spec = {
.num = ARRAY_SIZE(special_meter_labels),
.labels = special_meter_labels,
.get = &special_meter_get
};
-struct snd_bebob_spec maudio_special_spec = {
+const struct snd_bebob_spec maudio_special_spec = {
.clock = &special_clk_spec,
.rate = &special_rate_spec,
.meter = &special_meter_spec
};
/* Firewire 410 specification */
-static struct snd_bebob_rate_spec usual_rate_spec = {
+static const struct snd_bebob_rate_spec usual_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-static struct snd_bebob_meter_spec fw410_meter_spec = {
+static const struct snd_bebob_meter_spec fw410_meter_spec = {
.num = ARRAY_SIZE(fw410_meter_labels),
.labels = fw410_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_fw410_spec = {
+const struct snd_bebob_spec maudio_fw410_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &fw410_meter_spec
};
/* Firewire Audiophile specification */
-static struct snd_bebob_meter_spec audiophile_meter_spec = {
+static const struct snd_bebob_meter_spec audiophile_meter_spec = {
.num = ARRAY_SIZE(audiophile_meter_labels),
.labels = audiophile_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_audiophile_spec = {
+const struct snd_bebob_spec maudio_audiophile_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &audiophile_meter_spec
};
/* Firewire Solo specification */
-static struct snd_bebob_meter_spec solo_meter_spec = {
+static const struct snd_bebob_meter_spec solo_meter_spec = {
.num = ARRAY_SIZE(solo_meter_labels),
.labels = solo_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_solo_spec = {
+const struct snd_bebob_spec maudio_solo_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &solo_meter_spec
};
/* Ozonic specification */
-static struct snd_bebob_meter_spec ozonic_meter_spec = {
+static const struct snd_bebob_meter_spec ozonic_meter_spec = {
.num = ARRAY_SIZE(ozonic_meter_labels),
.labels = ozonic_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_ozonic_spec = {
+const struct snd_bebob_spec maudio_ozonic_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &ozonic_meter_spec
};
/* NRV10 specification */
-static struct snd_bebob_meter_spec nrv10_meter_spec = {
+static const struct snd_bebob_meter_spec nrv10_meter_spec = {
.num = ARRAY_SIZE(nrv10_meter_labels),
.labels = nrv10_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_nrv10_spec = {
+const struct snd_bebob_spec maudio_nrv10_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &nrv10_meter_spec
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 5681143925cd..90d95be499b0 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&bebob->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&bebob->tx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&bebob->tx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
@@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&bebob->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&bebob->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&bebob->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index c0f018a61fdc..ef224d6f5c24 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
s = &bebob->tx_stream;
formations = bebob->tx_stream_formations;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
s = &bebob->rx_stream;
formations = bebob->rx_stream_formations;
}
@@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
@@ -155,7 +155,7 @@ static int
pcm_open(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+ const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
unsigned int sampling_rate;
enum snd_bebob_clock_type src;
int err;
@@ -220,8 +220,8 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->substreams_counter);
- amdtp_stream_set_pcm_format(&bebob->tx_stream,
- params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
return 0;
}
@@ -239,8 +239,8 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->substreams_counter);
- amdtp_stream_set_pcm_format(&bebob->rx_stream,
- params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
index 301cc6a93945..ec24f96794f5 100644
--- a/sound/firewire/bebob/bebob_proc.c
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -73,7 +73,7 @@ proc_read_meters(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_bebob *bebob = entry->private_data;
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
u32 *buf;
unsigned int i, c, channels, size;
@@ -138,8 +138,8 @@ proc_read_clock(struct snd_info_entry *entry,
"SYT-Match",
};
struct snd_bebob *bebob = entry->private_data;
- struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
- struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
enum snd_bebob_clock_type src;
unsigned int rate;
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 5be5242e1ed8..926e5dcbb66a 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -119,7 +119,7 @@ end:
int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
enum snd_bebob_clock_type *src)
{
- struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
unsigned int id;
enum avc_bridgeco_plug_type type;
@@ -338,7 +338,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
err = -ENOSYS;
goto end;
}
- s->midi_position = stm_pos;
+ amdtp_am824_set_midi_position(s, stm_pos);
midi = stm_pos;
break;
/* for PCM data channel */
@@ -354,11 +354,12 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
case 0x09: /* Digital */
default:
location = pcm + sec_loc;
- if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+ if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
err = -ENOSYS;
goto end;
}
- s->pcm_positions[location] = stm_pos;
+ amdtp_am824_set_pcm_position(s, location,
+ stm_pos);
break;
}
}
@@ -427,12 +428,19 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
index = get_formation_index(rate);
pcm_channels = bebob->tx_stream_formations[index].pcm;
midi_channels = bebob->tx_stream_formations[index].midi;
- amdtp_stream_set_parameters(&bebob->tx_stream,
- rate, pcm_channels, midi_channels * 8);
+ err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
+ pcm_channels, midi_channels * 8,
+ false);
+ if (err < 0)
+ goto end;
+
pcm_channels = bebob->rx_stream_formations[index].pcm;
midi_channels = bebob->rx_stream_formations[index].midi;
- amdtp_stream_set_parameters(&bebob->rx_stream,
- rate, pcm_channels, midi_channels * 8);
+ err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
+ pcm_channels, midi_channels * 8,
+ false);
+ if (err < 0)
+ goto end;
/* establish connections for both streams */
err = cmp_connection_establish(&bebob->out_conn,
@@ -530,8 +538,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
if (err < 0)
goto end;
- err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
- AMDTP_IN_STREAM, CIP_BLOCKING);
+ err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
+ AMDTP_IN_STREAM, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(&bebob->tx_stream);
destroy_both_connections(bebob);
@@ -559,8 +567,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
if (bebob->maudio_special_quirk)
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
- err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
- AMDTP_OUT_STREAM, CIP_BLOCKING);
+ err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
+ AMDTP_OUT_STREAM, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(&bebob->tx_stream);
amdtp_stream_destroy(&bebob->rx_stream);
@@ -572,7 +580,7 @@ end:
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
- struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct amdtp_stream *master, *slave;
enum cip_flags sync_mode;
unsigned int curr_rate;
@@ -864,8 +872,8 @@ parse_stream_formation(u8 *buf, unsigned int len,
}
}
- if (formation[i].pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
- formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
return -ENOSYS;
return 0;
@@ -959,7 +967,7 @@ end:
int snd_bebob_stream_discover(struct snd_bebob *bebob)
{
- struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
enum avc_bridgeco_plug_type type;
unsigned int i;
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index 9242e33d2cf1..c38358b82ada 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -55,30 +55,30 @@ phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
return 0;
}
-static struct snd_bebob_rate_spec phase_series_rate_spec = {
+static const struct snd_bebob_rate_spec phase_series_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
/* PHASE 88 Rack FW */
-static struct snd_bebob_clock_spec phase88_rack_clk = {
+static const struct snd_bebob_clock_spec phase88_rack_clk = {
.num = ARRAY_SIZE(phase88_rack_clk_src_types),
.types = phase88_rack_clk_src_types,
.get = &phase88_rack_clk_src_get,
};
-struct snd_bebob_spec phase88_rack_spec = {
+const struct snd_bebob_spec phase88_rack_spec = {
.clock = &phase88_rack_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
};
/* 'PHASE 24 FW' and 'PHASE X24 FW' */
-static struct snd_bebob_clock_spec phase24_series_clk = {
+static const struct snd_bebob_clock_spec phase24_series_clk = {
.num = ARRAY_SIZE(phase24_series_clk_src_types),
.types = phase24_series_clk_src_types,
.get = &phase24_series_clk_src_get,
};
-struct snd_bebob_spec phase24_series_spec = {
+const struct snd_bebob_spec phase24_series_spec = {
.clock = &phase24_series_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
index 58101702410b..90d4404f77ce 100644
--- a/sound/firewire/bebob/bebob_yamaha.c
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -46,16 +46,16 @@ clk_src_get(struct snd_bebob *bebob, unsigned int *id)
return 0;
}
-static struct snd_bebob_clock_spec clock_spec = {
+static const struct snd_bebob_clock_spec clock_spec = {
.num = ARRAY_SIZE(clk_src_types),
.types = clk_src_types,
.get = &clk_src_get,
};
-static struct snd_bebob_rate_spec rate_spec = {
+static const struct snd_bebob_rate_spec rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-struct snd_bebob_spec yamaha_go_spec = {
+const struct snd_bebob_spec yamaha_go_spec = {
.clock = &clock_spec,
.rate = &rate_spec,
.meter = NULL
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 9ef228ef7baf..55b4be9b0034 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,3 +1,3 @@
snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
dice-pcm.o dice-hwdep.o dice.o
-obj-m += snd-dice.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
index fe43ce791f84..151b09f240f2 100644
--- a/sound/firewire/dice/dice-midi.c
+++ b/sound/firewire/dice/dice-midi.c
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream,
substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&dice->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&dice->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&dice->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&dice->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
}
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 4e67b1da0fe6..9b3431999fc8 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice,
SNDRV_PCM_INFO_BLOCK_TRANSFER;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
+ hw->formats = AM824_IN_PCM_FORMAT_BITS;
stream = &dice->tx_stream;
pcm_channels = dice->tx_channels;
} else {
- hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ hw->formats = AM824_OUT_PCM_FORMAT_BITS;
stream = &dice->rx_stream;
pcm_channels = dice->rx_channels;
}
@@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
end:
return err;
}
@@ -243,8 +243,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_stream_set_pcm_format(&dice->tx_stream,
- params_format(hw_params));
+ amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
return 0;
}
@@ -265,8 +264,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_stream_set_pcm_format(&dice->rx_stream,
- params_format(hw_params));
+ amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index 07dbd01d7a6b..a6a39f7ef58d 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -44,16 +44,16 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
static void release_resources(struct snd_dice *dice,
struct fw_iso_resources *resources)
{
- unsigned int channel;
+ __be32 channel;
/* Reset channel number */
channel = cpu_to_be32((u32)-1);
if (resources == &dice->tx_resources)
snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
else
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
fw_iso_resources_free(resources);
}
@@ -62,7 +62,7 @@ static int keep_resources(struct snd_dice *dice,
struct fw_iso_resources *resources,
unsigned int max_payload_bytes)
{
- unsigned int channel;
+ __be32 channel;
int err;
err = fw_iso_resources_allocate(resources, max_payload_bytes,
@@ -74,10 +74,10 @@ static int keep_resources(struct snd_dice *dice,
channel = cpu_to_be32(resources->channel);
if (resources == &dice->tx_resources)
err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
else
err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
if (err < 0)
release_resources(dice, resources);
end:
@@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
{
struct fw_iso_resources *resources;
unsigned int i, mode, pcm_chs, midi_ports;
+ bool double_pcm_frames;
int err;
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
@@ -125,21 +126,24 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
* For this quirk, blocking mode is required and PCM buffer size should
* be aligned to SYT_INTERVAL.
*/
- if (mode > 1) {
+ double_pcm_frames = mode > 1;
+ if (double_pcm_frames) {
rate /= 2;
pcm_chs *= 2;
- stream->double_pcm_frames = true;
- } else {
- stream->double_pcm_frames = false;
}
- amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
- if (mode > 1) {
+ err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
+ double_pcm_frames);
+ if (err < 0)
+ goto end;
+
+ if (double_pcm_frames) {
pcm_chs /= 2;
for (i = 0; i < pcm_chs; i++) {
- stream->pcm_positions[i] = i * 2;
- stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
+ amdtp_am824_set_pcm_position(stream, i, i * 2);
+ amdtp_am824_set_pcm_position(stream, i + pcm_chs,
+ i * 2 + 1);
}
}
@@ -302,7 +306,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
goto end;
resources->channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
+ err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 70a111d7f428..5d99436dfcae 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -29,7 +29,8 @@ static int dice_interface_check(struct fw_unit *unit)
struct fw_csr_iterator it;
int key, val, vendor = -1, model = -1, err;
unsigned int category, i;
- __be32 *pointers, value;
+ __be32 *pointers;
+ u32 value;
__be32 version;
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index ecf5dc862235..101550ac1a24 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -34,7 +34,7 @@
#include <sound/pcm_params.h>
#include <sound/rawmidi.h>
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../iso-resources.h"
#include "../lib.h"
#include "dice-interface.h"
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
new file mode 100644
index 000000000000..1123e68c8b28
--- /dev/null
+++ b/sound/firewire/digi00x/Makefile
@@ -0,0 +1,4 @@
+snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+ digi00x-pcm.o digi00x-hwdep.o \
+ digi00x-transaction.o digi00x-midi.o digi00x.o
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
new file mode 100644
index 000000000000..b02a5e8cad44
--- /dev/null
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -0,0 +1,442 @@
+/*
+ * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "digi00x.h"
+
+#define CIP_FMT_AM 0x10
+
+/* 'Clock-based rate control mode' is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+/*
+ * The double-oh-three algorithm was discovered by Robin Gareus and Damien
+ * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
+ */
+struct dot_state {
+ u8 carry;
+ u8 idx;
+ unsigned int off;
+};
+
+struct amdtp_dot {
+ unsigned int pcm_channels;
+ struct dot_state state;
+
+ unsigned int midi_ports;
+ /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
+ struct snd_rawmidi_substream *midi[2];
+ int midi_fifo_used[2];
+ int midi_fifo_limit;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+};
+
+/*
+ * double-oh-three look up table
+ *
+ * @param idx index byte (audio-sample data) 0x00..0xff
+ * @param off channel offset shift
+ * @return salt to XOR with given data
+ */
+#define BYTE_PER_SAMPLE (4)
+#define MAGIC_DOT_BYTE (2)
+#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
+static const u8 dot_scrt(const u8 idx, const unsigned int off)
+{
+ /*
+ * the length of the added pattern only depends on the lower nibble
+ * of the last non-zero data
+ */
+ static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
+ 12, 10, 8, 6, 4, 2, 0};
+
+ /*
+ * the lower nibble of the salt. Interleaved sequence.
+ * this is walked backwards according to len[]
+ */
+ static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
+ 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
+
+ /* circular list for the salt's hi nibble. */
+ static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
+ 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
+
+ /*
+ * start offset for upper nibble mapping.
+ * note: 9 is /special/. In the case where the high nibble == 0x9,
+ * hir[] is not used and - coincidentally - the salt's hi nibble is
+ * 0x09 regardless of the offset.
+ */
+ static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
+ 3, 0x00, 14, 13, 8, 9, 10, 2};
+
+ const u8 ln = idx & 0xf;
+ const u8 hn = (idx >> 4) & 0xf;
+ const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
+
+ if (len[ln] < off)
+ return 0x00;
+
+ return ((nib[14 + off - len[ln]]) | (hr << 4));
+}
+
+static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
+{
+ u8 * const data = (u8 *) buffer;
+
+ if (data[MAGIC_DOT_BYTE] != 0x00) {
+ state->off = 0;
+ state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
+ }
+ data[MAGIC_DOT_BYTE] ^= state->carry;
+ state->carry = dot_scrt(state->idx, ++(state->off));
+}
+
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_dot *p = s->protocol;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ /*
+ * A first data channel is for MIDI conformant data channel, the rest is
+ * Multi Bit Linear Audio data channel.
+ */
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
+ if (err < 0)
+ return err;
+
+ s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+
+ if (s->direction == AMDTP_IN_STREAM)
+ p->midi_ports = DOT_MIDI_IN_PORTS;
+ else
+ p->midi_ports = DOT_MIDI_OUT_PORTS;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src << 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ buffer++;
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_dot *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0)
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static inline void midi_use_bytes(struct amdtp_stream *s,
+ unsigned int port, unsigned int count)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ port = (s->data_block_counter + f) % 8;
+ b = (u8 *)&buffer[0];
+
+ len = 0;
+ if (port < p->midi_ports &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL)
+ len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
+
+ if (len > 0) {
+ b[3] = (0x10 << port) | len;
+ midi_use_bytes(s, port, len);
+ } else {
+ b[1] = 0;
+ b[2] = 0;
+ b[3] = 0;
+ }
+ b[0] = 0x80;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port, len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ b = (u8 *)&buffer[0];
+ port = b[3] >> 4;
+ len = b[3] & 0x0f;
+
+ if (port < p->midi_ports && p->midi[port] && len > 0)
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* This protocol delivers 24 bit data in 32bit data channel. */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (port < p->midi_ports)
+ ACCESS_ONCE(p->midi[port]) = midi;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ pcm_frames = 0;
+ }
+
+ read_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ write_pcm_silence(s, buffer, data_blocks);
+ pcm_frames = 0;
+ }
+
+ write_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+ enum cip_flags flags;
+
+ /* Use different mode between incoming/outgoing. */
+ if (dir == AMDTP_IN_STREAM) {
+ flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+ process_data_blocks = process_tx_data_blocks;
+ } else {
+ flags = CIP_BLOCKING;
+ process_data_blocks = process_rx_data_blocks;
+ }
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_data_blocks, sizeof(struct amdtp_dot));
+}
+
+void amdtp_dot_reset(struct amdtp_stream *s)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->state.carry = 0x00;
+ p->state.idx = 0x00;
+ p->state.off = 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
new file mode 100644
index 000000000000..f188e4758fd2
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-hwdep.c
@@ -0,0 +1,200 @@
+/*
+ * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ * 4.get asynchronous messaging
+ */
+
+#include "digi00x.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dg00x->lock);
+
+ while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
+ prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dg00x->lock);
+ schedule();
+ finish_wait(&dg00x->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dg00x->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dg00x->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (dg00x->dev_lock_count > 0);
+ dg00x->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.digi00x_message.type =
+ SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+ event.digi00x_message.message = dg00x->msg;
+ dg00x->msg = 0;
+
+ count = min_t(long, count, sizeof(event.digi00x_message));
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &dg00x->hwdep_wait, wait);
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_changed || dg00x->msg)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dg00x->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == 0) {
+ dg00x->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == -1) {
+ dg00x->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_count == -1)
+ dg00x->dev_lock_count = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dg00x, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dg00x);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dg00x);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Digi00x");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = dg00x;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
new file mode 100644
index 000000000000..1a72a382b384
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-midi.c
@@ -0,0 +1,223 @@
+/*
+ * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int midi_phys_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ err = snd_dg00x_stream_start_duplex(dg00x, 0);
+ mutex_unlock(&dg00x->mutex);
+ if (err < 0)
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return err;
+}
+
+static int midi_phys_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter--;
+ snd_dg00x_stream_stop_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+
+ snd_dg00x_stream_lock_release(dg00x);
+ return 0;
+}
+
+static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
+ substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
+ NULL);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
+ substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
+ NULL);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_phys_capture_ops = {
+ .open = midi_phys_open,
+ .close = midi_phys_close,
+ .trigger = midi_phys_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_phys_playback_ops = {
+ .open = midi_phys_open,
+ .close = midi_phys_close,
+ .trigger = midi_phys_playback_trigger,
+};
+
+static int midi_ctl_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_finish(&dg00x->out_control);
+
+ return 0;
+}
+
+static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ dg00x->in_control = substream;
+ else
+ dg00x->in_control = NULL;
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ snd_fw_async_midi_port_run(&dg00x->out_control, substream);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_ctl_capture_ops = {
+ .open = midi_ctl_open,
+ .close = midi_ctl_capture_close,
+ .trigger = midi_ctl_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_ctl_playback_ops = {
+ .open = midi_ctl_open,
+ .close = midi_ctl_playback_close,
+ .trigger = midi_ctl_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_dg00x *dg00x,
+ struct snd_rawmidi_str *str,
+ bool is_ctl)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ if (!is_ctl)
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ dg00x->card->shortname, subs->number + 1);
+ else
+ /* This port is for asynchronous transaction. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s control",
+ dg00x->card->shortname);
+ }
+}
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+ struct snd_rawmidi *rmidi[2];
+ struct snd_rawmidi_str *str;
+ unsigned int i;
+ int err;
+
+ /* Add physical midi ports. */
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
+ DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
+ "%s MIDI", dg00x->card->shortname);
+
+ snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_phys_capture_ops);
+ snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_phys_playback_ops);
+
+ /* Add a pair of control midi ports. */
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
+ 1, 1, &rmidi[1]);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
+ "%s control", dg00x->card->shortname);
+
+ snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_ctl_capture_ops);
+ snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_ctl_playback_ops);
+
+ for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
+ rmidi[i]->private_data = dg00x;
+
+ rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(dg00x, str, i);
+
+ rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(dg00x, str, i);
+
+ rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
new file mode 100644
index 000000000000..cac28f70aef7
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-pcm.c
@@ -0,0 +1,373 @@
+/*
+ * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(c,
+ snd_dg00x_stream_pcm_channels[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_rates[i]);
+ t.max = max(t.max, snd_dg00x_stream_rates[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
+ t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static int pcm_init_hw_params(struct snd_dg00x *dg00x,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 18,
+ .period_bytes_min = 4 * 18,
+ .period_bytes_max = 4 * 18 * 2048,
+ .buffer_bytes_max = 4 * 18 * 2048 * 2,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct amdtp_stream *s;
+ int err;
+
+ substream->runtime->hw = hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->tx_stream;
+ } else {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->rx_stream;
+ }
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, NULL,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ enum snd_dg00x_clock clock;
+ bool detect;
+ unsigned int rate;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(dg00x, substream);
+ if (err < 0)
+ goto err_locked;
+
+ /* Check current clock source. */
+ err = snd_dg00x_stream_get_clock(dg00x, &clock);
+ if (err < 0)
+ goto err_locked;
+ if (clock != SND_DG00X_CLOCK_INTERNAL) {
+ err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
+ if (err < 0)
+ goto err_locked;
+ if (!detect) {
+ err = -EBUSY;
+ goto err_locked;
+ }
+ }
+
+ if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
+ amdtp_stream_pcm_running(&dg00x->rx_stream) ||
+ amdtp_stream_pcm_running(&dg00x->tx_stream)) {
+ err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_dg00x_stream_lock_release(dg00x);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ mutex_unlock(&dg00x->mutex);
+ }
+
+ amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ mutex_unlock(&dg00x->mutex);
+ }
+
+ amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ mutex_lock(&dg00x->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dg00x->substreams_counter--;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ mutex_lock(&dg00x->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dg00x->substreams_counter--;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dg00x->tx_stream);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ if (err >= 0) {
+ amdtp_stream_pcm_prepare(&dg00x->rx_stream);
+ amdtp_dot_reset(&dg00x->rx_stream);
+ }
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = dg00x;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", dg00x->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c
new file mode 100644
index 000000000000..a1d601f31165
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-proc.c
@@ -0,0 +1,99 @@
+/*
+ * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int get_optical_iface_mode(struct snd_dg00x *dg00x,
+ enum snd_dg00x_optical_mode *mode)
+{
+ __be32 data;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
+ &data, sizeof(data), 0);
+ if (err >= 0)
+ *mode = be32_to_cpu(data) & 0x01;
+
+ return err;
+}
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buf)
+{
+ static const char *const source_name[] = {
+ [SND_DG00X_CLOCK_INTERNAL] = "internal",
+ [SND_DG00X_CLOCK_SPDIF] = "s/pdif",
+ [SND_DG00X_CLOCK_ADAT] = "adat",
+ [SND_DG00X_CLOCK_WORD] = "word clock",
+ };
+ static const char *const optical_name[] = {
+ [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
+ [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
+ };
+ struct snd_dg00x *dg00x = entry->private_data;
+ enum snd_dg00x_optical_mode mode;
+ unsigned int rate;
+ enum snd_dg00x_clock clock;
+ bool detect;
+
+ if (get_optical_iface_mode(dg00x, &mode) < 0)
+ return;
+ if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
+ return;
+ if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
+ return;
+
+ snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
+ snd_iprintf(buf, "Sampling Rate: %d\n", rate);
+ snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
+
+ if (clock == SND_DG00X_CLOCK_INTERNAL)
+ return;
+
+ if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
+ return;
+ snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
+ if (!detect)
+ return;
+
+ if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
+ snd_iprintf(buf, "External sampling rate: %d\n", rate);
+}
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
+{
+ struct snd_info_entry *root, *entry;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(dg00x->card, "firewire",
+ dg00x->card->proc_root);
+ if (root == NULL)
+ return;
+
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ entry = snd_info_create_card_entry(dg00x->card, "clock", root);
+ if (entry == NULL) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ snd_info_set_text_ops(entry, dg00x, proc_read_clock);
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ snd_info_free_entry(root);
+ }
+}
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644
index 000000000000..4d3b4ebbdd49
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -0,0 +1,422 @@
+/*
+ * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+#define CALLBACK_TIMEOUT 500
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+ [SND_DG00X_RATE_44100] = 44100,
+ [SND_DG00X_RATE_48000] = 48000,
+ [SND_DG00X_RATE_88200] = 88200,
+ [SND_DG00X_RATE_96000] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
+ /* Analog/ADAT/SPDIF */
+ [SND_DG00X_RATE_44100] = (8 + 8 + 2),
+ [SND_DG00X_RATE_48000] = (8 + 8 + 2),
+ /* Analog/SPDIF */
+ [SND_DG00X_RATE_88200] = (8 + 2),
+ [SND_DG00X_RATE_96000] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ else
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+ if (rate == snd_dg00x_stream_rates[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+ return -EINVAL;
+
+ reg = cpu_to_be32(i);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ *clock = be32_to_cpu(reg) & 0x0f;
+ if (*clock >= SND_DG00X_CLOCK_COUNT)
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *detect = be32_to_cpu(reg) > 0;
+
+ return err;
+}
+
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ /* This means desync. */
+ else
+ err = -EBUSY;
+
+ return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+ __be32 data = cpu_to_be32(0x00000003);
+
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+ __be32 data;
+ u32 curr;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+ curr = be32_to_cpu(data);
+
+ if (curr == 0)
+ curr = 2;
+
+ curr--;
+ while (curr > 0) {
+ data = cpu_to_be32(curr);
+ err = snd_fw_transaction(dg00x->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE +
+ DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+
+ msleep(20);
+ curr--;
+ }
+
+ return 0;
+error:
+ finish_session(dg00x);
+ return err;
+}
+
+static void release_resources(struct snd_dg00x *dg00x)
+{
+ __be32 data = 0;
+
+ /* Unregister isochronous channels for both direction. */
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+
+ /* Release isochronous resources. */
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+}
+
+static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ unsigned int i;
+ __be32 data;
+ int err;
+
+ /* Check sampling rate. */
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (snd_dg00x_stream_rates[i] == rate)
+ break;
+ }
+ if (i == SND_DG00X_RATE_COUNT)
+ return -EINVAL;
+
+ /* Keep resources for out-stream. */
+ err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&dg00x->rx_resources,
+ amdtp_stream_get_max_payload(&dg00x->rx_stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Keep resources for in-stream. */
+ err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&dg00x->tx_resources,
+ amdtp_stream_get_max_payload(&dg00x->tx_stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ /* Register isochronous channels for both direction. */
+ data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+ dg00x->rx_resources.channel);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ release_resources(dg00x);
+ return err;
+}
+
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ /* For out-stream. */
+ err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
+ if (err < 0)
+ goto error;
+ err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
+ if (err < 0)
+ goto error;
+
+ /* For in-stream. */
+ err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
+ if (err < 0)
+ goto error;
+ err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+ amdtp_stream_destroy(&dg00x->rx_stream);
+ fw_iso_resources_destroy(&dg00x->rx_resources);
+
+ amdtp_stream_destroy(&dg00x->tx_stream);
+ fw_iso_resources_destroy(&dg00x->tx_resources);
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ unsigned int curr_rate;
+ int err = 0;
+
+ if (dg00x->substreams_counter == 0)
+ goto end;
+
+ /* Check current sampling rate. */
+ err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+ if (err < 0)
+ goto error;
+ if (rate == 0)
+ rate = curr_rate;
+ if (curr_rate != rate ||
+ amdtp_streaming_error(&dg00x->tx_stream) ||
+ amdtp_streaming_error(&dg00x->rx_stream)) {
+ finish_session(dg00x);
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ release_resources(dg00x);
+ }
+
+ /*
+ * No packets are transmitted without receiving packets, reagardless of
+ * which source of clock is used.
+ */
+ if (!amdtp_stream_running(&dg00x->rx_stream)) {
+ err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+ if (err < 0)
+ goto error;
+
+ err = keep_resources(dg00x, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_stream_start(&dg00x->rx_stream,
+ dg00x->rx_resources.channel,
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ /*
+ * The value of SYT field in transmitted packets is always 0x0000. Thus,
+ * duplex streams with timestamp synchronization cannot be built.
+ */
+ if (!amdtp_stream_running(&dg00x->tx_stream)) {
+ err = amdtp_stream_start(&dg00x->tx_stream,
+ dg00x->tx_resources.channel,
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+end:
+ return err;
+error:
+ finish_session(dg00x);
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ release_resources(dg00x);
+
+ return err;
+}
+
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
+{
+ if (dg00x->substreams_counter > 0)
+ return;
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ finish_session(dg00x);
+ release_resources(dg00x);
+
+ /*
+ * Just after finishing the session, the device may lost transmitting
+ * functionality for a short time.
+ */
+ msleep(50);
+}
+
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+ fw_iso_resources_update(&dg00x->tx_resources);
+ fw_iso_resources_update(&dg00x->rx_resources);
+
+ amdtp_stream_update(&dg00x->tx_stream);
+ amdtp_stream_update(&dg00x->rx_stream);
+}
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
+{
+ dg00x->dev_lock_changed = true;
+ wake_up(&dg00x->hwdep_wait);
+}
+
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ /* user land lock this */
+ if (dg00x->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (dg00x->dev_lock_count++ == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+ err = 0;
+end:
+ spin_unlock_irq(&dg00x->lock);
+ return err;
+}
+
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
+{
+ spin_lock_irq(&dg00x->lock);
+
+ if (WARN_ON(dg00x->dev_lock_count <= 0))
+ goto end;
+ if (--dg00x->dev_lock_count == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+end:
+ spin_unlock_irq(&dg00x->lock);
+}
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644
index 000000000000..554324d8c602
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -0,0 +1,137 @@
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+ int bytes;
+
+ buf[0] = 0x80;
+ bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
+ if (bytes >= 0)
+ buf[3] = 0xc0 | bytes;
+
+ return bytes;
+}
+
+static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
+ unsigned int length)
+{
+ struct snd_rawmidi_substream *substream;
+ unsigned int i;
+ unsigned int len;
+ u8 *b;
+
+ substream = ACCESS_ONCE(dg00x->in_control);
+ if (substream == NULL)
+ return;
+
+ length /= 4;
+
+ for (i = 0; i < length; i++) {
+ b = (u8 *)&buf[i];
+ len = b[3] & 0xf;
+ if (len > 0)
+ snd_rawmidi_receive(dg00x->in_control, b + 1, len);
+ }
+}
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+ unsigned long long offset, __be32 *buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+ dg00x->msg = be32_to_cpu(*buf);
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+
+ wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dg00x *dg00x = callback_data;
+ __be32 *buf = (__be32 *)data;
+
+ if (offset == dg00x->async_handler.offset)
+ handle_unknown_message(dg00x, offset, buf);
+ else if (offset == dg00x->async_handler.offset + 4)
+ handle_midi_control(dg00x, buf, length);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+ struct fw_device *device = fw_parent_device(dg00x->unit);
+ __be32 data[2];
+ int err;
+
+ /* Unknown. 4bytes. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return err;
+
+ /* Asynchronous transactions for MIDI control message. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
+ &data, sizeof(data), 0);
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ dg00x->async_handler.length = 12;
+ dg00x->async_handler.address_callback = handle_message;
+ dg00x->async_handler.callback_data = dg00x;
+
+ err = fw_core_add_address_handler(&dg00x->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_dg00x_transaction_reregister(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
+ 4, fill_midi_message);
+ if (err < 0)
+ goto error;
+
+ return err;
+error:
+ fw_core_remove_address_handler(&dg00x->async_handler);
+ dg00x->async_handler.address_callback = NULL;
+ return err;
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+ snd_fw_async_midi_port_destroy(&dg00x->out_control);
+ fw_core_remove_address_handler(&dg00x->async_handler);
+}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
new file mode 100644
index 000000000000..1f33b7a1fca4
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.c
@@ -0,0 +1,170 @@
+/*
+ * digi00x.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+#define VENDOR_DIGIDESIGN 0x00a07e
+#define MODEL_DIGI00X 0x000002
+
+static int name_card(struct snd_dg00x *dg00x)
+{
+ struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
+ char name[32] = {0};
+ char *model;
+ int err;
+
+ err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
+ sizeof(name));
+ if (err < 0)
+ return err;
+
+ model = skip_spaces(name);
+
+ strcpy(dg00x->card->driver, "Digi00x");
+ strcpy(dg00x->card->shortname, model);
+ strcpy(dg00x->card->mixername, model);
+ snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
+ "Digidesign %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void dg00x_card_free(struct snd_card *card)
+{
+ struct snd_dg00x *dg00x = card->private_data;
+
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_dg00x_transaction_unregister(dg00x);
+
+ fw_unit_put(dg00x->unit);
+
+ mutex_destroy(&dg00x->mutex);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_dg00x *dg00x;
+ int err;
+
+ /* create card */
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(struct snd_dg00x), &card);
+ if (err < 0)
+ return err;
+ card->private_free = dg00x_card_free;
+
+ /* initialize myself */
+ dg00x = card->private_data;
+ dg00x->card = card;
+ dg00x->unit = fw_unit_get(unit);
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ err = name_card(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_stream_init_duplex(dg00x);
+ if (err < 0)
+ goto error;
+
+ snd_dg00x_proc_init(dg00x);
+
+ err = snd_dg00x_create_pcm_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_midi_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_hwdep_device(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_transaction_register(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, dg00x);
+
+ return err;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ snd_dg00x_transaction_reregister(dg00x);
+
+ mutex_lock(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+}
+
+static void snd_dg00x_remove(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(dg00x->card);
+}
+
+static const struct ieee1394_device_id snd_dg00x_id_table[] = {
+ /* Both of 002/003 use the same ID. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .model_id = MODEL_DIGI00X,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
+
+static struct fw_driver dg00x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-firewire-digi00x",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_dg00x_probe,
+ .update = snd_dg00x_update,
+ .remove = snd_dg00x_remove,
+ .id_table = snd_dg00x_id_table,
+};
+
+static int __init snd_dg00x_init(void)
+{
+ return driver_register(&dg00x_driver.driver);
+}
+
+static void __exit snd_dg00x_exit(void)
+{
+ driver_unregister(&dg00x_driver.driver);
+}
+
+module_init(snd_dg00x_init);
+module_exit(snd_dg00x_exit);
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
new file mode 100644
index 000000000000..907e73993677
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.h
@@ -0,0 +1,157 @@
+/*
+ * digi00x.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_DIGI00X_H_INCLUDED
+#define SOUND_DIGI00X_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../iso-resources.h"
+#include "../amdtp-stream.h"
+
+struct snd_dg00x {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ struct amdtp_stream tx_stream;
+ struct fw_iso_resources tx_resources;
+
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources rx_resources;
+
+ unsigned int substreams_counter;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For asynchronous messages. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* For asynchronous MIDI controls. */
+ struct snd_rawmidi_substream *in_control;
+ struct snd_fw_async_midi_port out_control;
+};
+
+#define DG00X_ADDR_BASE 0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE 0x0000
+#define DG00X_OFFSET_STREAMING_SET 0x0004
+#define DG00X_OFFSET_MIDI_CTL_ADDR 0x0008
+/* For LSB of the address 0x000c */
+/* unknown 0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR 0x0014
+/* For LSB of the address 0x0018 */
+/* unknown 0x001c */
+/* unknown 0x0020 */
+/* not used 0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS 0x0100
+/* unknown 0x0104 */
+/* unknown 0x0108 */
+/* unknown 0x010c */
+#define DG00X_OFFSET_LOCAL_RATE 0x0110
+#define DG00X_OFFSET_EXTERNAL_RATE 0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE 0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE 0x011c
+/* unknown 0x0120 */
+/* Mixer control on/off 0x0124 */
+/* unknown 0x0128 */
+#define DG00X_OFFSET_DETECT_EXTERNAL 0x012c
+/* unknown 0x0138 */
+#define DG00X_OFFSET_MMC 0x0400
+
+enum snd_dg00x_rate {
+ SND_DG00X_RATE_44100 = 0,
+ SND_DG00X_RATE_48000,
+ SND_DG00X_RATE_88200,
+ SND_DG00X_RATE_96000,
+ SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+ SND_DG00X_CLOCK_INTERNAL = 0,
+ SND_DG00X_CLOCK_SPDIF,
+ SND_DG00X_CLOCK_ADAT,
+ SND_DG00X_CLOCK_WORD,
+ SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+ SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+ SND_DG00X_OPT_IFACE_MODE_SPDIF,
+ SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+#define DOT_MIDI_IN_PORTS 1
+#define DOT_MIDI_OUT_PORTS 2
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+void amdtp_dot_reset(struct amdtp_stream *s);
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
+ bool *detect);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
+#endif
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index 0619597e3a3f..cce19768f43d 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -17,7 +17,7 @@
#include <linux/delay.h>
#include "fcp.h"
#include "lib.h"
-#include "amdtp.h"
+#include "amdtp-stream.h"
#define CTS_AVC 0x00
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index 0c7440826db8..15ef7f75a8ef 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,4 +1,4 @@
snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
fireworks_stream.o fireworks_proc.o fireworks_midi.o \
fireworks_pcm.o fireworks_hwdep.o fireworks.o
-obj-m += snd-fireworks.o
+obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index c94a432f7cc6..d5b19bc11e59 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw)
efw->midi_out_ports = hwinfo->midi_out_ports;
efw->midi_in_ports = hwinfo->midi_in_ports;
- if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
+ if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
err = -ENOSYS;
goto end;
}
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 084d414b228c..c7cb7deafe48 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -29,7 +29,7 @@
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
#include "../lib.h"
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
index 166f80584c2a..94bab0476a65 100644
--- a/sound/firewire/fireworks/fireworks_command.c
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -257,7 +257,7 @@ int snd_efw_command_get_phys_meters(struct snd_efw *efw,
struct snd_efw_phys_meters *meters,
unsigned int len)
{
- __be32 *buf = (__be32 *)meters;
+ u32 *buf = (u32 *)meters;
unsigned int i;
int err;
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
index cf9c65260439..fba01bbba456 100644
--- a/sound/firewire/fireworks/fireworks_midi.c
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&efw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&efw->tx_stream,
+ amdtp_am824_midi_trigger(&efw->tx_stream,
substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&efw->tx_stream,
+ amdtp_am824_midi_trigger(&efw->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
@@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&efw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&efw->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&efw->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
}
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
index c30b2ffa8dfb..d27135bac513 100644
--- a/sound/firewire/fireworks/fireworks_pcm.c
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
s = &efw->tx_stream;
pcm_channels = efw->pcm_capture_channels;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
s = &efw->rx_stream;
pcm_channels = efw->pcm_playback_channels;
}
@@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
@@ -253,7 +253,8 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->capture_substreams);
- amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
return 0;
}
@@ -270,7 +271,8 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->playback_substreams);
- amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 7e353f1f7bff..759f6e3ed44a 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
if (err < 0)
goto end;
- err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+ err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
@@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
midi_ports = efw->midi_in_ports;
}
- amdtp_stream_set_parameters(stream, sampling_rate,
- pcm_channels, midi_ports);
+ err = amdtp_am824_set_parameters(stream, sampling_rate,
+ pcm_channels, midi_ports, false);
+ if (err < 0)
+ goto end;
/* establish connection via CMP */
err = cmp_connection_establish(conn,
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 7409edba9f06..f80aafa44c89 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "lib.h"
#define ERROR_RETRY_DELAY_MS 20
@@ -66,6 +67,147 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
}
EXPORT_SYMBOL(snd_fw_transaction);
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_fw_async_midi_port *port = callback_data;
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+
+ /* This port is closed. */
+ if (substream == NULL)
+ return;
+
+ if (rcode == RCODE_COMPLETE)
+ snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+ else if (!rcode_is_permanent_error(rcode))
+ /* To start next transaction immediately for recovery. */
+ port->next_ktime = ktime_set(0, 0);
+ else
+ /* Don't continue processing. */
+ port->error = true;
+
+ port->idling = true;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+ struct snd_fw_async_midi_port *port =
+ container_of(work, struct snd_fw_async_midi_port, work);
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+ int generation;
+ int type;
+
+ /* Under transacting or error state. */
+ if (!port->idling || port->error)
+ return;
+
+ /* Nothing to do. */
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(port->next_ktime, ktime_get())) {
+ schedule_work(&port->work);
+ return;
+ }
+
+ /*
+ * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+ * Later, snd_rawmidi_transmit_ack() is called.
+ */
+ memset(port->buf, 0, port->len);
+ port->consume_bytes = port->fill(substream, port->buf);
+ if (port->consume_bytes <= 0) {
+ /* Do it in next chance, immediately. */
+ if (port->consume_bytes == 0) {
+ port->next_ktime = ktime_set(0, 0);
+ schedule_work(&port->work);
+ } else {
+ /* Fatal error. */
+ port->error = true;
+ }
+ return;
+ }
+
+ /* Calculate type of transaction. */
+ if (port->len == 4)
+ type = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ type = TCODE_WRITE_BLOCK_REQUEST;
+
+ /* Set interval to next transaction. */
+ port->next_ktime = ktime_add_ns(ktime_get(),
+ port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+
+ /* Start this transaction. */
+ port->idling = false;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = port->parent->generation;
+ smp_rmb();
+
+ fw_send_request(port->parent->card, &port->transaction, type,
+ port->parent->node_id, generation,
+ port->parent->max_speed, port->addr,
+ port->buf, port->len, async_midi_port_callback,
+ port);
+}
+
+/**
+ * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port to initialize
+ * @unit: the target of the asynchronous transaction
+ * @addr: the address to which transactions are transferred
+ * @len: the length of transaction
+ * @fill: the callback function to fill given buffer, and returns the
+ * number of consumed bytes for MIDI message.
+ *
+ */
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+ struct fw_unit *unit, u64 addr, unsigned int len,
+ snd_fw_async_midi_port_fill fill)
+{
+ port->len = DIV_ROUND_UP(len, 4) * 4;
+ port->buf = kzalloc(port->len, GFP_KERNEL);
+ if (port->buf == NULL)
+ return -ENOMEM;
+
+ port->parent = fw_parent_device(unit);
+ port->addr = addr;
+ port->fill = fill;
+ port->idling = true;
+ port->next_ktime = ktime_set(0, 0);
+ port->error = false;
+
+ INIT_WORK(&port->work, midi_port_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_init);
+
+/**
+ * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port structure
+ */
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
+{
+ snd_fw_async_midi_port_finish(port);
+ cancel_work_sync(&port->work);
+ kfree(port->buf);
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
+
MODULE_DESCRIPTION("FireWire audio helper functions");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index 02cfabc9c3c4..f3f6f84c48d6 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -3,6 +3,8 @@
#include <linux/firewire-constants.h>
#include <linux/types.h>
+#include <linux/sched.h>
+#include <sound/rawmidi.h>
struct fw_unit;
@@ -20,4 +22,58 @@ static inline bool rcode_is_permanent_error(int rcode)
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
}
+struct snd_fw_async_midi_port;
+typedef int (*snd_fw_async_midi_port_fill)(
+ struct snd_rawmidi_substream *substream,
+ u8 *buf);
+
+struct snd_fw_async_midi_port {
+ struct fw_device *parent;
+ struct work_struct work;
+ bool idling;
+ ktime_t next_ktime;
+ bool error;
+
+ u64 addr;
+ struct fw_transaction transaction;
+
+ u8 *buf;
+ unsigned int len;
+
+ struct snd_rawmidi_substream *substream;
+ snd_fw_async_midi_port_fill fill;
+ unsigned int consume_bytes;
+};
+
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+ struct fw_unit *unit, u64 addr, unsigned int len,
+ snd_fw_async_midi_port_fill fill);
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
+
+/**
+ * snd_fw_async_midi_port_run - run transactions for the async MIDI port
+ * @port: the asynchronous MIDI port
+ * @substream: the MIDI substream
+ */
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ if (!port->error) {
+ port->substream = substream;
+ schedule_work(&port->work);
+ }
+}
+
+/**
+ * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
+ * @port: the asynchronous MIDI port
+ */
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+ port->substream = NULL;
+ port->error = false;
+}
+
#endif
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index a926850864f6..06ff50f4e6c0 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,3 +1,3 @@
snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
-obj-m += snd-oxfw.o
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
index 540a30338516..8665e1043d41 100644
--- a/sound/firewire/oxfw/oxfw-midi.c
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&oxfw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&oxfw->tx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&oxfw->tx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&oxfw->lock, flags);
}
@@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&oxfw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&oxfw->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&oxfw->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&oxfw->lock, flags);
}
@@ -142,29 +142,11 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw,
int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
{
- struct snd_oxfw_stream_formation formation;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
- u8 *format;
- int i, err;
-
- /* If its stream has MIDI conformant data channel, add one MIDI port */
- for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
- format = oxfw->tx_stream_formats[i];
- if (format != NULL) {
- err = snd_oxfw_stream_parse_format(format, &formation);
- if (err >= 0 && formation.midi > 0)
- oxfw->midi_input_ports = 1;
- }
-
- format = oxfw->rx_stream_formats[i];
- if (format != NULL) {
- err = snd_oxfw_stream_parse_format(format, &formation);
- if (err >= 0 && formation.midi > 0)
- oxfw->midi_output_ports = 1;
- }
- }
- if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
+ int err;
+
+ if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
return 0;
/* create midi ports */
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 9c73930d0278..8d233417695d 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
stream = &oxfw->tx_stream;
formats = oxfw->tx_stream_formats;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
stream = &oxfw->rx_stream;
formats = oxfw->rx_stream_formats;
}
@@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
end:
return err;
}
@@ -244,7 +244,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&oxfw->mutex);
}
- amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
return 0;
}
@@ -265,7 +265,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&oxfw->mutex);
}
- amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 77ad5b98e806..7cb5743c073b 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -148,14 +148,17 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
}
pcm_channels = formation.pcm;
- midi_ports = DIV_ROUND_UP(formation.midi, 8);
+ midi_ports = formation.midi * 8;
/* The stream should have one pcm channels at least */
if (pcm_channels == 0) {
err = -EINVAL;
goto end;
}
- amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
+ false);
+ if (err < 0)
+ goto end;
err = cmp_connection_establish(conn,
amdtp_stream_get_max_payload(stream));
@@ -225,7 +228,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
if (err < 0)
goto end;
- err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+ err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
@@ -238,9 +241,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
* packets. As a result, next isochronous packet includes more data
* blocks than IEC 61883-6 defines.
*/
- if (stream == &oxfw->tx_stream)
+ if (stream == &oxfw->tx_stream) {
oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
CIP_JUMBO_PAYLOAD;
+ if (oxfw->wrong_dbs)
+ oxfw->tx_stream.flags |= CIP_WRONG_DBS;
+ }
end:
return err;
}
@@ -480,8 +486,8 @@ int snd_oxfw_stream_parse_format(u8 *format,
}
}
- if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
- formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
return -ENOSYS;
return 0;
@@ -623,6 +629,9 @@ end:
int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
{
u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+ struct snd_oxfw_stream_formation formation;
+ u8 *format;
+ unsigned int i;
int err;
/* the number of plugs for isoc in/out, ext in/out */
@@ -642,12 +651,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
if (err < 0)
goto end;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_input_ports = 1;
+ }
+
oxfw->has_output = true;
}
/* use iPCR[0] if exists */
- if (plugs[0] > 0)
+ if (plugs[0] > 0) {
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_output_ports = 1;
+ }
+ }
end:
return err;
}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 8c6ce019f437..588b93f20c2e 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -18,6 +18,9 @@
#define VENDOR_GRIFFIN 0x001292
#define VENDOR_BEHRINGER 0x001564
#define VENDOR_LACIE 0x00d04b
+#define VENDOR_TASCAM 0x00022e
+
+#define MODEL_SATELLITE 0x00200f
#define SPECIFIER_1394TA 0x00a02d
#define VERSION_AVC 0x010001
@@ -129,6 +132,40 @@ static void oxfw_card_free(struct snd_card *card)
mutex_destroy(&oxfw->mutex);
}
+static void detect_quirks(struct snd_oxfw *oxfw)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ int vendor, model;
+
+ /* Seek from Root Directory of Config ROM. */
+ vendor = model = 0;
+ fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_VENDOR)
+ vendor = val;
+ else if (key == CSR_MODEL)
+ model = val;
+ }
+
+ /*
+ * Mackie Onyx Satellite with base station has a quirk to report a wrong
+ * value in 'dbs' field of CIP header against its format information.
+ */
+ if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
+ oxfw->wrong_dbs = true;
+
+ /*
+ * TASCAM FireOne has physical control and requires a pair of additional
+ * MIDI ports.
+ */
+ if (vendor == VENDOR_TASCAM) {
+ oxfw->midi_input_ports++;
+ oxfw->midi_output_ports++;
+ }
+}
+
static int oxfw_probe(struct fw_unit *unit,
const struct ieee1394_device_id *id)
{
@@ -157,6 +194,8 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
+ detect_quirks(oxfw);
+
err = name_card(oxfw);
if (err < 0)
goto error;
@@ -294,6 +333,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
.specifier_id = SPECIFIER_1394TA,
.version = VERSION_AVC,
},
+ /* TASCAM, FireOne */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_TASCAM,
+ .model_id = 0x800007,
+ },
{ }
};
MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index cace5ad4fe76..8392c424ad1d 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -28,7 +28,7 @@
#include "../fcp.h"
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
struct device_info {
@@ -49,6 +49,7 @@ struct snd_oxfw {
struct mutex mutex;
spinlock_t lock;
+ bool wrong_dbs;
bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
new file mode 100644
index 000000000000..0fc955d5bd15
--- /dev/null
+++ b/sound/firewire/tascam/Makefile
@@ -0,0 +1,4 @@
+snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+ tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
+ tascam-midi.o tascam.o
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
new file mode 100644
index 000000000000..9dd0fccd5ccc
--- /dev/null
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -0,0 +1,243 @@
+/*
+ * amdtp-tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "tascam.h"
+
+#define AMDTP_FMT_TSCM_TX 0x1e
+#define AMDTP_FMT_TSCM_RX 0x3e
+
+struct amdtp_tscm {
+ unsigned int pcm_channels;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+};
+
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ data_channels = p->pcm_channels;
+
+ /* Packets in in-stream have extra 2 data channels. */
+ if (s->direction == AMDTP_IN_STREAM)
+ data_channels += 2;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src << 16);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ /* The first data channel is for event counter. */
+ buffer += 1;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]);
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = 0x00000000;
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /*
+ * Our implementation allows this protocol to deliver 24 bit sample in
+ * 32bit data channel.
+ */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_tscm *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (data_blocks > 0 && pcm)
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+
+ /* A place holder for control messages. */
+
+ return data_blocks;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ /* This field is not used. */
+ *syt = 0x0000;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm)
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ else
+ write_pcm_silence(s, buffer, data_blocks);
+
+ return data_blocks;
+}
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+ struct amdtp_tscm *p;
+ unsigned int fmt;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ fmt = AMDTP_FMT_TSCM_TX;
+ process_data_blocks = process_tx_data_blocks;
+ } else {
+ fmt = AMDTP_FMT_TSCM_RX;
+ process_data_blocks = process_rx_data_blocks;
+ }
+
+ err = amdtp_stream_init(s, unit, dir,
+ CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
+ process_data_blocks, sizeof(struct amdtp_tscm));
+ if (err < 0)
+ return 0;
+
+ /* Use fixed value for FDF field. */
+ s->fdf = 0x00;
+
+ /* This protocol uses fixed number of data channels for PCM samples. */
+ p = s->protocol;
+ p->pcm_channels = pcm_channels;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
new file mode 100644
index 000000000000..131267c3a042
--- /dev/null
+++ b/sound/firewire/tascam/tascam-hwdep.c
@@ -0,0 +1,201 @@
+/*
+ * tascam-hwdep.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "tascam.h"
+
+static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
+ long count)
+{
+ union snd_firewire_event event;
+
+ memset(&event, 0, sizeof(event));
+
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (tscm->dev_lock_count > 0);
+ tscm->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&tscm->lock);
+
+ while (!tscm->dev_lock_changed) {
+ prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&tscm->lock);
+ schedule();
+ finish_wait(&tscm->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&tscm->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ count = hwdep_read_locked(tscm, buf, count);
+ spin_unlock_irq(&tscm->lock);
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &tscm->hwdep_wait, wait);
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_changed)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(tscm->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == 0) {
+ tscm->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == -1) {
+ tscm->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_count == -1)
+ tscm->dev_lock_count = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(tscm, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(tscm);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(tscm);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Tascam");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = tscm;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c
new file mode 100644
index 000000000000..41f842079d9d
--- /dev/null
+++ b/sound/firewire/tascam/tascam-midi.c
@@ -0,0 +1,135 @@
+/*
+ * tascam-midi.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ /* Initialize internal status. */
+ tscm->running_status[substream->number] = 0;
+ tscm->on_sysex[substream->number] = 0;
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
+
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ tscm->tx_midi_substreams[substrm->number] = substrm;
+ else
+ tscm->tx_midi_substreams[substrm->number] = NULL;
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
+ substrm);
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+};
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ struct snd_rawmidi_substream *subs;
+ int err;
+
+ err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
+ tscm->spec->midi_playback_ports,
+ tscm->spec->midi_capture_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", tscm->card->shortname);
+ rmidi->private_data = tscm;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ /* Set port names for MIDI input. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ /* TODO: support virtual MIDI ports. */
+ if (subs->number < tscm->spec->midi_capture_ports) {
+ /* Hardware MIDI ports. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ /* Set port names for MIDI ourput. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ if (subs->number < tscm->spec->midi_playback_ports) {
+ /* Hardware MIDI ports only. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
new file mode 100644
index 000000000000..380d3db969a5
--- /dev/null
+++ b/sound/firewire/tascam/tascam-pcm.c
@@ -0,0 +1,312 @@
+/*
+ * tascam-pcm.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static void set_buffer_params(struct snd_pcm_hardware *hw)
+{
+ hw->period_bytes_min = 4 * hw->channels_min;
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * 2;
+
+ hw->periods_min = 2;
+ hw->periods_max = UINT_MAX;
+}
+
+static int pcm_init_hw_params(struct snd_tscm *tscm,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 18,
+ };
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *stream;
+ unsigned int pcm_channels;
+
+ runtime->hw = hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->tx_stream;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ } else {
+ runtime->hw.formats =
+ SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->rx_stream;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ }
+
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
+
+ set_buffer_params(&runtime->hw);
+
+ return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ enum snd_tscm_clock clock;
+ unsigned int rate;
+ int err;
+
+ err = snd_tscm_stream_lock_try(tscm);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(tscm, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_tscm_stream_get_clock(tscm, &clock);
+ if (clock != SND_TSCM_CLOCK_INTERNAL ||
+ amdtp_stream_pcm_running(&tscm->rx_stream) ||
+ amdtp_stream_pcm_running(&tscm->tx_stream)) {
+ err = snd_tscm_stream_get_rate(tscm, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_tscm_stream_lock_release(tscm);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ snd_tscm_stream_lock_release(tscm);
+
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&tscm->mutex);
+ tscm->substreams_counter++;
+ mutex_unlock(&tscm->mutex);
+ }
+
+ amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&tscm->mutex);
+ tscm->substreams_counter++;
+ mutex_unlock(&tscm->mutex);
+ }
+
+ amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ mutex_lock(&tscm->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ tscm->substreams_counter--;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ mutex_unlock(&tscm->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ mutex_lock(&tscm->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ tscm->substreams_counter--;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ mutex_unlock(&tscm->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->tx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->rx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&tscm->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&tscm->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = tscm;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", tscm->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c
new file mode 100644
index 000000000000..bfd4a4c06914
--- /dev/null
+++ b/sound/firewire/tascam/tascam-proc.c
@@ -0,0 +1,88 @@
+/*
+ * tascam-proc.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./tascam.h"
+
+static void proc_read_firmware(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_tscm *tscm = entry->private_data;
+ __be32 data;
+ unsigned int reg, fpga, arm, hw;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ reg = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ fpga = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ arm = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ hw = be32_to_cpu(data);
+
+ snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
+ snd_iprintf(buffer, "FPGA: %d (0x%08x)\n", fpga & 0xffff, fpga);
+ snd_iprintf(buffer, "ARM: %d (0x%08x)\n", arm & 0xffff, arm);
+ snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
+}
+
+static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(tscm->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, tscm, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_tscm_proc_init(struct snd_tscm *tscm)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(tscm->card, "firewire",
+ tscm->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(tscm, root, "firmware", proc_read_firmware);
+}
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
new file mode 100644
index 000000000000..0e6dd5c61f53
--- /dev/null
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -0,0 +1,496 @@
+/*
+ * tascam-stream.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include "tascam.h"
+
+#define CALLBACK_TIMEOUT 500
+
+static int get_clock(struct snd_tscm *tscm, u32 *data)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *data = be32_to_cpu(reg);
+
+ return err;
+}
+
+static int set_clock(struct snd_tscm *tscm, unsigned int rate,
+ enum snd_tscm_clock clock)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+ data &= 0x0000ffff;
+
+ if (rate > 0) {
+ data &= 0x000000ff;
+ /* Base rate. */
+ if ((rate % 44100) == 0) {
+ data |= 0x00000100;
+ /* Multiplier. */
+ if (rate / 44100 == 2)
+ data |= 0x00008000;
+ } else if ((rate % 48000) == 0) {
+ data |= 0x00000200;
+ /* Multiplier. */
+ if (rate / 48000 == 2)
+ data |= 0x00008000;
+ } else {
+ return -EAGAIN;
+ }
+ }
+
+ if (clock != INT_MAX) {
+ data &= 0x0000ff00;
+ data |= clock + 1;
+ }
+
+ reg = cpu_to_be32(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ if (data & 0x00008000)
+ reg = cpu_to_be32(0x0000001a);
+ else
+ reg = cpu_to_be32(0x0000000d);
+
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
+{
+ u32 data = 0x0;
+ unsigned int trials = 0;
+ int err;
+
+ while (data == 0x0 || trials++ < 5) {
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ data = (data & 0xff000000) >> 24;
+ }
+
+ /* Check base rate. */
+ if ((data & 0x0f) == 0x01)
+ *rate = 44100;
+ else if ((data & 0x0f) == 0x02)
+ *rate = 48000;
+ else
+ return -EAGAIN;
+
+ /* Check multiplier. */
+ if ((data & 0xf0) == 0x80)
+ *rate *= 2;
+ else if ((data & 0xf0) != 0x00)
+ return -EAGAIN;
+
+ return err;
+}
+
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
+{
+ u32 data;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ *clock = ((data & 0x00ff0000) >> 16) - 1;
+ if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
+ return -EIO;
+
+ return 0;
+}
+
+static int enable_data_channels(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ u32 data;
+ unsigned int i;
+ int err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+}
+
+static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ /* Set an option for unknown purpose. */
+ reg = cpu_to_be32(0x00200000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ err = enable_data_channels(tscm);
+ if (err < 0)
+ return err;
+
+ return set_clock(tscm, rate, INT_MAX);
+}
+
+static void finish_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+
+}
+
+static int begin_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ int err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Set an option for unknown purpose. */
+ reg = cpu_to_be32(0x00002000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Start multiplexing PCM samples on packets. */
+ reg = cpu_to_be32(0x00000001);
+ return snd_fw_transaction(tscm->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
+ &reg, sizeof(reg), 0);
+}
+
+static void release_resources(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ /* Unregister channels. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+
+ /* Release isochronous resources. */
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+}
+
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ /* Keep resources for in-stream. */
+ err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&tscm->tx_resources,
+ amdtp_stream_get_max_payload(&tscm->tx_stream),
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ /* Keep resources for out-stream. */
+ err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&tscm->rx_resources,
+ amdtp_stream_get_max_payload(&tscm->rx_stream),
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Register the isochronous channel for transmitting stream. */
+ reg = cpu_to_be32(tscm->tx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ /* Unknown */
+ reg = cpu_to_be32(0x00000002);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ /* Register the isochronous channel for receiving stream. */
+ reg = cpu_to_be32(tscm->rx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ release_resources(tscm);
+ return err;
+}
+
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
+{
+ unsigned int pcm_channels;
+ int err;
+
+ /* For out-stream. */
+ err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
+ if (err < 0)
+ return err;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
+ pcm_channels);
+ if (err < 0)
+ return err;
+
+ /* For in-stream. */
+ err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
+ if (err < 0)
+ return err;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
+ pcm_channels);
+ if (err < 0)
+ amdtp_stream_destroy(&tscm->rx_stream);
+
+ return 0;
+}
+
+/* At bus reset, streaming is stopped and some registers are clear. */
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
+{
+ amdtp_stream_pcm_abort(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->tx_stream);
+
+ amdtp_stream_pcm_abort(&tscm->rx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
+{
+ amdtp_stream_destroy(&tscm->rx_stream);
+ amdtp_stream_destroy(&tscm->tx_stream);
+
+ fw_iso_resources_destroy(&tscm->rx_resources);
+ fw_iso_resources_destroy(&tscm->tx_resources);
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+ unsigned int curr_rate;
+ int err;
+
+ if (tscm->substreams_counter == 0)
+ return 0;
+
+ err = snd_tscm_stream_get_rate(tscm, &curr_rate);
+ if (err < 0)
+ return err;
+ if (curr_rate != rate ||
+ amdtp_streaming_error(&tscm->tx_stream) ||
+ amdtp_streaming_error(&tscm->rx_stream)) {
+ finish_session(tscm);
+
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ release_resources(tscm);
+ }
+
+ if (!amdtp_stream_running(&tscm->tx_stream)) {
+ amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
+ &tscm->tx_stream, &tscm->rx_stream);
+ err = keep_resources(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = set_stream_formats(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(tscm);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_stream_start(&tscm->tx_stream,
+ tscm->tx_resources.channel,
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ if (!amdtp_stream_running(&tscm->rx_stream)) {
+ err = amdtp_stream_start(&tscm->rx_stream,
+ tscm->rx_resources.channel,
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ finish_session(tscm);
+ release_resources(tscm);
+
+ return err;
+}
+
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
+{
+ if (tscm->substreams_counter > 0)
+ return;
+
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ finish_session(tscm);
+ release_resources(tscm);
+}
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
+{
+ tscm->dev_lock_changed = true;
+ wake_up(&tscm->hwdep_wait);
+}
+
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ /* user land lock this */
+ if (tscm->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (tscm->dev_lock_count++ == 0)
+ snd_tscm_stream_lock_changed(tscm);
+ err = 0;
+end:
+ spin_unlock_irq(&tscm->lock);
+ return err;
+}
+
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
+{
+ spin_lock_irq(&tscm->lock);
+
+ if (WARN_ON(tscm->dev_lock_count <= 0))
+ goto end;
+ if (--tscm->dev_lock_count == 0)
+ snd_tscm_stream_lock_changed(tscm);
+end:
+ spin_unlock_irq(&tscm->lock);
+}
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
new file mode 100644
index 000000000000..904ce0329fa1
--- /dev/null
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -0,0 +1,302 @@
+/*
+ * tascam-transaction.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+ switch (status) {
+ case 0xf6: /* Tune request. */
+ case 0xf8: /* Timing clock. */
+ case 0xfa: /* Start. */
+ case 0xfb: /* Continue. */
+ case 0xfc: /* Stop. */
+ case 0xfe: /* Active sensing. */
+ case 0xff: /* System reset. */
+ return 1;
+ case 0xf1: /* MIDI time code quarter frame. */
+ case 0xf3: /* Song select. */
+ return 2;
+ case 0xf2: /* Song position pointer. */
+ return 3;
+ case 0xf0: /* Exclusive. */
+ return 0;
+ case 0xf7: /* End of exclusive. */
+ break;
+ case 0xf4: /* Undefined. */
+ case 0xf5: /* Undefined. */
+ case 0xf9: /* Undefined. */
+ case 0xfd: /* Undefined. */
+ break;
+ default:
+ switch (status & 0xf0) {
+ case 0x80: /* Note on. */
+ case 0x90: /* Note off. */
+ case 0xa0: /* Polyphonic key pressure. */
+ case 0xb0: /* Control change and Mode change. */
+ case 0xe0: /* Pitch bend change. */
+ return 3;
+ case 0xc0: /* Program change. */
+ case 0xd0: /* Channel pressure. */
+ return 2;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+ unsigned int port = substream->number;
+ int i, len, consume;
+ u8 *label, *msg;
+ u8 status;
+
+ /* The first byte is used for label, the rest for MIDI bytes. */
+ label = buf;
+ msg = buf + 1;
+
+ consume = snd_rawmidi_transmit_peek(substream, msg, 3);
+ if (consume == 0)
+ return 0;
+
+ /* On exclusive message. */
+ if (tscm->on_sysex[port]) {
+ /* Seek the end of exclusives. */
+ for (i = 0; i < consume; ++i) {
+ if (msg[i] == 0xf7) {
+ tscm->on_sysex[port] = false;
+ break;
+ }
+ }
+
+ /* At the end of exclusive message, use label 0x07. */
+ if (!tscm->on_sysex[port]) {
+ consume = i + 1;
+ *label = (port << 4) | 0x07;
+ /* During exclusive message, use label 0x04. */
+ } else if (consume == 3) {
+ *label = (port << 4) | 0x04;
+ /* We need to fill whole 3 bytes. Go to next change. */
+ } else {
+ return 0;
+ }
+
+ len = consume;
+ } else {
+ /* The beginning of exclusives. */
+ if (msg[0] == 0xf0) {
+ /* Transfer it in next chance in another condition. */
+ tscm->on_sysex[port] = true;
+ return 0;
+ } else {
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80)
+ status = tscm->running_status[port];
+ else
+ status = msg[0];
+
+ /* Calculate consume bytes. */
+ len = calculate_message_bytes(status);
+ if (len <= 0)
+ return 0;
+
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80) {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len - 1)
+ return 0;
+ consume = len - 1;
+
+ msg[2] = msg[1];
+ msg[1] = msg[0];
+ msg[0] = tscm->running_status[port];
+ } else {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len)
+ return 0;
+ consume = len;
+
+ tscm->running_status[port] = msg[0];
+ }
+ }
+
+ *label = (port << 4) | (msg[0] >> 4);
+ }
+
+ if (len > 0 && len < 3)
+ memset(msg + len, 0, 3 - len);
+
+ return consume;
+}
+
+static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_tscm *tscm = callback_data;
+ u32 *buf = (u32 *)data;
+ unsigned int messages;
+ unsigned int i;
+ unsigned int port;
+ struct snd_rawmidi_substream *substream;
+ u8 *b;
+ int bytes;
+
+ if (offset != tscm->async_handler.offset)
+ goto end;
+
+ messages = length / 8;
+ for (i = 0; i < messages; i++) {
+ b = (u8 *)(buf + i * 2);
+
+ port = b[0] >> 4;
+ /* TODO: support virtual MIDI ports. */
+ if (port >= tscm->spec->midi_capture_ports)
+ goto end;
+
+ /* Assume the message length. */
+ bytes = calculate_message_bytes(b[1]);
+ /* On MIDI data or exclusives. */
+ if (bytes <= 0) {
+ /* Seek the end of exclusives. */
+ for (bytes = 1; bytes < 4; bytes++) {
+ if (b[bytes] == 0xf7)
+ break;
+ }
+ if (bytes == 4)
+ bytes = 3;
+ }
+
+ substream = ACCESS_ONCE(tscm->tx_midi_substreams[port]);
+ if (substream != NULL)
+ snd_rawmidi_receive(substream, b + 1, bytes);
+ }
+end:
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ unsigned int i;
+ int err;
+
+ /*
+ * Usually, two quadlets are transferred by one transaction. The first
+ * quadlet has MIDI messages, the rest includes timestamp.
+ * Sometimes, 8 set of the data is transferred by a block transaction.
+ */
+ tscm->async_handler.length = 8 * 8;
+ tscm->async_handler.address_callback = handle_midi_tx;
+ tscm->async_handler.callback_data = tscm;
+
+ err = fw_core_add_address_handler(&tscm->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_tscm_transaction_reregister(tscm);
+ if (err < 0)
+ goto error;
+
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
+ err = snd_fw_async_midi_port_init(
+ &tscm->out_ports[i], tscm->unit,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+ 4, fill_message);
+ if (err < 0)
+ goto error;
+ }
+
+ return err;
+error:
+ fw_core_remove_address_handler(&tscm->async_handler);
+ return err;
+}
+
+/* At bus reset, these registers are cleared. */
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
+{
+ struct fw_device *device = fw_parent_device(tscm->unit);
+ __be32 reg;
+ int err;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ reg = cpu_to_be32((device->card->node_id << 16) |
+ (tscm->async_handler.offset >> 32));
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(tscm->async_handler.offset);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on messaging. */
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on FireWire LED. */
+ reg = cpu_to_be32(0x0001008e);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+}
+
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ unsigned int i;
+
+ /* Turn off FireWire LED. */
+ reg = cpu_to_be32(0x0000008e);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+
+ /* Turn off messaging. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+
+ /* Unregister the address. */
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&tscm->async_handler);
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
+ snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
+}
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
new file mode 100644
index 000000000000..ee0bc1839508
--- /dev/null
+++ b/sound/firewire/tascam/tascam.c
@@ -0,0 +1,209 @@
+/*
+ * tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+MODULE_DESCRIPTION("TASCAM FireWire series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static struct snd_tscm_spec model_specs[] = {
+ {
+ .name = "FW-1884",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 8,
+ .midi_capture_ports = 4,
+ .midi_playback_ports = 4,
+ .is_controller = true,
+ },
+ {
+ .name = "FW-1082",
+ .has_adat = false,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 2,
+ .is_controller = true,
+ },
+ /* FW-1804 may be supported. */
+};
+
+static int identify_model(struct snd_tscm *tscm)
+{
+ struct fw_device *fw_dev = fw_parent_device(tscm->unit);
+ const u32 *config_rom = fw_dev->config_rom;
+ char model[9];
+ unsigned int i;
+ u8 c;
+
+ if (fw_dev->config_rom_length < 30) {
+ dev_err(&tscm->unit->device,
+ "Configuration ROM is too short.\n");
+ return -ENODEV;
+ }
+
+ /* Pick up model name from certain addresses. */
+ for (i = 0; i < 8; i++) {
+ c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
+ if (c == '\0')
+ break;
+ model[i] = c;
+ }
+ model[i] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
+ if (strcmp(model, model_specs[i].name) == 0) {
+ tscm->spec = &model_specs[i];
+ break;
+ }
+ }
+ if (tscm->spec == NULL)
+ return -ENODEV;
+
+ strcpy(tscm->card->driver, "FW-TASCAM");
+ strcpy(tscm->card->shortname, model);
+ strcpy(tscm->card->mixername, model);
+ snprintf(tscm->card->longname, sizeof(tscm->card->longname),
+ "TASCAM %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void tscm_card_free(struct snd_card *card)
+{
+ struct snd_tscm *tscm = card->private_data;
+
+ snd_tscm_transaction_unregister(tscm);
+ snd_tscm_stream_destroy_duplex(tscm);
+
+ fw_unit_put(tscm->unit);
+
+ mutex_destroy(&tscm->mutex);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_tscm *tscm;
+ int err;
+
+ /* create card */
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(struct snd_tscm), &card);
+ if (err < 0)
+ return err;
+ card->private_free = tscm_card_free;
+
+ /* initialize myself */
+ tscm = card->private_data;
+ tscm->card = card;
+ tscm->unit = fw_unit_get(unit);
+
+ mutex_init(&tscm->mutex);
+ spin_lock_init(&tscm->lock);
+ init_waitqueue_head(&tscm->hwdep_wait);
+
+ err = identify_model(tscm);
+ if (err < 0)
+ goto error;
+
+ snd_tscm_proc_init(tscm);
+
+ err = snd_tscm_stream_init_duplex(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_pcm_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_transaction_register(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_midi_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_hwdep_device(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, tscm);
+
+ return err;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_tscm_update(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ snd_tscm_transaction_reregister(tscm);
+
+ mutex_lock(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
+ mutex_unlock(&tscm->mutex);
+}
+
+static void snd_tscm_remove(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(tscm->card);
+}
+
+static const struct ieee1394_device_id snd_tscm_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ },
+ /* FE-08 requires reverse-engineering because it just has faders. */
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
+
+static struct fw_driver tscm_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-firewire-tascam",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_tscm_probe,
+ .update = snd_tscm_update,
+ .remove = snd_tscm_remove,
+ .id_table = snd_tscm_id_table,
+};
+
+static int __init snd_tscm_init(void)
+{
+ return driver_register(&tscm_driver.driver);
+}
+
+static void __exit snd_tscm_exit(void)
+{
+ driver_unregister(&tscm_driver.driver);
+}
+
+module_init(snd_tscm_init);
+module_exit(snd_tscm_exit);
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
new file mode 100644
index 000000000000..2d028d2bd3bd
--- /dev/null
+++ b/sound/firewire/tascam/tascam.h
@@ -0,0 +1,147 @@
+/*
+ * tascam.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_TASCAM_H_INCLUDED
+#define SOUND_TASCAM_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_tscm_spec {
+ const char *const name;
+ bool has_adat;
+ bool has_spdif;
+ unsigned int pcm_capture_analog_channels;
+ unsigned int pcm_playback_analog_channels;
+ unsigned int midi_capture_ports;
+ unsigned int midi_playback_ports;
+ bool is_controller;
+};
+
+#define TSCM_MIDI_IN_PORT_MAX 4
+#define TSCM_MIDI_OUT_PORT_MAX 4
+
+struct snd_tscm {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_tscm_spec *spec;
+
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int substreams_counter;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For MIDI message incoming transactions. */
+ struct fw_address_handler async_handler;
+ struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
+
+ /* For MIDI message outgoing transactions. */
+ struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
+ u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
+ bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
+
+ /* For control messages. */
+ struct snd_firewire_tascam_status *status;
+};
+
+#define TSCM_ADDR_BASE 0xffff00000000ull
+
+#define TSCM_OFFSET_FIRMWARE_REGISTER 0x0000
+#define TSCM_OFFSET_FIRMWARE_FPGA 0x0004
+#define TSCM_OFFSET_FIRMWARE_ARM 0x0008
+#define TSCM_OFFSET_FIRMWARE_HW 0x000c
+
+#define TSCM_OFFSET_ISOC_TX_CH 0x0200
+#define TSCM_OFFSET_UNKNOWN 0x0204
+#define TSCM_OFFSET_START_STREAMING 0x0208
+#define TSCM_OFFSET_ISOC_RX_CH 0x020c
+#define TSCM_OFFSET_ISOC_RX_ON 0x0210 /* Little conviction. */
+#define TSCM_OFFSET_TX_PCM_CHANNELS 0x0214
+#define TSCM_OFFSET_RX_PCM_CHANNELS 0x0218
+#define TSCM_OFFSET_MULTIPLEX_MODE 0x021c
+#define TSCM_OFFSET_ISOC_TX_ON 0x0220
+/* Unknown 0x0224 */
+#define TSCM_OFFSET_CLOCK_STATUS 0x0228
+#define TSCM_OFFSET_SET_OPTION 0x022c
+
+#define TSCM_OFFSET_MIDI_TX_ON 0x0300
+#define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304
+#define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308
+
+#define TSCM_OFFSET_LED_POWER 0x0404
+
+#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
+
+enum snd_tscm_clock {
+ SND_TSCM_CLOCK_INTERNAL = 0,
+ SND_TSCM_CLOCK_WORD = 1,
+ SND_TSCM_CLOCK_SPDIF = 2,
+ SND_TSCM_CLOCK_ADAT = 3,
+};
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels);
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
+ enum snd_tscm_clock *clock);
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm);
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
+
+void snd_tscm_proc_init(struct snd_tscm *tscm);
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
+
+#endif
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
index 33ba77dd32f2..cb89ec7c8147 100644
--- a/sound/hda/ext/hdac_ext_stream.c
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -227,7 +227,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
int stream)
{
- snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
@@ -385,14 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
break;
case HDAC_EXT_STREAM_TYPE_HOST:
- if (stream->decoupled) {
+ if (stream->decoupled && !stream->link_locked)
snd_hdac_ext_stream_decouple(ebus, stream, false);
- snd_hdac_stream_release(&stream->hstream);
- }
+ snd_hdac_stream_release(&stream->hstream);
break;
case HDAC_EXT_STREAM_TYPE_LINK:
- if (stream->decoupled)
+ if (stream->decoupled && !stream->hstream.opened)
snd_hdac_ext_stream_decouple(ebus, stream, false);
spin_lock_irq(&bus->reg_lock);
stream->link_locked = 0;
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c
index 89c2711baaaf..3060e2aee36f 100644
--- a/sound/hda/hda_bus_type.c
+++ b/sound/hda/hda_bus_type.c
@@ -4,6 +4,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/export.h>
#include <sound/hdaudio.h>
@@ -63,9 +64,21 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv)
return 1;
}
+static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ char modalias[32];
+
+ snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+ sizeof(modalias));
+ if (add_uevent_var(env, "MODALIAS=%s", modalias))
+ return -ENOMEM;
+ return 0;
+}
+
struct bus_type snd_hda_bus_type = {
.name = "hdaudio",
.match = hda_bus_match,
+ .uevent = hda_uevent,
};
EXPORT_SYMBOL_GPL(snd_hda_bus_type);
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
index 27c447e4fe5c..0e81ea89a596 100644
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/hdac_bus.c
@@ -172,6 +172,15 @@ static void process_unsol_events(struct work_struct *work)
}
}
+/**
+ * snd_hdac_bus_add_device - Add a codec to bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to add
+ *
+ * Adds the given codec to the list in the bus. The caddr_tbl array
+ * and codec_powered bits are updated, as well.
+ * Returns zero if success, or a negative error code.
+ */
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
{
if (bus->caddr_tbl[codec->addr]) {
@@ -188,6 +197,11 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
+/**
+ * snd_hdac_bus_remove_device - Remove a codec from bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to remove
+ */
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec)
{
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index db96042a497f..e361024eabb6 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -164,6 +164,43 @@ void snd_hdac_device_unregister(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+ char *newname;
+
+ if (!name)
+ return 0;
+ newname = kstrdup(name, GFP_KERNEL);
+ if (!newname)
+ return -ENOMEM;
+ kfree(codec->chip_name);
+ codec->chip_name = newname;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
+{
+ return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+ codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
+/**
* snd_hdac_make_cmd - compose a 32bit command word to be sent to the
* HD-audio controller
* @codec: the codec object
@@ -592,8 +629,10 @@ int snd_hdac_power_down_pm(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif
-/*
- * Enable/disable the link power for a codec.
+/**
+ * snd_hdac_link_power - Enable/disable the link power for a codec
+ * @codec: the codec object
+ * @bool: enable or disable the link power
*/
int snd_hdac_link_power(struct hdac_device *codec, bool enable)
{
@@ -952,3 +991,84 @@ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
return true;
}
EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+ unsigned int res;
+
+ if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+ return -1;
+
+ return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+ return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/**
+ * snd_hdac_check_power_state - check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+ hda_nid_t nid, unsigned int target_state)
+{
+ unsigned int state = codec_read(hdac, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+
+ if (state & AC_PWRST_ERROR)
+ return true;
+ state = (state >> 4) & 0x0f;
+ return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index 55c3df4458f7..8fef1b8d1fd8 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -23,6 +23,19 @@
static struct i915_audio_component *hdac_acomp;
+/**
+ * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
+ * @bus: HDA core bus
+ * @enable: enable or disable the wakeup
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function should be called during the chip reset, also called at
+ * resume for updating STATESTS register read.
+ *
+ * Returns zero for success or a negative error code.
+ */
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
{
struct i915_audio_component *acomp = bus->audio_component;
@@ -45,6 +58,19 @@ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
}
EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
+/**
+ * snd_hdac_display_power - Power up / down the power refcount
+ * @bus: HDA core bus
+ * @enable: power up or down
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function manages a refcount and calls the i915 get_power() and
+ * put_power() ops accordingly, toggling the codec wakeup, too.
+ *
+ * Returns zero for success or a negative error code.
+ */
int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
{
struct i915_audio_component *acomp = bus->audio_component;
@@ -71,6 +97,16 @@ int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
}
EXPORT_SYMBOL_GPL(snd_hdac_display_power);
+/**
+ * snd_hdac_get_display_clk - Get CDCLK in kHz
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function queries CDCLK value in kHz from the graphics driver and
+ * returns the value. A negative code is returned in error.
+ */
int snd_hdac_get_display_clk(struct hdac_bus *bus)
{
struct i915_audio_component *acomp = bus->audio_component;
@@ -134,6 +170,17 @@ static int hdac_component_master_match(struct device *dev, void *data)
return !strcmp(dev->driver->name, "i915");
}
+/**
+ * snd_hdac_i915_register_notifier - Register i915 audio component ops
+ * @aops: i915 audio component ops
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function sets the given ops to be called by the i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
{
if (WARN_ON(!hdac_acomp))
@@ -144,6 +191,18 @@ int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops
}
EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier);
+/**
+ * snd_hdac_i915_init - Initialize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
int snd_hdac_i915_init(struct hdac_bus *bus)
{
struct component_match *match = NULL;
@@ -187,6 +246,17 @@ out_err:
}
EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
+/**
+ * snd_hdac_i915_exit - Finalize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function releases the i915 audio component that has been used.
+ *
+ * Returns zero for success or a negative error code.
+ */
int snd_hdac_i915_exit(struct hdac_bus *bus)
{
struct device *dev = bus->dev;
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index b0ed870ffb88..eb8f7c30cb09 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -339,6 +339,12 @@ static const struct regmap_config hda_regmap_cfg = {
.use_single_rw = true,
};
+/**
+ * snd_hdac_regmap_init - Initialize regmap for HDA register accesses
+ * @codec: the codec object
+ *
+ * Returns zero for success or a negative error code.
+ */
int snd_hdac_regmap_init(struct hdac_device *codec)
{
struct regmap *regmap;
@@ -352,6 +358,10 @@ int snd_hdac_regmap_init(struct hdac_device *codec)
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
+/**
+ * snd_hdac_regmap_init - Release the regmap from HDA codec
+ * @codec: the codec object
+ */
void snd_hdac_regmap_exit(struct hdac_device *codec)
{
if (codec->regmap) {
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 8981159813ef..38990a77d7b7 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -426,7 +426,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
-/* snd_hdac_stream_set_params - set stream parameters
+/**
+ * snd_hdac_stream_set_params - set stream parameters
* @azx_dev: HD-audio core stream for which parameters are to be set
* @format_val: format value parameter
*
diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c
index c71142dea98a..42d61bf41969 100644
--- a/sound/hda/hdac_sysfs.c
+++ b/sound/hda/hdac_sysfs.c
@@ -45,6 +45,13 @@ CODEC_ATTR(mfg);
CODEC_ATTR_STR(vendor_name);
CODEC_ATTR_STR(chip_name);
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
static struct attribute *hdac_dev_attrs[] = {
&dev_attr_type.attr,
&dev_attr_vendor_id.attr,
@@ -54,6 +61,7 @@ static struct attribute *hdac_dev_attrs[] = {
&dev_attr_mfg.attr,
&dev_attr_vendor_name.attr,
&dev_attr_chip_name.attr,
+ &dev_attr_modalias.attr,
NULL
};
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 2a9f4a345171..2706f271a83b 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -1864,7 +1864,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - IEC958");
- chip->pcm_rear = pcm;
+ chip->pcm_iec958 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
@@ -2528,7 +2528,7 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs == 1) {
unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
- if (id2 == 0x592b || id2 == 0x592d) {
+ if ((id2 & 0xfff0) == 0x5920) { /* CS4294 and CS4298 */
err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
if (err < 0)
return err;
@@ -3780,6 +3780,11 @@ static int snd_cs46xx_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->in_suspend = 1;
snd_pcm_suspend_all(chip->pcm);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_pcm_suspend_all(chip->pcm_rear);
+ snd_pcm_suspend_all(chip->pcm_center_lfe);
+ snd_pcm_suspend_all(chip->pcm_iec958);
+#endif
// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index d5ac25cc7fee..70671ad65d24 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -15,21 +15,22 @@
#include "hda_local.h"
/*
- * find a matching codec preset
+ * find a matching codec id
*/
static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
struct hda_codec_driver *driver =
container_of(drv, struct hda_codec_driver, core);
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *list;
/* check probe_id instead of vendor_id if set */
u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+ u32 rev_id = codec->core.revision_id;
- for (preset = driver->preset; preset->id; preset++) {
- if (preset->id == id &&
- (!preset->rev || preset->rev == codec->core.revision_id)) {
- codec->preset = preset;
+ for (list = driver->id; list->vendor_id; list++) {
+ if (list->vendor_id == id &&
+ (!list->rev_id || list->rev_id == rev_id)) {
+ codec->preset = list;
return 1;
}
}
@@ -45,26 +46,45 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
codec->patch_ops.unsol_event(codec, ev);
}
-/* reset the codec name from the preset */
-static int codec_refresh_name(struct hda_codec *codec, const char *name)
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
{
- if (name) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup(name, GFP_KERNEL);
+ int err;
+
+ if (!name)
+ return 0;
+ err = snd_hdac_device_set_chip_name(&codec->core, name);
+ if (err < 0)
+ return err;
+
+ /* update the mixer name */
+ if (!*codec->card->mixername ||
+ codec->bus->mixer_assigned >= codec->core.addr) {
+ snprintf(codec->card->mixername,
+ sizeof(codec->card->mixername), "%s %s",
+ codec->core.vendor_name, codec->core.chip_name);
+ codec->bus->mixer_assigned = codec->core.addr;
}
- return codec->core.chip_name ? 0 : -ENOMEM;
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
static int hda_codec_driver_probe(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
struct module *owner = dev->driver->owner;
+ hda_codec_patch_t patch;
int err;
if (WARN_ON(!codec->preset))
return -EINVAL;
- err = codec_refresh_name(codec, codec->preset->name);
+ err = snd_hda_codec_set_name(codec, codec->preset->name);
if (err < 0)
goto error;
err = snd_hdac_regmap_init(&codec->core);
@@ -76,9 +96,12 @@ static int hda_codec_driver_probe(struct device *dev)
goto error;
}
- err = codec->preset->patch(codec);
- if (err < 0)
- goto error_module;
+ patch = (hda_codec_patch_t)codec->preset->driver_data;
+ if (patch) {
+ err = patch(codec);
+ if (err < 0)
+ goto error_module;
+ }
err = snd_hda_codec_build_pcms(codec);
if (err < 0)
@@ -155,11 +178,10 @@ static inline bool codec_probed(struct hda_codec *codec)
static void codec_bind_module(struct hda_codec *codec)
{
#ifdef MODULE
- request_module("snd-hda-codec-id:%08x", codec->core.vendor_id);
- if (codec_probed(codec))
- return;
- request_module("snd-hda-codec-id:%04x*",
- (codec->core.vendor_id >> 16) & 0xffff);
+ char modalias[32];
+
+ snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+ request_module(modalias);
if (codec_probed(codec))
return;
#endif
@@ -251,11 +273,6 @@ int snd_hda_codec_configure(struct hda_codec *codec)
}
}
- /* audio codec should override the mixer name */
- if (codec->core.afg || !*codec->card->mixername)
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername), "%s %s",
- codec->core.vendor_name, codec->core.chip_name);
return 0;
error:
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a249d5486889..83741887faa1 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -91,50 +91,6 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
}
/**
- * snd_hda_codec_read - send a command and get the response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int flags,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
- unsigned int res;
- if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res))
- return -1;
- return res;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_read);
-
-/**
- * snd_hda_codec_write - send a single command without waiting for response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
- return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_write);
-
-/**
* snd_hda_sequence_write - sequence writes
* @codec: the HDA codec
* @seq: VERB array to send
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 2970413f18a0..373fcad840ea 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -22,6 +22,7 @@
#define __SOUND_HDA_CODEC_H
#include <linux/kref.h>
+#include <linux/mod_devicetable.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -69,6 +70,7 @@ struct hda_bus {
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
int primary_dig_out_type; /* primary digital out PCM type */
+ unsigned int mixer_assigned; /* codec addr for mixer name */
};
/* from hdac_bus to hda_bus */
@@ -80,19 +82,21 @@ struct hda_bus {
* Known codecs have the patch to build and set up the controls/PCMs
* better than the generic parser.
*/
-struct hda_codec_preset {
- unsigned int id;
- unsigned int rev;
- const char *name;
- int (*patch)(struct hda_codec *codec);
-};
+typedef int (*hda_codec_patch_t)(struct hda_codec *);
#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
#define HDA_CODEC_ID_GENERIC 0x00000201
+#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
+ { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+ .api_version = HDA_DEV_LEGACY, \
+ .driver_data = (unsigned long)(_patch) }
+#define HDA_CODEC_ENTRY(_vid, _name, _patch) \
+ HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch)
+
struct hda_codec_driver {
struct hdac_driver core;
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *id;
};
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
@@ -183,7 +187,7 @@ struct hda_codec {
u32 probe_id; /* overridden id for probing */
/* detected preset */
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *preset;
const char *modelname; /* model name for preset */
/* set by patch */
@@ -297,10 +301,6 @@ struct hda_codec {
/*
* constructors
*/
-int snd_hda_bus_new(struct snd_card *card,
- const struct hdac_bus_ops *ops,
- const struct hdac_io_ops *io_ops,
- struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
unsigned int codec_addr, struct hda_codec **codecp);
int snd_hda_codec_configure(struct hda_codec *codec);
@@ -309,11 +309,21 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec);
/*
* low level functions
*/
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
+static inline unsigned int
+snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int flags,
- unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
- unsigned int verb, unsigned int parm);
+ unsigned int verb, unsigned int parm)
+{
+ return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
+}
+
+static inline int
+snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
+ unsigned int verb, unsigned int parm)
+{
+ return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
+}
+
#define snd_hda_param_read(codec, nid, param) \
snd_hdac_read_parm(&(codec)->core, nid, param)
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
@@ -453,6 +463,8 @@ void snd_hda_unlock_devices(struct hda_bus *bus);
void snd_hda_bus_reset(struct hda_bus *bus);
void snd_hda_bus_reset_codecs(struct hda_bus *bus);
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
+
/*
* power management
*/
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 944455997fdc..d6b93a20361b 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -1045,6 +1045,7 @@ int azx_bus_init(struct azx *chip, const char *model,
mutex_init(&bus->prepare_mutex);
bus->pci = chip->pci;
bus->modelname = model;
+ bus->mixer_assigned = -1;
bus->core.snoop = azx_snoop(chip);
if (chip->get_position[0] != azx_get_pos_lpib ||
chip->get_position[1] != azx_get_pos_lpib)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 24f91114a32c..c6e8a651cea1 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -5877,13 +5877,14 @@ error:
return err;
}
-static const struct hda_codec_preset snd_hda_preset_generic[] = {
- { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+static const struct hda_device_id snd_hda_id_generic[] = {
+ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
{} /* terminator */
};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
static struct hda_codec_driver generic_driver = {
- .preset = snd_hda_preset_generic,
+ .id = snd_hda_id_generic,
};
module_hda_codec_driver(generic_driver);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 4a21c2199e02..d0e066e4c985 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -681,12 +681,7 @@ static inline bool
snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int target_state)
{
- unsigned int state = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state & AC_PWRST_ERROR)
- return true;
- state = (state >> 4) & 0x0f;
- return (state == target_state);
+ return snd_hdac_check_power_state(&codec->core, nid, target_state);
}
unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
index a6e3d9b511ab..64e0d1d81ca5 100644
--- a/sound/pci/hda/hda_sysfs.c
+++ b/sound/pci/hda/hda_sysfs.c
@@ -595,8 +595,7 @@ static void parse_model_mode(char *buf, struct hda_bus *bus,
static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
struct hda_codec **codecp)
{
- kfree((*codecp)->core.chip_name);
- (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL);
+ snd_hda_codec_set_name(*codecp, buf);
}
#define DEFINE_PARSE_ID_MODE(name) \
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index c033a4ee6547..e0fb8c6d1bc2 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1165,32 +1165,31 @@ static int patch_ad1882(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
- { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
- { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
- { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
- { .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_ad1884 },
- { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
- { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
- { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
- { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
- { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
- { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
+static const struct hda_device_id snd_hda_id_analog[] = {
+ HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
+ HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
+ HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
+ HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:11d4*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
static struct hda_codec_driver analog_driver = {
- .preset = snd_hda_preset_analog,
+ .id = snd_hda_id_analog,
};
module_hda_codec_driver(analog_driver);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 484bbf4134cd..c2d9ee9cfdc0 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -83,22 +83,19 @@ static int patch_ca0110(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
- { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+static const struct hda_device_id snd_hda_id_ca0110[] = {
+ HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1102000a");
-MODULE_ALIAS("snd-hda-codec-id:1102000b");
-MODULE_ALIAS("snd-hda-codec-id:1102000d");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
static struct hda_codec_driver ca0110_driver = {
- .preset = snd_hda_preset_ca0110,
+ .id = snd_hda_id_ca0110,
};
module_hda_codec_driver(ca0110_driver);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 186792fe226e..f8a12ca477f1 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -2673,13 +2673,13 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
do {
if (dspload_is_loaded(codec)) {
- pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n");
+ codec_info(codec, "ca0132 DSP downloaded and running\n");
return true;
}
msleep(20);
} while (time_before(jiffies, timeout));
- pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
+ codec_err(codec, "ca0132 failed to download DSP\n");
return false;
}
@@ -4375,7 +4375,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec)
dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
- pr_err("ca0132 dspload_image failed.\n");
+ codec_err(codec, "ca0132 DSP load image failed\n");
goto exit_download;
}
@@ -4778,18 +4778,17 @@ static int patch_ca0132(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_ca0132[] = {
- { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
+static struct hda_device_id snd_hda_id_ca0132[] = {
+ HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:11020011");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative Sound Core3D codec");
static struct hda_codec_driver ca0132_driver = {
- .preset = snd_hda_preset_ca0132,
+ .id = snd_hda_id_ca0132,
};
module_hda_codec_driver(ca0132_driver);
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 85813de26da8..a12ae8ac0914 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -570,6 +570,7 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
return NULL;
codec->spec = spec;
spec->vendor_nid = vendor_nid;
+ codec->power_save_node = 1;
snd_hda_gen_spec_init(&spec->gen);
return spec;
@@ -1200,26 +1201,21 @@ static int patch_cs4213(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
- { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
- { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
- { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 },
- { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
- { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
+static const struct hda_device_id snd_hda_id_cirrus[] = {
+ HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
+ HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
+ HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10134206");
-MODULE_ALIAS("snd-hda-codec-id:10134207");
-MODULE_ALIAS("snd-hda-codec-id:10134208");
-MODULE_ALIAS("snd-hda-codec-id:10134210");
-MODULE_ALIAS("snd-hda-codec-id:10134213");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
static struct hda_codec_driver cirrus_driver = {
- .preset = snd_hda_preset_cirrus,
+ .id = snd_hda_id_cirrus,
};
module_hda_codec_driver(cirrus_driver);
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index f5ed078710f8..1b2195dd2b26 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -123,22 +123,19 @@ static int patch_cmi8888(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
- { .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 },
- { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
- { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+ HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
+ HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
+ HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:13f68888");
-MODULE_ALIAS("snd-hda-codec-id:13f69880");
-MODULE_ALIAS("snd-hda-codec-id:434d4980");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("C-Media HD-audio codec");
static struct hda_codec_driver cmedia_driver = {
- .preset = snd_hda_preset_cmedia,
+ .id = snd_hda_id_cmedia,
};
module_hda_codec_driver(cmedia_driver);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 2f0ec7c45fc7..c8b8ef5246a6 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -954,100 +954,44 @@ static int patch_conexant_auto(struct hda_codec *codec)
/*
*/
-static const struct hda_codec_preset snd_hda_preset_conexant[] = {
- { .id = 0x14f15045, .name = "CX20549 (Venice)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15066, .name = "CX20582 (Pebble)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15068, .name = "CX20584",
- .patch = patch_conexant_auto },
- { .id = 0x14f15069, .name = "CX20585",
- .patch = patch_conexant_auto },
- { .id = 0x14f1506c, .name = "CX20588",
- .patch = patch_conexant_auto },
- { .id = 0x14f1506e, .name = "CX20590",
- .patch = patch_conexant_auto },
- { .id = 0x14f15097, .name = "CX20631",
- .patch = patch_conexant_auto },
- { .id = 0x14f15098, .name = "CX20632",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a1, .name = "CX20641",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a2, .name = "CX20642",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ab, .name = "CX20651",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ac, .name = "CX20652",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b8, .name = "CX20664",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b9, .name = "CX20665",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f1, .name = "CX20721",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f2, .name = "CX20722",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f3, .name = "CX20723",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f4, .name = "CX20724",
- .patch = patch_conexant_auto },
- { .id = 0x14f1510f, .name = "CX20751/2",
- .patch = patch_conexant_auto },
- { .id = 0x14f15110, .name = "CX20751/2",
- .patch = patch_conexant_auto },
- { .id = 0x14f15111, .name = "CX20753/4",
- .patch = patch_conexant_auto },
- { .id = 0x14f15113, .name = "CX20755",
- .patch = patch_conexant_auto },
- { .id = 0x14f15114, .name = "CX20756",
- .patch = patch_conexant_auto },
- { .id = 0x14f15115, .name = "CX20757",
- .patch = patch_conexant_auto },
- { .id = 0x14f151d7, .name = "CX20952",
- .patch = patch_conexant_auto },
+static const struct hda_device_id snd_hda_id_conexant[] = {
+ HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f1, "CX20721", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f3, "CX20723", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:14f15045");
-MODULE_ALIAS("snd-hda-codec-id:14f15047");
-MODULE_ALIAS("snd-hda-codec-id:14f15051");
-MODULE_ALIAS("snd-hda-codec-id:14f15066");
-MODULE_ALIAS("snd-hda-codec-id:14f15067");
-MODULE_ALIAS("snd-hda-codec-id:14f15068");
-MODULE_ALIAS("snd-hda-codec-id:14f15069");
-MODULE_ALIAS("snd-hda-codec-id:14f1506c");
-MODULE_ALIAS("snd-hda-codec-id:14f1506e");
-MODULE_ALIAS("snd-hda-codec-id:14f15097");
-MODULE_ALIAS("snd-hda-codec-id:14f15098");
-MODULE_ALIAS("snd-hda-codec-id:14f150a1");
-MODULE_ALIAS("snd-hda-codec-id:14f150a2");
-MODULE_ALIAS("snd-hda-codec-id:14f150ab");
-MODULE_ALIAS("snd-hda-codec-id:14f150ac");
-MODULE_ALIAS("snd-hda-codec-id:14f150b8");
-MODULE_ALIAS("snd-hda-codec-id:14f150b9");
-MODULE_ALIAS("snd-hda-codec-id:14f150f1");
-MODULE_ALIAS("snd-hda-codec-id:14f150f2");
-MODULE_ALIAS("snd-hda-codec-id:14f150f3");
-MODULE_ALIAS("snd-hda-codec-id:14f150f4");
-MODULE_ALIAS("snd-hda-codec-id:14f1510f");
-MODULE_ALIAS("snd-hda-codec-id:14f15110");
-MODULE_ALIAS("snd-hda-codec-id:14f15111");
-MODULE_ALIAS("snd-hda-codec-id:14f15113");
-MODULE_ALIAS("snd-hda-codec-id:14f15114");
-MODULE_ALIAS("snd-hda-codec-id:14f15115");
-MODULE_ALIAS("snd-hda-codec-id:14f151d7");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
static struct hda_codec_driver conexant_driver = {
- .preset = snd_hda_preset_conexant,
+ .id = snd_hda_id_conexant,
};
module_hda_codec_driver(conexant_driver);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index acbfbe087ee8..f503a883bef3 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1775,6 +1775,16 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
return non_pcm;
}
+/* There is a fixed mapping between audio pin node and display port
+ * on current Intel platforms:
+ * Pin Widget 5 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 6 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ */
+static int intel_pin2port(hda_nid_t pin_nid)
+{
+ return pin_nid - 4;
+}
/*
* HDMI callbacks
@@ -1791,6 +1801,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
int pin_idx = hinfo_to_pin_index(codec, hinfo);
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct i915_audio_component *acomp = codec->bus->core.audio_component;
bool non_pcm;
int pinctl;
@@ -1807,6 +1819,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
}
+ /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
+ /* Todo: add DP1.2 MST audio support later */
+ if (acomp && acomp->ops && acomp->ops->sync_audio_rate)
+ acomp->ops->sync_audio_rate(acomp->dev,
+ intel_pin2port(pin_nid),
+ runtime->rate);
+
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock);
per_pin->channels = substream->runtime->channels;
@@ -2561,7 +2580,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hdmi_spec *spec = codec->spec;
struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
- switch (codec->preset->id) {
+ switch (codec->preset->vendor_id) {
case 0x10de0002:
case 0x10de0003:
case 0x10de0005:
@@ -2879,7 +2898,7 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
snd_pcm_alt_chmaps, 8, 0, &chmap);
if (err < 0)
return err;
- switch (codec->preset->id) {
+ switch (codec->preset->vendor_id) {
case 0x10de0002:
case 0x10de0003:
case 0x10de0005:
@@ -3487,138 +3506,77 @@ static int patch_via_hdmi(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
-{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
-{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi },
+static const struct hda_device_id snd_hda_id_hdmi[] = {
+HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi),
/* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
-{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
-{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862809, .name = "Skylake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x8086280a, .name = "Broxton HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
/* special ID for generic HDMI */
-{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0015");
-MODULE_ALIAS("snd-hda-codec-id:10de0016");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0028");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0051");
-MODULE_ALIAS("snd-hda-codec-id:10de0060");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de0070");
-MODULE_ALIAS("snd-hda-codec-id:10de0071");
-MODULE_ALIAS("snd-hda-codec-id:10de0072");
-MODULE_ALIAS("snd-hda-codec-id:10de007d");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-MODULE_ALIAS("snd-hda-codec-id:11069f80");
-MODULE_ALIAS("snd-hda-codec-id:11069f81");
-MODULE_ALIAS("snd-hda-codec-id:11069f84");
-MODULE_ALIAS("snd-hda-codec-id:11069f85");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80862806");
-MODULE_ALIAS("snd-hda-codec-id:80862807");
-MODULE_ALIAS("snd-hda-codec-id:80862808");
-MODULE_ALIAS("snd-hda-codec-id:80862809");
-MODULE_ALIAS("snd-hda-codec-id:8086280a");
-MODULE_ALIAS("snd-hda-codec-id:80862880");
-MODULE_ALIAS("snd-hda-codec-id:80862882");
-MODULE_ALIAS("snd-hda-codec-id:80862883");
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("HDMI HD-audio codec");
@@ -3627,7 +3585,7 @@ MODULE_ALIAS("snd-hda-codec-nvhdmi");
MODULE_ALIAS("snd-hda-codec-atihdmi");
static struct hda_codec_driver hdmi_driver = {
- .preset = snd_hda_preset_hdmi,
+ .id = snd_hda_id_hdmi,
};
module_hda_codec_driver(hdmi_driver);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 720a9fb32e20..2f7b065f9ac4 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -822,17 +822,7 @@ static const struct hda_codec_ops alc_patch_ops = {
};
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup(name, GFP_KERNEL);
- if (!codec->core.chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return 0;
-}
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
/*
* Rename codecs appropriately from COEF value or subvendor id
@@ -6640,78 +6630,70 @@ static int patch_alc680(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_realtek[] = {
- { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
- { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
- { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
- { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
- { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
- { .id = 0x10ec0256, .name = "ALC256", .patch = patch_alc269 },
- { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
- { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
- { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
- { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
- { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
- { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
- { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
- { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
- { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
- { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
- { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
- { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
- { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
- { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
- { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
- { .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 },
- { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
- { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
- { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
- { .id = 0x10ec0298, .name = "ALC298", .patch = patch_alc269 },
- { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
- .patch = patch_alc861 },
- { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
- { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
- .patch = patch_alc882 },
- { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
- .patch = patch_alc662 },
- { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3",
- .patch = patch_alc662 },
- { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
- { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
- { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
- { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
- { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
- { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
- { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
- { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
- { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
- { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
- { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
- { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
- .patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
- { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
- { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
- { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 },
- { .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 },
+static const struct hda_device_id snd_hda_id_realtek[] = {
+ HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
+ HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262),
+ HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
+ HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
+ HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
+ HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
+ HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10ec*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek HD-audio codec");
static struct hda_codec_driver realtek_driver = {
- .preset = snd_hda_preset_realtek,
+ .id = snd_hda_id_realtek,
};
module_hda_codec_driver(realtek_driver);
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 5104bebb2286..ffda38c45509 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -289,41 +289,30 @@ static int patch_si3054(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_si3054[] = {
- { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+static const struct hda_device_id snd_hda_id_si3054[] = {
+ HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054),
/* VIA HDA on Clevo m540 */
- { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
/* Asus A8J Modem (SM56) */
- { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
/* LG LW20 modem */
- { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
{}
};
-
-MODULE_ALIAS("snd-hda-codec-id:163c3055");
-MODULE_ALIAS("snd-hda-codec-id:163c3155");
-MODULE_ALIAS("snd-hda-codec-id:11c13026");
-MODULE_ALIAS("snd-hda-codec-id:11c13055");
-MODULE_ALIAS("snd-hda-codec-id:11c13155");
-MODULE_ALIAS("snd-hda-codec-id:10573055");
-MODULE_ALIAS("snd-hda-codec-id:10573057");
-MODULE_ALIAS("snd-hda-codec-id:10573155");
-MODULE_ALIAS("snd-hda-codec-id:11063288");
-MODULE_ALIAS("snd-hda-codec-id:15433155");
-MODULE_ALIAS("snd-hda-codec-id:18540018");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
static struct hda_codec_driver si3054_driver = {
- .preset = snd_hda_preset_si3054,
+ .id = snd_hda_id_si3054,
};
module_hda_codec_driver(si3054_driver);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index def5cc8dff02..08a0f6a35cca 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -5012,121 +5012,119 @@ static int patch_stac9872(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
- { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
- { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
- { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
- { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
- { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
- { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
- { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
- { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
- { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
- { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
- { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
- { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
- { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
- { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
- { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
- { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
- { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
- { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
- { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
- { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
- { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
- { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
- { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
- /* The following does not take into account .id=0x83847661 when subsys =
- * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
- * currently not fully supported.
- */
- { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
- { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
- { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
- { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
- { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
- { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
- { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
- { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
- { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
- { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
- { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
- { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx},
+static const struct hda_device_id snd_hda_id_sigmatel[] = {
+ HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200),
+ HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x),
+ /* The following does not take into account .id=0x83847661 when subsys =
+ * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+ * currently not fully supported.
+ */
+ HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95),
+ HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:8384*");
-MODULE_ALIAS("snd-hda-codec-id:111d*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
static struct hda_codec_driver sigmatel_driver = {
- .preset = snd_hda_preset_sigmatel,
+ .id = snd_hda_id_sigmatel,
};
module_hda_codec_driver(sigmatel_driver);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index da5366405eda..fc30d1e8aa76 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -785,21 +785,11 @@ static int patch_vt1708S(struct hda_codec *codec)
override_mic_boost(codec, 0x1e, 0, 3, 40);
/* correct names for VT1708BCE */
- if (get_codec_type(codec) == VT1708BCE) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername),
- "%s %s", codec->core.vendor_name, codec->core.chip_name);
- }
+ if (get_codec_type(codec) == VT1708BCE)
+ snd_hda_codec_set_name(codec, "VT1708BCE");
/* correct names for VT1705 */
- if (codec->core.vendor_id == 0x11064397) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL);
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername),
- "%s %s", codec->core.vendor_name, codec->core.chip_name);
- }
+ if (codec->core.vendor_id == 0x11064397)
+ snd_hda_codec_set_name(codec, "VT1705");
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
@@ -1210,109 +1200,64 @@ static int patch_vt3476(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_via[] = {
- { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106e710, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e711, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e712, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e713, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e714, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e715, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e716, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e717, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e720, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e721, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e722, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e723, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e724, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e725, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e726, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e727, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x11060397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11061397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11062397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11063397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11064397, .name = "VT1705",
- .patch = patch_vt1708S},
- { .id = 0x11065397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11066397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11067397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11060398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11061398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11062398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11063398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11064398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11065398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11066398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11067398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11060428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11064428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11060441, .name = "VT2020",
- .patch = patch_vt1718S},
- { .id = 0x11064441, .name = "VT1828S",
- .patch = patch_vt1718S},
- { .id = 0x11060433, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x1106a721, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
- { .id = 0x11060440, .name = "VT1818S",
- .patch = patch_vt1708S},
- { .id = 0x11060446, .name = "VT1802",
- .patch = patch_vt2002P},
- { .id = 0x11068446, .name = "VT1802",
- .patch = patch_vt2002P},
- { .id = 0x11064760, .name = "VT1705CF",
- .patch = patch_vt3476},
- { .id = 0x11064761, .name = "VT1708SCE",
- .patch = patch_vt3476},
- { .id = 0x11064762, .name = "VT1808",
- .patch = patch_vt3476},
+static const struct hda_device_id snd_hda_id_via[] = {
+ HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
+ HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1106*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
static struct hda_codec_driver via_driver = {
- .preset = snd_hda_preset_via,
+ .id = snd_hda_id_via,
};
MODULE_LICENSE("GPL");
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 7acbc21d642a..9e1ad119a3ce 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -1394,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
+
return 0;
}
@@ -1422,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
return 0;
}
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index cba89beb2b38..8b8e2e54fba3 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -234,8 +234,8 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
/* the clock rate cannot be changed */
board_rate = chip->board_sample_rate;
- err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
- board_rate, board_rate);
+ err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ board_rate);
if (err < 0) {
dev_warn(chip->card->dev, "could not constrain periods\n");
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 72e89cedc52d..17ae92613de4 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -1929,15 +1929,32 @@ snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
return;
snd_m3_outw(chip, val, CODEC_DATA);
snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+ /*
+ * Workaround for buggy ES1988 integrated AC'97 codec. It remains silent
+ * until the MASTER volume or mute is touched (alsactl restore does not
+ * work).
+ */
+ if (ac97->id == 0x45838308 && reg == AC97_MASTER) {
+ snd_m3_ac97_wait(chip);
+ snd_m3_outw(chip, val, CODEC_DATA);
+ snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+ }
}
-static void snd_m3_remote_codec_config(int io, int isremote)
+static void snd_m3_remote_codec_config(struct snd_m3 *chip, int isremote)
{
+ int io = chip->iobase;
+ u16 tmp;
+
isremote = isremote ? 1 : 0;
- outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
- io + RING_BUS_CTRL_B);
+ tmp = inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK;
+ /* enable dock on Dell Latitude C810 */
+ if (chip->pci->subsystem_vendor == 0x1028 &&
+ chip->pci->subsystem_device == 0x00e5)
+ tmp |= M3I_DOCK_ENABLE;
+ outw(tmp | isremote, io + RING_BUS_CTRL_B);
outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
io + SDO_OUT_DEST_CTRL);
outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
@@ -1989,7 +2006,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip)
if (!chip->irda_workaround)
dir |= 0x10; /* assuming pci bus master? */
- snd_m3_remote_codec_config(io, 0);
+ snd_m3_remote_codec_config(chip, 0);
outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
udelay(20);
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 23d7f5d30c41..cd94ac548ba3 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -831,9 +831,9 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
static void snd_rme32_set_buffer_constraint(struct rme32 *rme32, struct snd_pcm_runtime *runtime)
{
if (! rme32->fullduplex_mode) {
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+ RME32_BUFFER_SIZE);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
&hw_constraints_period_bytes);
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 2306ccf7281e..714df906249e 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1152,13 +1152,13 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96,
{
unsigned int size;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ RME96_BUFFER_SIZE);
if ((size = rme96->playback_periodsize) != 0 ||
(size = rme96->capture_periodsize) != 0)
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- size, size);
+ size);
else
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 9bba275b4c9b..2875b4f6d8c9 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -5112,6 +5112,7 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
dev_err(hdsp->card->dev,
"too short firmware size %d (expected %d)\n",
(int)fw->size, HDSP_FIRMWARE_SIZE);
+ release_firmware(fw);
return -EINVAL;
}
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index cb666c73712d..8bc8016c173d 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6080,18 +6080,17 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
32, 4096);
/* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- 16384, 16384);
+ 16384);
break;
default:
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
64, 8192);
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS,
- 2, 2);
+ snd_pcm_hw_constraint_single(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
break;
}
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 225bfda414e9..7ff7d88e46dd 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -9,7 +9,6 @@ menuconfig SND_SOC
select SND_JACK if INPUT=y || INPUT=SND
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
- select SND_COMPRESS_OFFLOAD
---help---
If you want ASoC support, you should say Y here and also to the
@@ -30,6 +29,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM
bool
select SND_DMAENGINE_PCM
+config SND_SOC_COMPRESS
+ bool
+ select SND_COMPRESS_OFFLOAD
+
config SND_SOC_TOPOLOGY
bool
@@ -58,6 +61,7 @@ source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sti/Kconfig"
+source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/ux500/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 134aca150a50..8eb06db32fa0 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,5 +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 soc-devres.o soc-ops.o
+snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
snd-soc-core-objs += soc-topology.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += sti/
+obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 1489cd461aec..2d30464b81ce 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -59,4 +59,13 @@ config SND_AT91_SOC_SAM9X5_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_ATMEL_SOC_CLASSD
+ tristate "Atmel ASoC driver for boards using CLASSD"
+ depends on ARCH_AT91 || COMPILE_TEST
+ select SND_ATMEL_SOC_DMA
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ CLASSD.
endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index b327e5cc8de3..f6f7db428216 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
snd-atmel-soc-wm8904-objs := atmel_wm8904.o
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
+snd-atmel-soc-classd-objs := atmel-classd.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_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
new file mode 100644
index 000000000000..8276675730ef
--- /dev/null
+++ b/sound/soc/atmel/atmel-classd.c
@@ -0,0 +1,679 @@
+/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
+ *
+ * Copyright (C) 2015 Atmel
+ *
+ * Author: Songjun Wu <songjun.wu@atmel.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 or later
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "atmel-classd.h"
+
+struct atmel_classd_pdata {
+ bool non_overlap_enable;
+ int non_overlap_time;
+ int pwm_type;
+ const char *card_name;
+};
+
+struct atmel_classd {
+ dma_addr_t phy_base;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct clk *aclk;
+ int irq;
+ const struct atmel_classd_pdata *pdata;
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_classd_of_match[] = {
+ {
+ .compatible = "atmel,sama5d2-classd",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_classd_of_match);
+
+static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct atmel_classd_pdata *pdata;
+ const char *pwm_type;
+ int ret;
+
+ if (!np) {
+ dev_err(dev, "device node not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type);
+ if ((ret == 0) && (strcmp(pwm_type, "diff") == 0))
+ pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
+ else
+ pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
+
+ ret = of_property_read_u32(np,
+ "atmel,non-overlap-time", &pdata->non_overlap_time);
+ if (ret)
+ pdata->non_overlap_enable = false;
+ else
+ pdata->non_overlap_enable = true;
+
+ ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
+ if (ret)
+ pdata->card_name = "CLASSD";
+
+ return pdata;
+}
+#else
+static inline struct atmel_classd_pdata *
+atmel_classd_dt_init(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
+ | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \
+ | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \
+ | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \
+ | SNDRV_PCM_RATE_96000)
+
+static const struct snd_pcm_hardware atmel_classd_hw = {
+ .info = SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = ATMEL_CLASSD_RATES,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 256,
+ .period_bytes_max = 32 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+};
+
+#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024)
+
+/* cpu dai component */
+static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ regmap_write(dd->regmap, CLASSD_THR, 0x0);
+
+ return clk_prepare_enable(dd->pclk);
+}
+
+static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(dd->pclk);
+}
+
+static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
+ .startup = atmel_classd_cpu_dai_startup,
+ .shutdown = atmel_classd_cpu_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ATMEL_CLASSD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &atmel_classd_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
+ .name = "atmel-classd",
+};
+
+/* platform */
+static int
+atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ if (params_physical_width(params) != 16) {
+ dev_err(rtd->platform->dev,
+ "only supports 16-bit audio data\n");
+ return -EINVAL;
+ }
+
+ slave_config->direction = DMA_MEM_TO_DEV;
+ slave_config->dst_addr = dd->phy_base + CLASSD_THR;
+ slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave_config->dst_maxburst = 1;
+ slave_config->src_maxburst = 1;
+ slave_config->device_fc = false;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config
+atmel_classd_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_classd_platform_configure_dma,
+ .pcm_hardware = &atmel_classd_hw,
+ .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE,
+};
+
+/* codec */
+static const char * const mono_mode_text[] = {
+ "mix", "sat", "left", "right"
+};
+
+static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
+ mono_mode_text);
+
+static const char * const eqcfg_text[] = {
+ "Treble-12dB", "Treble-6dB",
+ "Medium-8dB", "Medium-3dB",
+ "Bass-12dB", "Bass-6dB",
+ "0 dB",
+ "Bass+6dB", "Bass+12dB",
+ "Medium+3dB", "Medium+8dB",
+ "Treble+6dB", "Treble+12dB",
+};
+
+static const unsigned int eqcfg_value[] = {
+ CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
+ CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
+ CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
+ CLASSD_INTPMR_EQCFG_FLAT,
+ CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
+ CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
+ CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
+ eqcfg_text, eqcfg_value);
+
+static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
+
+static const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
+SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
+ CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
+ 78, 1, classd_digital_tlv),
+
+SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
+ CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
+
+SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
+
+SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
+
+SOC_ENUM("Mono Mode", classd_mono_mode_enum),
+
+SOC_ENUM("EQ", classd_eqcfg_enum),
+};
+
+static const char * const pwm_type[] = {
+ "Single ended", "Differential"
+};
+
+static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+ const struct atmel_classd_pdata *pdata = dd->pdata;
+ u32 mask, val;
+
+ mask = CLASSD_MR_PWMTYP_MASK;
+ val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
+
+ mask |= CLASSD_MR_NON_OVERLAP_MASK;
+ if (pdata->non_overlap_enable) {
+ val |= (CLASSD_MR_NON_OVERLAP_EN
+ << CLASSD_MR_NON_OVERLAP_SHIFT);
+
+ mask |= CLASSD_MR_NOVR_VAL_MASK;
+ switch (pdata->non_overlap_time) {
+ case 5:
+ val |= (CLASSD_MR_NOVR_VAL_5NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 10:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 15:
+ val |= (CLASSD_MR_NOVR_VAL_15NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 20:
+ val |= (CLASSD_MR_NOVR_VAL_20NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ default:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ dev_warn(codec->dev,
+ "non-overlapping value %d is invalid, the default value 10 is specified\n",
+ pdata->non_overlap_time);
+ break;
+ }
+ }
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ dev_info(codec->dev,
+ "PWM modulation type is %s, non-overlapping is %s\n",
+ pwm_type[pdata->pwm_type],
+ pdata->non_overlap_enable?"enabled":"disabled");
+
+ return 0;
+}
+
+static struct regmap *atmel_classd_codec_get_remap(struct device *dev)
+{
+ return dev_get_regmap(dev, NULL);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_classd = {
+ .probe = atmel_classd_codec_probe,
+ .controls = atmel_classd_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
+ .get_regmap = atmel_classd_codec_get_remap,
+};
+
+/* codec dai component */
+static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = clk_prepare_enable(dd->aclk);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(dd->gclk);
+}
+
+static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
+ int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
+
+ if (mute)
+ val = mask;
+ else
+ val = 0;
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
+
+static struct {
+ int rate;
+ int sample_rate;
+ int dsp_clk;
+ unsigned long aclk_rate;
+} const sample_rates[] = {
+ { 8000, CLASSD_INTPMR_FRAME_8K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 16000, CLASSD_INTPMR_FRAME_16K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 32000, CLASSD_INTPMR_FRAME_32K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 48000, CLASSD_INTPMR_FRAME_48K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 96000, CLASSD_INTPMR_FRAME_96K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 22050, CLASSD_INTPMR_FRAME_22K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ { 44100, CLASSD_INTPMR_FRAME_44K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ { 88200, CLASSD_INTPMR_FRAME_88K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+};
+
+static int
+atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int fs;
+ int i, best, best_val, cur_val, ret;
+ u32 mask, val;
+
+ fs = params_rate(params);
+
+ best = 0;
+ best_val = abs(fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+
+ dev_dbg(codec->dev,
+ "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
+ sample_rates[best].rate, sample_rates[best].aclk_rate);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->aclk);
+
+ ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate);
+ if (ret)
+ return ret;
+
+ mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
+ val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
+ | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
+
+ snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
+
+ ret = clk_prepare_enable(dd->aclk);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(dd->gclk);
+}
+
+static void
+atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->aclk);
+}
+
+static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ snd_soc_update_bits(codec, CLASSD_MR,
+ CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
+ (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
+
+ return 0;
+}
+
+static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = {
+ .digital_mute = atmel_classd_codec_dai_digital_mute,
+ .startup = atmel_classd_codec_dai_startup,
+ .shutdown = atmel_classd_codec_dai_shutdown,
+ .hw_params = atmel_classd_codec_dai_hw_params,
+ .prepare = atmel_classd_codec_dai_prepare,
+ .trigger = atmel_classd_codec_dai_trigger,
+};
+
+#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
+
+static struct snd_soc_dai_driver atmel_classd_codec_dai = {
+ .name = ATMEL_CLASSD_CODEC_DAI_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ATMEL_CLASSD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &atmel_classd_codec_dai_ops,
+};
+
+/* ASoC sound card */
+static int atmel_classd_asoc_card_init(struct device *dev,
+ struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+
+ dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
+ if (!dai_link)
+ return -ENOMEM;
+
+ dai_link->name = "CLASSD";
+ dai_link->stream_name = "CLASSD PCM";
+ dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME;
+ dai_link->cpu_dai_name = dev_name(dev);
+ dai_link->codec_name = dev_name(dev);
+ dai_link->platform_name = dev_name(dev);
+
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->name = dd->pdata->card_name;
+ card->dev = dev;
+
+ return 0;
+};
+
+/* regmap configuration */
+static const struct reg_default atmel_classd_reg_defaults[] = {
+ { CLASSD_INTPMR, 0x00301212 },
+};
+
+#define ATMEL_CLASSD_REG_MAX 0xE4
+static const struct regmap_config atmel_classd_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_CLASSD_REG_MAX,
+
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = atmel_classd_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults),
+};
+
+static int atmel_classd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_classd *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ const struct atmel_classd_pdata *pdata;
+ struct snd_soc_card *card;
+ int ret;
+
+ pdata = dev_get_platdata(dev);
+ if (!pdata) {
+ pdata = atmel_classd_dt_init(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->pdata = pdata;
+
+ dd->irq = platform_get_irq(pdev, 0);
+ if (dd->irq < 0) {
+ ret = dd->irq;
+ dev_err(dev, "failed to could not get irq: %d\n", ret);
+ return ret;
+ }
+
+ dd->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dd->pclk)) {
+ ret = PTR_ERR(dd->pclk);
+ dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(dd->gclk)) {
+ ret = PTR_ERR(dd->gclk);
+ dev_err(dev, "failed to get GCK clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(dd->aclk)) {
+ ret = PTR_ERR(dd->aclk);
+ dev_err(dev, "failed to get audio clock: %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no memory resource\n");
+ return -ENXIO;
+ }
+
+ io_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(io_base)) {
+ ret = PTR_ERR(io_base);
+ dev_err(dev, "failed to remap register memory: %d\n", ret);
+ return ret;
+ }
+
+ dd->phy_base = res->start;
+
+ dd->regmap = devm_regmap_init_mmio(dev, io_base,
+ &atmel_classd_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ ret = PTR_ERR(dd->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev,
+ &atmel_classd_cpu_dai_component,
+ &atmel_classd_cpu_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(dev,
+ &atmel_classd_dmaengine_pcm_config,
+ 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_classd,
+ &atmel_classd_codec_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register codec: %d\n", ret);
+ return ret;
+ }
+
+ /* register sound card */
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, dd);
+ platform_set_drvdata(pdev, card);
+
+ ret = atmel_classd_asoc_card_init(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to init sound card\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to register sound card: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int atmel_classd_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver atmel_classd_driver = {
+ .driver = {
+ .name = "atmel-classd",
+ .of_match_table = of_match_ptr(atmel_classd_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = atmel_classd_probe,
+ .remove = atmel_classd_remove,
+};
+module_platform_driver(atmel_classd_driver);
+
+MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h
new file mode 100644
index 000000000000..73f8fdd1ca83
--- /dev/null
+++ b/sound/soc/atmel/atmel-classd.h
@@ -0,0 +1,120 @@
+#ifndef __ATMEL_CLASSD_H_
+#define __ATMEL_CLASSD_H_
+
+#define CLASSD_CR 0x00000000
+#define CLASSD_CR_RESET 0x1
+
+#define CLASSD_MR 0x00000004
+
+#define CLASSD_MR_LEN_DIS 0x0
+#define CLASSD_MR_LEN_EN 0x1
+#define CLASSD_MR_LEN_MASK (0x1 << 0)
+#define CLASSD_MR_LEN_SHIFT (0)
+
+#define CLASSD_MR_LMUTE_DIS 0x0
+#define CLASSD_MR_LMUTE_EN 0x1
+#define CLASSD_MR_LMUTE_SHIFT (0x1)
+#define CLASSD_MR_LMUTE_MASK (0x1 << 1)
+
+#define CLASSD_MR_REN_DIS 0x0
+#define CLASSD_MR_REN_EN 0x1
+#define CLASSD_MR_REN_MASK (0x1 << 4)
+#define CLASSD_MR_REN_SHIFT (4)
+
+#define CLASSD_MR_RMUTE_DIS 0x0
+#define CLASSD_MR_RMUTE_EN 0x1
+#define CLASSD_MR_RMUTE_SHIFT (0x5)
+#define CLASSD_MR_RMUTE_MASK (0x1 << 5)
+
+#define CLASSD_MR_PWMTYP_SINGLE 0x0
+#define CLASSD_MR_PWMTYP_DIFF 0x1
+#define CLASSD_MR_PWMTYP_MASK (0x1 << 8)
+#define CLASSD_MR_PWMTYP_SHIFT (8)
+
+#define CLASSD_MR_NON_OVERLAP_DIS 0x0
+#define CLASSD_MR_NON_OVERLAP_EN 0x1
+#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16)
+#define CLASSD_MR_NON_OVERLAP_SHIFT (16)
+
+#define CLASSD_MR_NOVR_VAL_5NS 0x0
+#define CLASSD_MR_NOVR_VAL_10NS 0x1
+#define CLASSD_MR_NOVR_VAL_15NS 0x2
+#define CLASSD_MR_NOVR_VAL_20NS 0x3
+#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20)
+#define CLASSD_MR_NOVR_VAL_SHIFT (20)
+
+#define CLASSD_INTPMR 0x00000008
+
+#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0)
+#define CLASSD_INTPMR_ATTL_SHIFT (0)
+#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8)
+#define CLASSD_INTPMR_ATTR_SHIFT (8)
+
+#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0
+#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1
+#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16)
+#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16)
+
+#define CLASSD_INTPMR_DEEMP_DIS 0x0
+#define CLASSD_INTPMR_DEEMP_EN 0x1
+#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18)
+#define CLASSD_INTPMR_DEEMP_SHIFT (18)
+
+#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0
+#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1
+#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19)
+#define CLASSD_INTPMR_SWAP_SHIFT (19)
+
+#define CLASSD_INTPMR_FRAME_8K 0x0
+#define CLASSD_INTPMR_FRAME_16K 0x1
+#define CLASSD_INTPMR_FRAME_32K 0x2
+#define CLASSD_INTPMR_FRAME_48K 0x3
+#define CLASSD_INTPMR_FRAME_96K 0x4
+#define CLASSD_INTPMR_FRAME_22K 0x5
+#define CLASSD_INTPMR_FRAME_44K 0x6
+#define CLASSD_INTPMR_FRAME_88K 0x7
+#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20)
+#define CLASSD_INTPMR_FRAME_SHIFT (20)
+
+#define CLASSD_INTPMR_EQCFG_FLAT 0x0
+#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1
+#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2
+#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3
+#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4
+#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5
+#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6
+#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7
+#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8
+#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9
+#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa
+#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb
+#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc
+#define CLASSD_INTPMR_EQCFG_SHIFT (24)
+
+#define CLASSD_INTPMR_MONO_DIS 0x0
+#define CLASSD_INTPMR_MONO_EN 0x1
+#define CLASSD_INTPMR_MONO_MASK (0x1 << 28)
+#define CLASSD_INTPMR_MONO_SHIFT (28)
+
+#define CLASSD_INTPMR_MONO_MODE_MIX 0x0
+#define CLASSD_INTPMR_MONO_MODE_SAT 0x1
+#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2
+#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3
+#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29)
+#define CLASSD_INTPMR_MONO_MODE_SHIFT (29)
+
+#define CLASSD_INTSR 0x0000000c
+
+#define CLASSD_THR 0x00000010
+
+#define CLASSD_IER 0x00000014
+
+#define CLASSD_IDR 0x00000018
+
+#define CLASSD_IMR 0x0000001c
+
+#define CLASSD_ISR 0x00000020
+
+#define CLASSD_WPMR 0x000000e4
+
+#endif
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index aa354e1c6ff7..1933bcd46cca 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
{ .compatible = "atmel,asoc-wm8904", },
{ }
};
+MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids);
#endif
static struct platform_driver atmel_asoc_wm8904_driver = {
diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c
index 452f404abfd2..e97c32798e98 100644
--- a/sound/soc/au1x/db1000.c
+++ b/sound/soc/au1x/db1000.c
@@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &db1000_ac97;
card->dev = &pdev->dev;
- return snd_soc_register_card(card);
-}
-
-static int db1000_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- snd_soc_unregister_card(card);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver db1000_audio_driver = {
@@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = db1000_audio_probe,
- .remove = db1000_audio_remove,
};
module_platform_driver(db1000_audio_driver);
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index 8c907ebea189..5c73061d912a 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -178,14 +178,7 @@ static int db1200_audio_probe(struct platform_device *pdev)
card = db1200_cards[pid->driver_data];
card->dev = &pdev->dev;
- return snd_soc_register_card(card);
-}
-
-static int db1200_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- snd_soc_unregister_card(card);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver db1200_audio_driver = {
@@ -195,7 +188,6 @@ static struct platform_driver db1200_audio_driver = {
},
.id_table = db1200_pids,
.probe = db1200_audio_probe,
- .remove = db1200_audio_remove,
};
module_platform_driver(db1200_audio_driver);
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index 5bf1501e5e3c..864df2616e10 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -87,27 +87,18 @@ static int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
-static int bf5xx_ad1836_driver_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver bf5xx_ad1836_driver = {
.driver = {
.name = "bfin-snd-ad1836",
.pm = &snd_soc_pm_ops,
},
.probe = bf5xx_ad1836_driver_probe,
- .remove = bf5xx_ad1836_driver_remove,
};
module_platform_driver(bf5xx_ad1836_driver);
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
index 523baf5820d7..72ac78988426 100644
--- a/sound/soc/blackfin/bfin-eval-adau1373.c
+++ b/sound/soc/blackfin/bfin-eval-adau1373.c
@@ -154,16 +154,7 @@ static int bfin_eval_adau1373_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adau1373);
-}
-
-static int bfin_eval_adau1373_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373);
}
static struct platform_driver bfin_eval_adau1373_driver = {
@@ -172,7 +163,6 @@ static struct platform_driver bfin_eval_adau1373_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1373_probe,
- .remove = bfin_eval_adau1373_remove,
};
module_platform_driver(bfin_eval_adau1373_driver);
diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c
index f9e926dfd4ef..5c67f72cf9a9 100644
--- a/sound/soc/blackfin/bfin-eval-adau1701.c
+++ b/sound/soc/blackfin/bfin-eval-adau1701.c
@@ -94,16 +94,7 @@ static int bfin_eval_adau1701_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adau1701);
-}
-
-static int bfin_eval_adau1701_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701);
}
static struct platform_driver bfin_eval_adau1701_driver = {
@@ -112,7 +103,6 @@ static struct platform_driver bfin_eval_adau1701_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1701_probe,
- .remove = bfin_eval_adau1701_remove,
};
module_platform_driver(bfin_eval_adau1701_driver);
diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c
index 27eee66afdb2..1037477d10b2 100644
--- a/sound/soc/blackfin/bfin-eval-adav80x.c
+++ b/sound/soc/blackfin/bfin-eval-adav80x.c
@@ -119,16 +119,7 @@ static int bfin_eval_adav80x_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adav80x);
-}
-
-static int bfin_eval_adav80x_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x);
}
static const struct platform_device_id bfin_eval_adav80x_ids[] = {
@@ -144,7 +135,6 @@ static struct platform_driver bfin_eval_adav80x_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adav80x_probe,
- .remove = bfin_eval_adav80x_remove,
.id_table = bfin_eval_adav80x_ids,
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0c9733ecd17f..cfdafc4c11ea 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4554
+ select SND_SOC_AK4613 if I2C
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
@@ -57,6 +58,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
+ select SND_SOC_DA7219 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
select SND_SOC_DMIC
@@ -79,7 +81,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
- select SND_SOC_HDMI_CODEC
+ select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
@@ -171,6 +173,7 @@ config SND_SOC_ALL_CODECS
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_WM8998 if MFD_WM8998
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -195,9 +198,11 @@ config SND_SOC_ARIZONA
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM8997=y
+ default y if SND_SOC_WM8998=y
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM8997=m
+ default m if SND_SOC_WM8998=m
config SND_SOC_WM_HUBS
tristate
@@ -319,6 +324,10 @@ config SND_SOC_AK4535
config SND_SOC_AK4554
tristate "AKM AK4554 CODEC"
+config SND_SOC_AK4613
+ tristate "AKM AK4613 CODEC"
+ depends on I2C
+
config SND_SOC_AK4641
tristate
@@ -430,6 +439,9 @@ config SND_SOC_DA7210
config SND_SOC_DA7213
tristate
+config SND_SOC_DA7219
+ tristate
+
config SND_SOC_DA732X
tristate
@@ -442,9 +454,6 @@ config SND_SOC_BT_SCO
config SND_SOC_DMIC
tristate
-config SND_SOC_HDMI_CODEC
- tristate "HDMI stub CODEC"
-
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
@@ -865,6 +874,9 @@ config SND_SOC_WM8996
config SND_SOC_WM8997
tristate
+config SND_SOC_WM8998
+ tristate
+
config SND_SOC_WM9081
tristate
@@ -896,6 +908,9 @@ config SND_SOC_MC13783
config SND_SOC_ML26124
tristate
+config SND_SOC_NAU8825
+ tristate
+
config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 4a32077954ae..f632fc42f59f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -26,6 +26,7 @@ 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-ak4613-objs := ak4613.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
@@ -49,6 +50,7 @@ snd-soc-cs4349-objs := cs4349.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
+snd-soc-da7219-objs := da7219.o da7219-aad.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
snd-soc-bt-sco-objs := bt-sco.o
@@ -72,7 +74,7 @@ snd-soc-max98925-objs := max98925.o
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-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
@@ -176,6 +178,7 @@ 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-wm8998-objs := wm8998.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
@@ -216,6 +219,7 @@ 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_AK4613) += snd-soc-ak4613.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
@@ -241,6 +245,7 @@ obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
+obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
@@ -264,7 +269,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
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_NAU8825) += snd-soc-nau8825.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
@@ -364,6 +369,7 @@ 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_WM8998) += snd-soc-wm8998.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/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index df3a1a415825..171313664bc8 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -15,8 +15,8 @@
#include "ad193x.h"
static const struct i2c_device_id ad193x_id[] = {
- { "ad1936", 0 },
- { "ad1937", 0 },
+ { "ad1936", AD193X },
+ { "ad1937", AD193X },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
@@ -30,7 +30,9 @@ static int ad193x_i2c_probe(struct i2c_client *client,
config.val_bits = 8;
config.reg_bits = 8;
- return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
+ return ad193x_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ (enum ad193x_type)id->driver_data);
}
static int ad193x_i2c_remove(struct i2c_client *client)
diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c
index 390cef9b9dc2..431f95da1de1 100644
--- a/sound/soc/codecs/ad193x-spi.c
+++ b/sound/soc/codecs/ad193x-spi.c
@@ -16,6 +16,7 @@
static int ad193x_spi_probe(struct spi_device *spi)
{
+ const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
config = ad193x_regmap_config;
@@ -24,7 +25,8 @@ static int ad193x_spi_probe(struct spi_device *spi)
config.read_flag_mask = 0x09;
config.write_flag_mask = 0x08;
- return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+ return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config),
+ (enum ad193x_type)id->driver_data);
}
static int ad193x_spi_remove(struct spi_device *spi)
@@ -33,6 +35,17 @@ static int ad193x_spi_remove(struct spi_device *spi)
return 0;
}
+static const struct spi_device_id ad193x_spi_id[] = {
+ { "ad193x", AD193X },
+ { "ad1933", AD1933 },
+ { "ad1934", AD1934 },
+ { "ad1938", AD193X },
+ { "ad1939", AD193X },
+ { "adau1328", AD193X },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad193x_spi_id);
+
static struct spi_driver ad193x_spi_driver = {
.driver = {
.name = "ad193x",
@@ -40,6 +53,7 @@ static struct spi_driver ad193x_spi_driver = {
},
.probe = ad193x_spi_probe,
.remove = ad193x_spi_remove,
+ .id_table = ad193x_spi_id,
};
module_spi_driver(ad193x_spi_driver);
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 17c953595660..3a3f3f2343d7 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -23,6 +23,7 @@
/* codec private data */
struct ad193x_priv {
struct regmap *regmap;
+ enum ad193x_type type;
int sysclk;
};
@@ -47,12 +48,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
- /* ADC switch control */
- SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
- AD193X_ADCR1_MUTE, 1, 1),
- SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
- AD193X_ADCR2_MUTE, 1, 1),
-
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
AD193X_DACR1_MUTE, 1, 1),
@@ -63,26 +58,37 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
AD193X_DACR4_MUTE, 1, 1),
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
+};
+
+static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = {
+ /* ADC switch control */
+ SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
+ AD193X_ADCR1_MUTE, 1, 1),
+ SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
+ AD193X_ADCR2_MUTE, 1, 1),
+
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
AD193X_ADC_HIGHPASS_FILTER, 1, 0),
-
- /* DAC de-emphasis */
- SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
};
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
SND_SOC_DAPM_VMID("VMID"),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
@@ -91,18 +97,33 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "SYSCLK" },
{ "DAC Output", NULL, "DAC" },
{ "DAC Output", NULL, "VMID" },
- { "ADC", NULL, "SYSCLK" },
- { "DAC", NULL, "ADC_PWR" },
- { "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", NULL, "DAC Output" },
{ "DAC2OUT", NULL, "DAC Output" },
{ "DAC3OUT", NULL, "DAC Output" },
{ "DAC4OUT", NULL, "DAC Output" },
+ { "SYSCLK", NULL, "PLL_PWR" },
+};
+
+static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
+ { "ADC", NULL, "SYSCLK" },
+ { "ADC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
- { "SYSCLK", NULL, "PLL_PWR" },
};
+static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
+{
+ switch (ad193x->type) {
+ case AD1933:
+ case AD1934:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
/*
* DAI ops entries
*/
@@ -147,8 +168,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
- AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_CHAN_MASK,
+ channels << AD193X_ADC_CHAN_SHFT);
return 0;
}
@@ -172,7 +195,9 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
adc_serfmt |= AD193X_ADC_SERFMT_AUX;
break;
default:
- return -EINVAL;
+ if (ad193x_has_adc(ad193x))
+ return -EINVAL;
+ break;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -217,10 +242,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
- AD193X_ADC_SERFMT_MASK, adc_serfmt);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
- AD193X_ADC_FMT_MASK, adc_fmt);
+ if (ad193x_has_adc(ad193x)) {
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_SERFMT_MASK, adc_serfmt);
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_FMT_MASK, adc_fmt);
+ }
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_FMT_MASK, dac_fmt);
@@ -287,8 +314,9 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
AD193X_DAC_WORD_LEN_MASK,
word_len << AD193X_DAC_WORD_LEN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
- AD193X_ADC_WORD_LEN_MASK, word_len);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_WORD_LEN_MASK, word_len);
return 0;
}
@@ -326,6 +354,8 @@ static struct snd_soc_dai_driver ad193x_dai = {
static int ad193x_codec_probe(struct snd_soc_codec *codec)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int num, ret;
/* default setting for ad193x */
@@ -335,14 +365,46 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec)
regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
/* dac in tdm mode */
regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40);
- /* high-pass filter enable */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
- /* sata delay=1, adc aux mode */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+
+ /* adc only */
+ if (ad193x_has_adc(ad193x)) {
+ /* high-pass filter enable */
+ regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
+ /* sata delay=1, adc aux mode */
+ regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+ }
+
/* pll input: mclki/xi */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
+ /* adc only */
+ if (ad193x_has_adc(ad193x)) {
+ /* add adc controls */
+ num = ARRAY_SIZE(ad193x_adc_snd_controls);
+ ret = snd_soc_add_codec_controls(codec,
+ ad193x_adc_snd_controls,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc widgets */
+ num = ARRAY_SIZE(ad193x_adc_widgets);
+ ret = snd_soc_dapm_new_controls(dapm,
+ ad193x_adc_widgets,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc routes */
+ num = ARRAY_SIZE(ad193x_adc_audio_paths);
+ ret = snd_soc_dapm_add_routes(dapm,
+ ad193x_adc_audio_paths,
+ num);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -356,18 +418,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
.num_dapm_routes = ARRAY_SIZE(audio_paths),
};
-static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
const struct regmap_config ad193x_regmap_config = {
.max_register = AD193X_NUM_REGS - 1,
- .volatile_reg = adau193x_reg_volatile,
};
EXPORT_SYMBOL_GPL(ad193x_regmap_config);
-int ad193x_probe(struct device *dev, struct regmap *regmap)
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type)
{
struct ad193x_priv *ad193x;
@@ -379,6 +436,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
ad193x->regmap = regmap;
+ ad193x->type = type;
dev_set_drvdata(dev, ad193x);
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index ab9a998f15be..8b1e65f928d2 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -13,8 +13,15 @@
struct device;
+enum ad193x_type {
+ AD193X,
+ AD1933,
+ AD1934,
+};
+
extern const struct regmap_config ad193x_regmap_config;
-int ad193x_probe(struct device *dev, struct regmap *regmap);
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type);
#define AD193X_PLL_CLK_CTRL0 0x00
#define AD193X_PLL_POWERDOWN 0x01
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 198c924551b7..acff8d62059c 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -728,8 +728,8 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
return 0;
- return snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, adav80x->rate);
}
static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
new file mode 100644
index 000000000000..07a266460ec3
--- /dev/null
+++ b/sound/soc/codecs/ak4613.c
@@ -0,0 +1,497 @@
+/*
+ * ak4613.c -- Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on ak4642.c by Kuninori Morimoto
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * 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/clk.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1 0x00 /* Power Management 1 */
+#define PW_MGMT2 0x01 /* Power Management 2 */
+#define PW_MGMT3 0x02 /* Power Management 3 */
+#define CTRL1 0x03 /* Control 1 */
+#define CTRL2 0x04 /* Control 2 */
+#define DEMP1 0x05 /* De-emphasis1 */
+#define DEMP2 0x06 /* De-emphasis2 */
+#define OFD 0x07 /* Overflow Detect */
+#define ZRD 0x08 /* Zero Detect */
+#define ICTRL 0x09 /* Input Control */
+#define OCTRL 0x0a /* Output Control */
+#define LOUT1 0x0b /* LOUT1 Volume Control */
+#define ROUT1 0x0c /* ROUT1 Volume Control */
+#define LOUT2 0x0d /* LOUT2 Volume Control */
+#define ROUT2 0x0e /* ROUT2 Volume Control */
+#define LOUT3 0x0f /* LOUT3 Volume Control */
+#define ROUT3 0x10 /* ROUT3 Volume Control */
+#define LOUT4 0x11 /* LOUT4 Volume Control */
+#define ROUT4 0x12 /* ROUT4 Volume Control */
+#define LOUT5 0x13 /* LOUT5 Volume Control */
+#define ROUT5 0x14 /* ROUT5 Volume Control */
+#define LOUT6 0x15 /* LOUT6 Volume Control */
+#define ROUT6 0x16 /* ROUT6 Volume Control */
+
+/* PW_MGMT1 */
+#define RSTN BIT(0)
+#define PMDAC BIT(1)
+#define PMADC BIT(2)
+#define PMVR BIT(3)
+
+/* PW_MGMT2 */
+#define PMAD_ALL 0x7
+
+/* PW_MGMT3 */
+#define PMDA_ALL 0x3f
+
+/* CTRL1 */
+#define DIF0 BIT(3)
+#define DIF1 BIT(4)
+#define DIF2 BIT(5)
+#define TDM0 BIT(6)
+#define TDM1 BIT(7)
+#define NO_FMT (0xff)
+#define FMT_MASK (0xf8)
+
+/* CTRL2 */
+#define DFS_NORMAL_SPEED (0 << 2)
+#define DFS_DOUBLE_SPEED (1 << 2)
+#define DFS_QUAD_SPEED (2 << 2)
+
+struct ak4613_priv {
+ struct mutex lock;
+
+ unsigned int fmt;
+ u8 fmt_ctrl;
+ int cnt;
+};
+
+struct ak4613_formats {
+ unsigned int width;
+ unsigned int fmt;
+};
+
+struct ak4613_interface {
+ struct ak4613_formats capture;
+ struct ak4613_formats playback;
+};
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -127.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new ak4613_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6,
+ 0, 0xFF, 1, out_tlv),
+};
+
+static const struct reg_default ak4613_reg[] = {
+ { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 },
+ { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 },
+ { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 },
+ { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 },
+ { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 },
+ { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+};
+
+#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
+#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+static const struct ak4613_interface ak4613_iface[] = {
+ /* capture */ /* playback */
+ [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) },
+ [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) },
+ [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) },
+ [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) },
+ [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) },
+};
+
+static const struct regmap_config ak4613_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x16,
+ .reg_defaults = ak4613_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4613_reg),
+};
+
+static const struct of_device_id ak4613_of_match[] = {
+ { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4613_of_match);
+
+static const struct i2c_device_id ak4613_i2c_id[] = {
+ { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
+
+static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = {
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("LOUT4"),
+ SND_SOC_DAPM_OUTPUT("LOUT5"),
+ SND_SOC_DAPM_OUTPUT("LOUT6"),
+
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT3"),
+ SND_SOC_DAPM_OUTPUT("ROUT4"),
+ SND_SOC_DAPM_OUTPUT("ROUT5"),
+ SND_SOC_DAPM_OUTPUT("ROUT6"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0),
+ SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0),
+ SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0),
+ SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0),
+ SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ak4613_intercon[] = {
+ {"LOUT1", NULL, "DAC1"},
+ {"LOUT2", NULL, "DAC2"},
+ {"LOUT3", NULL, "DAC3"},
+ {"LOUT4", NULL, "DAC4"},
+ {"LOUT5", NULL, "DAC5"},
+ {"LOUT6", NULL, "DAC6"},
+
+ {"ROUT1", NULL, "DAC1"},
+ {"ROUT2", NULL, "DAC2"},
+ {"ROUT3", NULL, "DAC3"},
+ {"ROUT4", NULL, "DAC4"},
+ {"ROUT5", NULL, "DAC5"},
+ {"ROUT6", NULL, "DAC6"},
+
+ {"DAC1", NULL, "Playback"},
+ {"DAC2", NULL, "Playback"},
+ {"DAC3", NULL, "Playback"},
+ {"DAC4", NULL, "Playback"},
+ {"DAC5", NULL, "Playback"},
+ {"DAC6", NULL, "Playback"},
+
+ {"Capture", NULL, "ADC1"},
+ {"Capture", NULL, "ADC2"},
+
+ {"ADC1", NULL, "LIN1"},
+ {"ADC2", NULL, "LIN2"},
+
+ {"ADC1", NULL, "RIN1"},
+ {"ADC2", NULL, "RIN2"},
+};
+
+static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct device *dev = codec->dev;
+
+ mutex_lock(&priv->lock);
+ priv->cnt--;
+ if (priv->cnt < 0) {
+ dev_err(dev, "unexpected counter error\n");
+ priv->cnt = 0;
+ }
+ if (!priv->cnt)
+ priv->fmt_ctrl = NO_FMT;
+ mutex_unlock(&priv->lock);
+}
+
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+
+ switch (fmt) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ priv->fmt = fmt;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ const struct ak4613_formats *fmts;
+ struct device *dev = codec->dev;
+ unsigned int width = params_width(params);
+ unsigned int fmt = priv->fmt;
+ unsigned int rate;
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int i, ret;
+ u8 fmt_ctrl, ctrl2;
+
+ rate = params_rate(params);
+ switch (rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ ctrl2 = DFS_NORMAL_SPEED;
+ break;
+ case 88200:
+ case 96000:
+ ctrl2 = DFS_DOUBLE_SPEED;
+ break;
+ case 176400:
+ case 192000:
+ ctrl2 = DFS_QUAD_SPEED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * FIXME
+ *
+ * It doesn't support TDM at this point
+ */
+ fmt_ctrl = NO_FMT;
+ for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
+ fmts = (is_play) ? &ak4613_iface[i].playback :
+ &ak4613_iface[i].capture;
+
+ if (fmts->fmt != fmt)
+ continue;
+
+ if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
+ if (fmts->width != width)
+ continue;
+ } else {
+ if (fmts->width < width)
+ continue;
+ }
+
+ fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
+ break;
+ }
+
+ ret = -EINVAL;
+ if (fmt_ctrl == NO_FMT)
+ goto hw_params_end;
+
+ mutex_lock(&priv->lock);
+ if ((priv->fmt_ctrl == NO_FMT) ||
+ (priv->fmt_ctrl == fmt_ctrl)) {
+ priv->fmt_ctrl = fmt_ctrl;
+ priv->cnt++;
+ ret = 0;
+ }
+ mutex_unlock(&priv->lock);
+
+ if (ret < 0)
+ goto hw_params_end;
+
+ snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
+ snd_soc_write(codec, CTRL2, ctrl2);
+
+hw_params_end:
+ if (ret < 0)
+ dev_warn(dev, "unsupported data width/format combination\n");
+
+ return ret;
+}
+
+static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 mgmt1 = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ mgmt1 |= RSTN;
+ /* fall through */
+ case SND_SOC_BIAS_PREPARE:
+ mgmt1 |= PMADC | PMDAC;
+ /* fall through */
+ case SND_SOC_BIAS_STANDBY:
+ mgmt1 |= PMVR;
+ /* fall through */
+ case SND_SOC_BIAS_OFF:
+ default:
+ break;
+ }
+
+ snd_soc_write(codec, PW_MGMT1, mgmt1);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak4613_dai_ops = {
+ .shutdown = ak4613_dai_shutdown,
+ .set_fmt = ak4613_dai_set_fmt,
+ .hw_params = ak4613_dai_hw_params,
+};
+
+#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_64000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4613_dai = {
+ .name = "ak4613-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .ops = &ak4613_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_mark_dirty(regmap);
+ return regcache_sync(regmap);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+ .resume = ak4613_resume,
+ .set_bias_level = ak4613_set_bias_level,
+ .controls = ak4613_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4613_snd_controls),
+ .dapm_widgets = ak4613_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets),
+ .dapm_routes = ak4613_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
+};
+
+static int ak4613_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
+ const struct regmap_config *regmap_cfg;
+ struct regmap *regmap;
+ struct ak4613_priv *priv;
+
+ regmap_cfg = NULL;
+ if (np) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(ak4613_of_match, dev);
+ if (of_id)
+ regmap_cfg = of_id->data;
+ } else {
+ regmap_cfg = (const struct regmap_config *)id->driver_data;
+ }
+
+ if (!regmap_cfg)
+ return -EINVAL;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->fmt_ctrl = NO_FMT;
+ priv->cnt = 0;
+
+ mutex_init(&priv->lock);
+
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+ &ak4613_dai, 1);
+}
+
+static int ak4613_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static struct i2c_driver ak4613_i2c_driver = {
+ .driver = {
+ .name = "ak4613-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = ak4613_of_match,
+ },
+ .probe = ak4613_i2c_probe,
+ .remove = ak4613_i2c_remove,
+ .id_table = ak4613_i2c_id,
+};
+
+module_i2c_driver(ak4613_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4613 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 4a90143d0e90..cda27c22812a 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -23,6 +23,8 @@
* AK4648 is tested.
*/
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
@@ -128,11 +130,8 @@
#define I2S (3 << 0)
/* MD_CTL2 */
-#define FS0 (1 << 0)
-#define FS1 (1 << 1)
-#define FS2 (1 << 2)
-#define FS3 (1 << 5)
-#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val) ((val & 0x3) << 6)
/* MD_CTL3 */
#define BST1 (1 << 3)
@@ -147,6 +146,7 @@ struct ak4642_drvdata {
struct ak4642_priv {
const struct ak4642_drvdata *drvdata;
+ struct clk *mcko;
};
/*
@@ -430,56 +430,56 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+static int ak4642_set_mcko(struct snd_soc_codec *codec,
+ u32 frequency)
+{
+ u32 fs_list[] = {
+ [0] = 8000,
+ [1] = 12000,
+ [2] = 16000,
+ [3] = 24000,
+ [4] = 7350,
+ [5] = 11025,
+ [6] = 14700,
+ [7] = 22050,
+ [10] = 32000,
+ [11] = 48000,
+ [14] = 29400,
+ [15] = 44100,
+ };
+ u32 ps_list[] = {
+ [0] = 256,
+ [1] = 128,
+ [2] = 64,
+ [3] = 32
+ };
+ int ps, fs;
+
+ for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+ for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+ if (frequency == ps_list[ps] * fs_list[fs]) {
+ snd_soc_write(codec, MD_CTL2,
+ PSs(ps) | FSs(fs));
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- u8 rate;
+ struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ u32 rate = clk_get_rate(priv->mcko);
- switch (params_rate(params)) {
- case 7350:
- rate = FS2;
- break;
- case 8000:
- rate = 0;
- break;
- case 11025:
- rate = FS2 | FS0;
- break;
- case 12000:
- rate = FS0;
- break;
- case 14700:
- rate = FS2 | FS1;
- break;
- case 16000:
- rate = FS1;
- break;
- case 22050:
- rate = FS2 | FS1 | FS0;
- break;
- case 24000:
- rate = FS1 | FS0;
- break;
- case 29400:
- rate = FS3 | FS2 | FS1;
- break;
- case 32000:
- rate = FS3 | FS1;
- break;
- case 44100:
- rate = FS3 | FS2 | FS1 | FS0;
- break;
- case 48000:
- rate = FS3 | FS1 | FS0;
- break;
- default:
- return -EINVAL;
- }
- snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+ if (!rate)
+ rate = params_rate(params) * 256;
- return 0;
+ return ak4642_set_mcko(codec, rate);
}
static int ak4642_set_bias_level(struct snd_soc_codec *codec,
@@ -532,7 +532,18 @@ static int ak4642_resume(struct snd_soc_codec *codec)
return 0;
}
+static int ak4642_probe(struct snd_soc_codec *codec)
+{
+ struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (priv->mcko)
+ ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+
+ return 0;
+}
+
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+ .probe = ak4642_probe,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
@@ -580,19 +591,54 @@ static const struct ak4642_drvdata ak4648_drvdata = {
.extended_frequencies = 1,
};
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk *clk;
+ const char *clk_name = np->name;
+ const char *parent_clk_name = NULL;
+ u32 rate;
+
+ if (of_property_read_u32(np, "clock-frequency", &rate))
+ return NULL;
+
+ if (of_property_read_bool(np, "clocks"))
+ parent_clk_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
+ (parent_clk_name) ? 0 : CLK_IS_ROOT,
+ rate);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+ return clk;
+}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
+
static const struct of_device_id ak4642_of_match[];
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct device_node *np = i2c->dev.of_node;
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
const struct ak4642_drvdata *drvdata = NULL;
struct regmap *regmap;
struct ak4642_priv *priv;
+ struct clk *mcko = NULL;
if (np) {
const struct of_device_id *of_id;
- of_id = of_match_device(ak4642_of_match, &i2c->dev);
+ mcko = ak4642_of_parse_mcko(dev);
+ if (IS_ERR(mcko))
+ mcko = NULL;
+
+ of_id = of_match_device(ak4642_of_match, dev);
if (of_id)
drvdata = of_id->data;
} else {
@@ -600,15 +646,16 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
}
if (!drvdata) {
- dev_err(&i2c->dev, "Unknown device type\n");
+ dev_err(dev, "Unknown device type\n");
return -EINVAL;
}
- priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->drvdata = drvdata;
+ priv->mcko = mcko;
i2c_set_clientdata(i2c, priv);
@@ -616,7 +663,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(&i2c->dev,
+ return snd_soc_register_codec(dev,
&soc_codec_dev_ak4642, &ak4642_dai, 1);
}
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 8a2221ab3d10..9929efc6b9aa 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
0x4f5, 0x0da);
}
break;
+ default:
+ break;
}
return 0;
@@ -314,6 +316,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"Tone Generator 2",
"Haptics",
"AEC",
+ "AEC2",
"Mic Mute Mixer",
"Noise Generator",
"IN1L",
@@ -421,6 +424,7 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
0x05,
0x06, /* Haptics */
0x08, /* AEC */
+ 0x09, /* AEC2 */
0x0c, /* Noise mixer */
0x0d, /* Comfort noise */
0x10, /* IN1L */
@@ -525,6 +529,32 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values);
const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
+const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ "12kHz", "24kHz", "48kHz", "96kHz", "192kHz",
+ "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz",
+ "4kHz", "8kHz", "16kHz", "32kHz",
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_text);
+
+const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x10, 0x11, 0x12, 0x13,
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) {
+ if (arizona_sample_rate_val[i] == rate_val)
+ return arizona_sample_rate_text[i];
+ }
+
+ return "Illegal";
+}
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
+
const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
};
@@ -689,6 +719,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
ARIZONA_IN_VU, val);
}
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+{
+ unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
+ unsigned int val = snd_soc_read(codec, reg);
+
+ return !(val & ARIZONA_IN1_MODE_MASK);
+}
+EXPORT_SYMBOL_GPL(arizona_input_analog);
+
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
@@ -725,6 +764,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES);
if (reg == 0)
arizona_in_set_vu(codec, 0);
+ break;
+ default:
+ break;
}
return 0;
@@ -806,6 +848,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
break;
}
break;
+ default:
+ break;
}
return 0;
@@ -1868,6 +1912,11 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
if (fll->arizona->rev < 3 || sync)
return init_ratio;
break;
+ case WM8998:
+ case WM1814:
+ if (sync)
+ return init_ratio;
+ break;
default:
return init_ratio;
}
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index ada0a418ff4b..fea8b8ae8e1a 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -93,12 +93,17 @@ struct arizona_priv {
bool dvfs_cached;
};
-#define ARIZONA_NUM_MIXER_INPUTS 103
+#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
+#define ARIZONA_GAINMUX_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv)
+
#define ARIZONA_MIXER_CONTROLS(name, base) \
SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
@@ -209,8 +214,12 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
.num_regs = 1 }) }
#define ARIZONA_RATE_ENUM_SIZE 4
+#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
+
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
+extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
extern const struct soc_enum arizona_isrc_fsl[];
extern const struct soc_enum arizona_isrc_fsh[];
@@ -294,4 +303,7 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai);
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
bool diff);
+extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
+extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
#endif
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index a9c86efb3187..7278f93460c1 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -12,6 +12,7 @@
* option) any later version.
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -1222,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq))
+ return 0;
+
+ if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
switch (clk_id) {
case DA7213_CLKSRC_MCLK:
- if ((freq == 32768) ||
- ((freq >= 5000000) && (freq <= 54000000))) {
- da7213->mclk_rate = freq;
- return 0;
- } else {
- dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
- freq);
- return -EINVAL;
- }
+ da7213->mclk_squarer_en = false;
+ break;
+ case DA7213_CLKSRC_MCLK_SQR:
+ da7213->mclk_squarer_en = true;
break;
default:
dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
return -EINVAL;
}
+
+ da7213->clk_src = clk_id;
+
+ if (da7213->mclk) {
+ freq = clk_round_rate(da7213->mclk, freq);
+ ret = clk_set_rate(da7213->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7213->mclk_rate = freq;
+
+ return 0;
}
/* Supported PLL input frequencies are 5MHz - 54MHz. */
@@ -1366,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = {
static int da7213_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ /* MCLK */
+ if (da7213->mclk) {
+ ret = clk_prepare_enable(da7213->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+
/* Enable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN,
@@ -1382,15 +1417,127 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
/* Disable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
+
+ /* MCLK */
+ if (da7213->mclk)
+ clk_disable_unprepare(da7213->mclk);
break;
}
return 0;
}
+/* DT */
+static const struct of_device_id da7213_of_match[] = {
+ { .compatible = "dlg,da7213", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7213_of_match);
+
+static enum da7213_micbias_voltage
+ da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1600:
+ return DA7213_MICBIAS_1_6V;
+ case 2200:
+ return DA7213_MICBIAS_2_2V;
+ case 2500:
+ return DA7213_MICBIAS_2_5V;
+ case 3000:
+ return DA7213_MICBIAS_3_0V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias level\n");
+ return DA7213_MICBIAS_2_2V;
+ }
+}
+
+static enum da7213_dmic_data_sel
+ da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "lrise_rfall")) {
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ } else if (!strcmp(str, "lfall_rrise")) {
+ return DA7213_DMIC_DATA_LFALL_RRISE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC data select type\n");
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ }
+}
+
+static enum da7213_dmic_samplephase
+ da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "on_clkedge")) {
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ } else if (!strcmp(str, "between_clkedge")) {
+ return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC sample phase\n");
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ }
+}
+
+static enum da7213_dmic_clk_rate
+ da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1500000:
+ return DA7213_DMIC_CLK_1_5MHZ;
+ case 3000000:
+ return DA7213_DMIC_CLK_3_0MHZ;
+ default:
+ dev_warn(codec->dev, "Invalid DMIC clock rate\n");
+ return DA7213_DMIC_CLK_1_5MHZ;
+ }
+}
+
+static struct da7213_platform_data
+ *da7213_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct da7213_platform_data *pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_warn(codec->dev, "Failed to allocate memory for pdata\n");
+ return NULL;
+ }
+
+ if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0)
+ pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias1_lvl = DA7213_MICBIAS_2_2V;
+
+ if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0)
+ pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias2_lvl = DA7213_MICBIAS_2_2V;
+
+ if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str))
+ pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str);
+ else
+ pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL;
+
+ if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str))
+ pdata->dmic_samplephase =
+ da7213_of_dmic_samplephase(codec, of_str);
+ else
+ pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0)
+ pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32);
+ else
+ pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ;
+
+ return pdata;
+}
+
+
static int da7213_probe(struct snd_soc_codec *codec)
{
struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
- struct da7213_platform_data *pdata = da7213->pdata;
/* Default to using ALC auto offset calibration mode. */
snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
@@ -1450,8 +1597,15 @@ static int da7213_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, DA7213_LINE_CTRL,
DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE);
+ /* Handle DT/Platform data */
+ if (codec->dev->of_node)
+ da7213->pdata = da7213_of_to_pdata(codec);
+ else
+ da7213->pdata = dev_get_platdata(codec->dev);
+
/* Set platform data values */
if (da7213->pdata) {
+ struct da7213_platform_data *pdata = da7213->pdata;
u8 micbias_lvl = 0, dmic_cfg = 0;
/* Set Mic Bias voltages */
@@ -1503,10 +1657,17 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_DMIC_DATA_SEL_MASK |
DA7213_DMIC_SAMPLEPHASE_MASK |
DA7213_DMIC_CLK_RATE_MASK, dmic_cfg);
+ }
- /* Set MCLK squaring */
- da7213->mclk_squarer_en = pdata->mclk_squaring;
+ /* Check if MCLK provided */
+ da7213->mclk = devm_clk_get(codec->dev, "mclk");
+ if (IS_ERR(da7213->mclk)) {
+ if (PTR_ERR(da7213->mclk) != -ENOENT)
+ return PTR_ERR(da7213->mclk);
+ else
+ da7213->mclk = NULL;
}
+
return 0;
}
@@ -1537,7 +1698,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct da7213_priv *da7213;
- struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret;
da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv),
@@ -1545,9 +1705,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
if (!da7213)
return -ENOMEM;
- if (pdata)
- da7213->pdata = pdata;
-
i2c_set_clientdata(i2c, da7213);
da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config);
@@ -1582,6 +1739,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
static struct i2c_driver da7213_i2c_driver = {
.driver = {
.name = "da7213",
+ .of_match_table = of_match_ptr(da7213_of_match),
},
.probe = da7213_i2c_probe,
.remove = da7213_remove,
diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h
index 9cb9ddd01282..030fd691b076 100644
--- a/sound/soc/codecs/da7213.h
+++ b/sound/soc/codecs/da7213.h
@@ -13,6 +13,7 @@
#ifndef _DA7213_H
#define _DA7213_H
+#include <linux/clk.h>
#include <linux/regmap.h>
#include <sound/da7213.h>
@@ -504,14 +505,17 @@
#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
-enum clk_src {
- DA7213_CLKSRC_MCLK
+enum da7213_clk_src {
+ DA7213_CLKSRC_MCLK = 0,
+ DA7213_CLKSRC_MCLK_SQR,
};
/* Codec private data */
struct da7213_priv {
struct regmap *regmap;
+ struct clk *mclk;
unsigned int mclk_rate;
+ int clk_src;
bool master;
bool mclk_squarer_en;
bool srm_en;
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
new file mode 100644
index 000000000000..9459593eef13
--- /dev/null
+++ b/sound/soc/codecs/da7219-aad.c
@@ -0,0 +1,823 @@
+/*
+ * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219.h>
+
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * Detection control
+ */
+
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ da7219->aad->jack = jack;
+ da7219->aad->jack_inserted = false;
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
+
+ /* Enable/Disable jack detection */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK,
+ (jack ? DA7219_ACCDET_EN_MASK : 0));
+}
+EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
+
+/*
+ * Button/HPTest work
+ */
+
+static void da7219_aad_btn_det_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, btn_det_work);
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ u8 statusa, micbias_ctrl;
+ bool micbias_up = false;
+ int retries = 0;
+
+ /* Drive headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ /* Make sure mic bias is up */
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ do {
+ statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+ if (statusa & DA7219_MICBIAS_UP_STS_MASK)
+ micbias_up = true;
+ else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
+ msleep(DA7219_AAD_MICBIAS_CHK_DELAY);
+ } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
+
+ if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
+ dev_warn(codec->dev, "Mic bias status check timed out");
+
+ /*
+ * Mic bias pulse required to enable mic, must be done before enabling
+ * button detection to prevent erroneous button readings.
+ */
+ if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
+ /* Pulse higher level voltage */
+ micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
+ snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_LEVEL_MASK,
+ da7219_aad->micbias_pulse_lvl);
+ msleep(da7219_aad->micbias_pulse_time);
+ snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl);
+
+ }
+
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK,
+ da7219_aad->btn_cfg);
+}
+
+static void da7219_aad_hptest_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, hptest_work);
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ u16 tonegen_freq_hptest;
+ u8 accdet_cfg8;
+ int report = 0;
+
+ /* Lock DAPM and any Kcontrols that are affected by this test */
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&da7219->lock);
+
+ /* Bypass cache so it saves current settings */
+ regcache_cache_bypass(da7219->regmap, true);
+
+ /* Make sure Tone Generator is disabled */
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+ /* Enable HPTest block, 1KOhms check */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
+ DA7219_HPTEST_EN_MASK |
+ DA7219_HPTEST_RES_SEL_1KOHMS);
+
+ /* Set gains to 0db */
+ snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
+ snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
+
+ /* Disable DAC filters, EQs and soft mute */
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
+ 0);
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
+ 0);
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_MASK, 0);
+
+ /* Enable HP left & right paths */
+ snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
+ DA7219_CP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
+ DA7219_DAC_L_SRC_TONEGEN |
+ DA7219_DAC_R_SRC_TONEGEN);
+ snd_soc_update_bits(codec, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
+ DA7219_DAC_L_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
+ DA7219_DAC_R_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK);
+ snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_OUTFILT_ST_1L_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1L);
+ snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R,
+ DA7219_OUTFILT_ST_1R_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1R);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_MASK,
+ DA7219_MIXOUT_L_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_MASK,
+ DA7219_MIXOUT_R_AMP_EN_MASK);
+ snd_soc_write(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+ snd_soc_write(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+
+ /* Configure & start Tone Generator */
+ snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+ regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ &tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
+ snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2,
+ DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
+ DA7219_SWG_SEL_SRAMP |
+ DA7219_TONE_GEN_GAIN_MINUS_15DB);
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Grab comparator reading */
+ accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8);
+ if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
+ report |= SND_JACK_HEADPHONE;
+ else
+ report |= SND_JACK_LINEOUT;
+
+ /* Stop tone generator */
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Restore original settings from cache */
+ regcache_mark_dirty(da7219->regmap);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_DROUTING_ST_OUTFILT_1R);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_R_SELECT);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC,
+ DA7219_DIG_ROUTING_DAC);
+ regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5,
+ DA7219_DAC_FILTERS5);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4,
+ DA7219_DAC_FILTERS1);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER,
+ DA7219_TONE_GEN_ON_PER);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ DA7219_TONE_GEN_FREQ1_U);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1,
+ DA7219_TONE_GEN_CFG2);
+
+ regcache_cache_bypass(da7219->regmap, false);
+
+ /* Disable HPTest block */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK, 0);
+
+ /* Drive Headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ mutex_unlock(&da7219->lock);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ /*
+ * Only send report if jack hasn't been removed during process,
+ * otherwise it's invalid and we drop it.
+ */
+ if (da7219_aad->jack_inserted)
+ snd_soc_jack_report(da7219_aad->jack, report,
+ SND_JACK_HEADSET | SND_JACK_LINEOUT);
+}
+
+
+/*
+ * IRQ
+ */
+
+static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
+{
+ struct da7219_aad_priv *da7219_aad = data;
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 events[DA7219_AAD_IRQ_REG_MAX];
+ u8 statusa;
+ int i, report = 0, mask = 0;
+
+ /* Read current IRQ events */
+ regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
+ return IRQ_NONE;
+
+ /* Read status register for jack insertion & type status */
+ statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+
+ /* Clear events */
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
+ events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
+ statusa);
+
+ if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
+ /* Jack Insertion */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_INSERTED_MASK) {
+ report |= SND_JACK_MECHANICAL;
+ mask |= SND_JACK_MECHANICAL;
+ da7219_aad->jack_inserted = true;
+ }
+
+ /* Jack type detection */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_DETECT_COMPLETE_MASK) {
+ /*
+ * If 4-pole, then enable button detection, else perform
+ * HP impedance test to determine output type to report.
+ *
+ * We schedule work here as the tasks themselves can
+ * take time to complete, and in particular for hptest
+ * we want to be able to check if the jack was removed
+ * during the procedure as this will invalidate the
+ * result. By doing this as work, the IRQ thread can
+ * handle a removal, and we can check at the end of
+ * hptest if we have a valid result or not.
+ */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ report |= SND_JACK_HEADSET;
+ mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
+ schedule_work(&da7219_aad->btn_det_work);
+ } else {
+ schedule_work(&da7219_aad->hptest_work);
+ }
+ }
+
+ /* Button support for 4-pole jack */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Press */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_PRESSED_MASK << i)) {
+ report |= SND_JACK_BTN_0 >> i;
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Release */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) {
+ report &= ~(SND_JACK_BTN_0 >> i);
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ }
+ } else {
+ /* Jack removal */
+ if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) {
+ report = 0;
+ mask |= DA7219_AAD_REPORT_ALL_MASK;
+ da7219_aad->jack_inserted = false;
+
+ /* Un-drive headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK, 0);
+
+ /* Ensure button detection disabled */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ /* Disable mic bias */
+ snd_soc_dapm_disable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ /* Cancel any pending work */
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+ }
+ }
+
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * DT to pdata conversion
+ */
+
+static enum da7219_aad_micbias_pulse_lvl
+ da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 2800:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V;
+ case 2900:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias pulse level");
+ return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+ }
+}
+
+static enum da7219_aad_btn_cfg
+ da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 2:
+ return DA7219_AAD_BTN_CFG_2MS;
+ case 5:
+ return DA7219_AAD_BTN_CFG_5MS;
+ case 10:
+ return DA7219_AAD_BTN_CFG_10MS;
+ case 50:
+ return DA7219_AAD_BTN_CFG_50MS;
+ case 100:
+ return DA7219_AAD_BTN_CFG_100MS;
+ case 200:
+ return DA7219_AAD_BTN_CFG_200MS;
+ case 500:
+ return DA7219_AAD_BTN_CFG_500MS;
+ default:
+ dev_warn(codec->dev, "Invalid button config");
+ return DA7219_AAD_BTN_CFG_10MS;
+ }
+}
+
+static enum da7219_aad_mic_det_thr
+ da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 200:
+ return DA7219_AAD_MIC_DET_THR_200_OHMS;
+ case 500:
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ case 750:
+ return DA7219_AAD_MIC_DET_THR_750_OHMS;
+ case 1000:
+ return DA7219_AAD_MIC_DET_THR_1000_OHMS;
+ default:
+ dev_warn(codec->dev, "Invalid mic detect threshold");
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ }
+}
+
+static enum da7219_aad_jack_ins_deb
+ da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 5:
+ return DA7219_AAD_JACK_INS_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_INS_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ case 50:
+ return DA7219_AAD_JACK_INS_DEB_50MS;
+ case 100:
+ return DA7219_AAD_JACK_INS_DEB_100MS;
+ case 200:
+ return DA7219_AAD_JACK_INS_DEB_200MS;
+ case 500:
+ return DA7219_AAD_JACK_INS_DEB_500MS;
+ case 1000:
+ return DA7219_AAD_JACK_INS_DEB_1S;
+ default:
+ dev_warn(codec->dev, "Invalid jack insert debounce");
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ }
+}
+
+static enum da7219_aad_jack_det_rate
+ da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "32ms_64ms")) {
+ return DA7219_AAD_JACK_DET_RATE_32_64MS;
+ } else if (!strcmp(str, "64ms_128ms")) {
+ return DA7219_AAD_JACK_DET_RATE_64_128MS;
+ } else if (!strcmp(str, "128ms_256ms")) {
+ return DA7219_AAD_JACK_DET_RATE_128_256MS;
+ } else if (!strcmp(str, "256ms_512ms")) {
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ } else {
+ dev_warn(codec->dev, "Invalid jack detect rate");
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ }
+}
+
+static enum da7219_aad_jack_rem_deb
+ da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ case 5:
+ return DA7219_AAD_JACK_REM_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_REM_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_REM_DEB_20MS;
+ default:
+ dev_warn(codec->dev, "Invalid jack removal debounce");
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ }
+}
+
+static enum da7219_aad_btn_avg
+ da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_BTN_AVG_1;
+ case 2:
+ return DA7219_AAD_BTN_AVG_2;
+ case 4:
+ return DA7219_AAD_BTN_AVG_4;
+ case 8:
+ return DA7219_AAD_BTN_AVG_8;
+ default:
+ dev_warn(codec->dev, "Invalid button average value");
+ return DA7219_AAD_BTN_AVG_2;
+ }
+}
+
+static enum da7219_aad_adc_1bit_rpt
+ da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ case 2:
+ return DA7219_AAD_ADC_1BIT_RPT_2;
+ case 4:
+ return DA7219_AAD_ADC_1BIT_RPT_4;
+ case 8:
+ return DA7219_AAD_ADC_1BIT_RPT_8;
+ default:
+ dev_warn(codec->dev, "Invalid ADC 1-bit repeat value");
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ }
+}
+
+static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+ struct da7219_aad_pdata *aad_pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ if (!aad_np)
+ return NULL;
+
+ aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
+ if (!aad_pdata)
+ goto out;
+
+ aad_pdata->irq = irq_of_parse_and_map(np, 0);
+
+ if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+ &of_val32) >= 0)
+ aad_pdata->micbias_pulse_lvl =
+ da7219_aad_of_micbias_pulse_lvl(codec, of_val32);
+ else
+ aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+
+ if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+ &of_val32) >= 0)
+ aad_pdata->micbias_pulse_time = of_val32;
+
+ if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0)
+ aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32);
+ else
+ aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
+
+ if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0)
+ aad_pdata->mic_det_thr =
+ da7219_aad_of_mic_det_thr(codec, of_val32);
+ else
+ aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
+
+ if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0)
+ aad_pdata->jack_ins_deb =
+ da7219_aad_of_jack_ins_deb(codec, of_val32);
+ else
+ aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
+
+ if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str))
+ aad_pdata->jack_det_rate =
+ da7219_aad_of_jack_det_rate(codec, of_str);
+ else
+ aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
+
+ if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0)
+ aad_pdata->jack_rem_deb =
+ da7219_aad_of_jack_rem_deb(codec, of_val32);
+ else
+ aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
+
+ if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0)
+ aad_pdata->a_d_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->a_d_btn_thr = 0xA;
+
+ if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0)
+ aad_pdata->d_b_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->d_b_btn_thr = 0x16;
+
+ if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0)
+ aad_pdata->b_c_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->b_c_btn_thr = 0x21;
+
+ if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0)
+ aad_pdata->c_mic_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->c_mic_btn_thr = 0x3E;
+
+ if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0)
+ aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32);
+ else
+ aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
+
+ if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0)
+ aad_pdata->adc_1bit_rpt =
+ da7219_aad_of_adc_1bit_rpt(codec, of_val32);
+ else
+ aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
+
+out:
+ of_node_put(aad_np);
+
+ return aad_pdata;
+}
+
+static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if ((pdata) && (pdata->aad_pdata)) {
+ struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata;
+ u8 cfg, mask;
+
+ da7219_aad->irq = aad_pdata->irq;
+
+ switch (aad_pdata->micbias_pulse_lvl) {
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V:
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V:
+ da7219_aad->micbias_pulse_lvl =
+ (aad_pdata->micbias_pulse_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time;
+
+ switch (aad_pdata->btn_cfg) {
+ case DA7219_AAD_BTN_CFG_2MS:
+ case DA7219_AAD_BTN_CFG_5MS:
+ case DA7219_AAD_BTN_CFG_10MS:
+ case DA7219_AAD_BTN_CFG_50MS:
+ case DA7219_AAD_BTN_CFG_100MS:
+ case DA7219_AAD_BTN_CFG_200MS:
+ case DA7219_AAD_BTN_CFG_500MS:
+ da7219_aad->btn_cfg = (aad_pdata->btn_cfg <<
+ DA7219_BUTTON_CONFIG_SHIFT);
+ }
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->mic_det_thr) {
+ case DA7219_AAD_MIC_DET_THR_200_OHMS:
+ case DA7219_AAD_MIC_DET_THR_500_OHMS:
+ case DA7219_AAD_MIC_DET_THR_750_OHMS:
+ case DA7219_AAD_MIC_DET_THR_1000_OHMS:
+ cfg |= (aad_pdata->mic_det_thr <<
+ DA7219_MIC_DET_THRESH_SHIFT);
+ mask |= DA7219_MIC_DET_THRESH_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->jack_ins_deb) {
+ case DA7219_AAD_JACK_INS_DEB_5MS:
+ case DA7219_AAD_JACK_INS_DEB_10MS:
+ case DA7219_AAD_JACK_INS_DEB_20MS:
+ case DA7219_AAD_JACK_INS_DEB_50MS:
+ case DA7219_AAD_JACK_INS_DEB_100MS:
+ case DA7219_AAD_JACK_INS_DEB_200MS:
+ case DA7219_AAD_JACK_INS_DEB_500MS:
+ case DA7219_AAD_JACK_INS_DEB_1S:
+ cfg |= (aad_pdata->jack_ins_deb <<
+ DA7219_JACKDET_DEBOUNCE_SHIFT);
+ mask |= DA7219_JACKDET_DEBOUNCE_MASK;
+ }
+ switch (aad_pdata->jack_det_rate) {
+ case DA7219_AAD_JACK_DET_RATE_32_64MS:
+ case DA7219_AAD_JACK_DET_RATE_64_128MS:
+ case DA7219_AAD_JACK_DET_RATE_128_256MS:
+ case DA7219_AAD_JACK_DET_RATE_256_512MS:
+ cfg |= (aad_pdata->jack_det_rate <<
+ DA7219_JACK_DETECT_RATE_SHIFT);
+ mask |= DA7219_JACK_DETECT_RATE_MASK;
+ }
+ switch (aad_pdata->jack_rem_deb) {
+ case DA7219_AAD_JACK_REM_DEB_1MS:
+ case DA7219_AAD_JACK_REM_DEB_5MS:
+ case DA7219_AAD_JACK_REM_DEB_10MS:
+ case DA7219_AAD_JACK_REM_DEB_20MS:
+ cfg |= (aad_pdata->jack_rem_deb <<
+ DA7219_JACKDET_REM_DEB_SHIFT);
+ mask |= DA7219_JACKDET_REM_DEB_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg);
+
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_3,
+ aad_pdata->a_d_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_4,
+ aad_pdata->d_b_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_5,
+ aad_pdata->b_c_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_6,
+ aad_pdata->c_mic_btn_thr);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->btn_avg) {
+ case DA7219_AAD_BTN_AVG_1:
+ case DA7219_AAD_BTN_AVG_2:
+ case DA7219_AAD_BTN_AVG_4:
+ case DA7219_AAD_BTN_AVG_8:
+ cfg |= (aad_pdata->btn_avg <<
+ DA7219_BUTTON_AVERAGE_SHIFT);
+ mask |= DA7219_BUTTON_AVERAGE_MASK;
+ }
+ switch (aad_pdata->adc_1bit_rpt) {
+ case DA7219_AAD_ADC_1BIT_RPT_1:
+ case DA7219_AAD_ADC_1BIT_RPT_2:
+ case DA7219_AAD_ADC_1BIT_RPT_4:
+ case DA7219_AAD_ADC_1BIT_RPT_8:
+ cfg |= (aad_pdata->adc_1bit_rpt <<
+ DA7219_ADC_1_BIT_REPEAT_SHIFT);
+ mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg);
+ }
+}
+
+
+/*
+ * Init/Exit
+ */
+
+int da7219_aad_init(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+ int ret;
+
+ da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL);
+ if (!da7219_aad)
+ return -ENOMEM;
+
+ da7219->aad = da7219_aad;
+ da7219_aad->codec = codec;
+
+ /* Handle any DT/platform data */
+ if ((codec->dev->of_node) && (da7219->pdata))
+ da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec);
+
+ da7219_aad_handle_pdata(codec);
+
+ /* Disable button detection */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
+ INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
+
+ ret = request_threaded_irq(da7219_aad->irq, NULL,
+ da7219_aad_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "da7219-aad", da7219_aad);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+
+ /* Unmask AAD IRQs */
+ memset(mask, 0, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ &mask, DA7219_AAD_IRQ_REG_MAX);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da7219_aad_init);
+
+void da7219_aad_exit(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+
+ /* Mask off AAD IRQs */
+ memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ mask, DA7219_AAD_IRQ_REG_MAX);
+
+ free_irq(da7219_aad->irq, da7219_aad);
+
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+}
+EXPORT_SYMBOL_GPL(da7219_aad_exit);
+
+MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
new file mode 100644
index 000000000000..4fccf677cd06
--- /dev/null
+++ b/sound/soc/codecs/da7219-aad.h
@@ -0,0 +1,212 @@
+/*
+ * da7219-aad.h - DA7322 ASoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __DA7219_AAD_H
+#define __DA7219_AAD_H
+
+#include <linux/timer.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219-aad.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_ACCDET_STATUS_A 0xC0
+#define DA7219_ACCDET_STATUS_B 0xC1
+#define DA7219_ACCDET_IRQ_EVENT_A 0xC2
+#define DA7219_ACCDET_IRQ_EVENT_B 0xC3
+#define DA7219_ACCDET_IRQ_MASK_A 0xC4
+#define DA7219_ACCDET_IRQ_MASK_B 0xC5
+#define DA7219_ACCDET_CONFIG_1 0xC6
+#define DA7219_ACCDET_CONFIG_2 0xC7
+#define DA7219_ACCDET_CONFIG_3 0xC8
+#define DA7219_ACCDET_CONFIG_4 0xC9
+#define DA7219_ACCDET_CONFIG_5 0xCA
+#define DA7219_ACCDET_CONFIG_6 0xCB
+#define DA7219_ACCDET_CONFIG_7 0xCC
+#define DA7219_ACCDET_CONFIG_8 0xCD
+
+
+/*
+ * Bit Fields
+ */
+
+/* DA7219_ACCDET_STATUS_A = 0xC0 */
+#define DA7219_JACK_INSERTION_STS_SHIFT 0
+#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0)
+#define DA7219_JACK_TYPE_STS_SHIFT 1
+#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1)
+#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2
+#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2)
+#define DA7219_MICBIAS_UP_STS_SHIFT 3
+#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3)
+
+/* DA7219_ACCDET_STATUS_B = 0xC1 */
+#define DA7219_BUTTON_TYPE_STS_SHIFT 0
+#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */
+#define DA7219_E_JACK_INSERTED_SHIFT 0
+#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_E_JACK_REMOVED_SHIFT 1
+#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */
+#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */
+#define DA7219_M_JACK_INSERTED_SHIFT 0
+#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_M_JACK_REMOVED_SHIFT 1
+#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */
+#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_1 = 0xC6 */
+#define DA7219_ACCDET_EN_SHIFT 0
+#define DA7219_ACCDET_EN_MASK (0x1 << 0)
+#define DA7219_BUTTON_CONFIG_SHIFT 1
+#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1)
+#define DA7219_MIC_DET_THRESH_SHIFT 4
+#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4)
+#define DA7219_JACK_TYPE_DET_EN_SHIFT 6
+#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6)
+#define DA7219_PIN_ORDER_DET_EN_SHIFT 7
+#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_2 = 0xC7 */
+#define DA7219_ACCDET_PAUSE_SHIFT 0
+#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0)
+#define DA7219_JACKDET_DEBOUNCE_SHIFT 1
+#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1)
+#define DA7219_JACK_DETECT_RATE_SHIFT 4
+#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4)
+#define DA7219_JACKDET_REM_DEB_SHIFT 6
+#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6)
+
+/* DA7219_ACCDET_CONFIG_3 = 0xC8 */
+#define DA7219_A_D_BUTTON_THRESH_SHIFT 0
+#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_4 = 0xC9 */
+#define DA7219_D_B_BUTTON_THRESH_SHIFT 0
+#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_5 = 0xCA */
+#define DA7219_B_C_BUTTON_THRESH_SHIFT 0
+#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_6 = 0xCB */
+#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0
+#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_7 = 0xCC */
+#define DA7219_BUTTON_AVERAGE_SHIFT 0
+#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0)
+#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2
+#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2)
+#define DA7219_PIN_ORDER_FORCE_SHIFT 4
+#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4)
+#define DA7219_JACK_TYPE_FORCE_SHIFT 5
+#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5)
+
+/* DA7219_ACCDET_CONFIG_8 = 0xCD */
+#define DA7219_HPTEST_EN_SHIFT 0
+#define DA7219_HPTEST_EN_MASK (0x1 << 0)
+#define DA7219_HPTEST_RES_SEL_SHIFT 1
+#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1)
+#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1)
+#define DA7219_HPTEST_COMP_SHIFT 4
+#define DA7219_HPTEST_COMP_MASK (0x1 << 4)
+
+
+#define DA7219_AAD_MAX_BUTTONS 4
+#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \
+ SND_JACK_HEADSET | SND_JACK_LINEOUT | \
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+#define DA7219_AAD_MICBIAS_CHK_DELAY 10
+#define DA7219_AAD_MICBIAS_CHK_RETRIES 5
+
+#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28
+#define DA7219_AAD_HPTEST_PERIOD 65
+
+enum da7219_aad_event_regs {
+ DA7219_AAD_IRQ_REG_A = 0,
+ DA7219_AAD_IRQ_REG_B,
+ DA7219_AAD_IRQ_REG_MAX,
+};
+
+/* Private data */
+struct da7219_aad_priv {
+ struct snd_soc_codec *codec;
+ int irq;
+
+ u8 micbias_pulse_lvl;
+ u32 micbias_pulse_time;
+
+ u8 btn_cfg;
+
+ struct work_struct btn_det_work;
+ struct work_struct hptest_work;
+
+ struct snd_soc_jack *jack;
+ bool jack_inserted;
+};
+
+/* AAD control */
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+/* Init/Exit */
+int da7219_aad_init(struct snd_soc_codec *codec);
+void da7219_aad_exit(struct snd_soc_codec *codec);
+
+#endif /* __DA7219_AAD_H */
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
new file mode 100644
index 000000000000..f238c1e8a69c
--- /dev/null
+++ b/sound/soc/codecs/da7219.c
@@ -0,0 +1,1955 @@
+/*
+ * da7219.c - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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/clk.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include <sound/da7219.h>
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * TLVs and Enums
+ */
+
+/* Input TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0);
+
+/* Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0);
+
+static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv,
+ 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -77.25dB to 12dB */
+ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0);
+
+/* Input Enums */
+static const char * const da7219_alc_attack_rate_txt[] = {
+ "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs",
+ "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs",
+ "30024/fs"
+};
+
+static const struct soc_enum da7219_alc_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT,
+ DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt);
+
+static const char * const da7219_alc_release_rate_txt[] = {
+ "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs",
+ "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs"
+};
+
+static const struct soc_enum da7219_alc_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT,
+ DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt);
+
+static const char * const da7219_alc_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static const struct soc_enum da7219_alc_hold_time =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT,
+ DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt);
+
+static const char * const da7219_alc_env_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da7219_alc_env_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const struct soc_enum da7219_alc_env_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const char * const da7219_alc_anticlip_step_txt[] = {
+ "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs"
+};
+
+static const struct soc_enum da7219_alc_anticlip_step =
+ SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTICLIP_STEP_SHIFT,
+ DA7219_ALC_ANTICLIP_STEP_MAX,
+ da7219_alc_anticlip_step_txt);
+
+/* Input/Output Enums */
+static const char * const da7219_gain_ramp_rate_txt[] = {
+ "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8",
+ "Nominal Rate / 16"
+};
+
+static const struct soc_enum da7219_gain_ramp_rate =
+ SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT,
+ DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt);
+
+static const char * const da7219_hpf_mode_txt[] = {
+ "Disabled", "Audio", "Voice"
+};
+
+static const unsigned int da7219_hpf_mode_val[] = {
+ DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN,
+};
+
+static const struct soc_enum da7219_adc_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const struct soc_enum da7219_dac_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const char * const da7219_audio_hpf_corner_txt[] = {
+ "2Hz", "4Hz", "8Hz", "16Hz"
+};
+
+static const struct soc_enum da7219_adc_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const char * const da7219_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum da7219_adc_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const char * const da7219_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7219_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT,
+ DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt);
+
+static const char * const da7219_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "SWG1_1-Cos"
+};
+
+static const struct soc_enum da7219_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT,
+ DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt);
+
+/* Output Enums */
+static const char * const da7219_dac_softmute_rate_txt[] = {
+ "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples",
+ "32 Samples", "64 Samples"
+};
+
+static const struct soc_enum da7219_dac_softmute_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT,
+ DA7219_DAC_SOFTMUTE_RATE_MAX,
+ da7219_dac_softmute_rate_txt);
+
+static const char * const da7219_dac_ng_setup_time_txt[] = {
+ "256 Samples", "512 Samples", "1024 Samples", "2048 Samples"
+};
+
+static const struct soc_enum da7219_dac_ng_setup_time =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_SETUP_TIME_SHIFT,
+ DA7219_DAC_NG_SETUP_TIME_MAX,
+ da7219_dac_ng_setup_time_txt);
+
+static const char * const da7219_dac_ng_rampup_txt[] = {
+ "0.22ms/dB", "0.0138ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampup_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPUP_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampup_txt);
+
+static const char * const da7219_dac_ng_rampdown_txt[] = {
+ "0.88ms/dB", "14.08ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampdown_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPDN_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampdown_txt);
+
+
+static const char * const da7219_cp_track_mode_txt[] = {
+ "Largest Volume", "DAC Volume", "Signal Magnitude"
+};
+
+static const unsigned int da7219_cp_track_mode_val[] = {
+ DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL,
+ DA7219_CP_MCHANGE_SIG_MAG
+};
+
+static const struct soc_enum da7219_cp_track_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT,
+ DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX,
+ da7219_cp_track_mode_txt,
+ da7219_cp_track_mode_val);
+
+
+/*
+ * Control Functions
+ */
+
+/* Locked Kcontrol calls */
+static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+/* ALC */
+static void da7219_alc_calib(struct snd_soc_codec *codec)
+{
+ u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl;
+
+ /* Save current state of mic control register */
+ mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL);
+
+ /* Save current state of input mixer control register */
+ mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL);
+
+ /* Save current state of input ADC control register */
+ adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL);
+
+ /* Enable then Mute MIC PGAs */
+ snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
+ DA7219_MIC_1_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK);
+
+ /* Enable input mixers unmuted */
+ snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_MASK |
+ DA7219_MIXIN_L_AMP_MUTE_EN_MASK,
+ DA7219_MIXIN_L_AMP_EN_MASK);
+
+ /* Enable input filters unmuted */
+ snd_soc_update_bits(codec, DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK,
+ DA7219_ADC_L_EN_MASK);
+
+ /* Perform auto calibration */
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_AUTO_CALIB_EN_MASK,
+ DA7219_ALC_AUTO_CALIB_EN_MASK);
+ do {
+ calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1);
+ } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK);
+
+ /* If auto calibration fails, disable DC offset, hybrid ALC */
+ if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) {
+ dev_warn(codec->dev,
+ "ALC auto calibration failed with overflow\n");
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK, 0);
+ } else {
+ /* Enable DC offset cancellation, hybrid mode */
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK);
+ }
+
+ /* Restore input filter control register to original state */
+ snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl);
+
+ /* Restore input mixer control registers to original state */
+ snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl);
+
+ /* Restore MIC control registers to original states */
+ snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl);
+}
+
+static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ /*
+ * If ALC in operation and value of control has been updated,
+ * make sure calibrated offsets are updated.
+ */
+ if ((ret == 1) && (da7219->alc_en))
+ da7219_alc_calib(codec);
+
+ return ret;
+}
+
+static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+
+ /* Force ALC offset calibration if enabling ALC */
+ if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) {
+ da7219_alc_calib(codec);
+ da7219->alc_en = true;
+ } else {
+ da7219->alc_en = false;
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* ToneGen */
+static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7219->lock);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to host endianness here.
+ */
+ ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+ return 0;
+}
+
+static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+ int ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to little endian here to align with
+ * HW registers.
+ */
+ val = cpu_to_le16(ucontrol->value.integer.value[0]);
+
+ mutex_lock(&da7219->lock);
+ ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7219_snd_controls[] = {
+ /* Mics */
+ SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN,
+ DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_mic_gain_tlv),
+ SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Mixer Input */
+ SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN,
+ DA7219_MIXIN_L_AMP_GAIN_SHIFT,
+ DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_mixin_gain_put,
+ da7219_mixin_gain_tlv),
+ SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ADC */
+ SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN,
+ DA7219_ADC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_adc_dig_gain_tlv),
+ SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ALC */
+ SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7219_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7219_alc_hold_time),
+ SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate),
+ SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate),
+ SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE,
+ DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN,
+ DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX,
+ DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MIN_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MAX_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step),
+ SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_alc_sw_put),
+
+ /* Input High-Pass Filters */
+ SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode),
+ SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner),
+ SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner),
+
+ /* Sidetone Filter */
+ SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN,
+ DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_sidetone_gain_tlv),
+ SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Tone Generator */
+ SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2,
+ DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put, da7219_tonegen_gain_tlv),
+ SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1,
+ DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L,
+ DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L,
+ DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER,
+ DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER,
+ DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT),
+
+ /* Gain ramping */
+ SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate),
+
+ /* DAC High-Pass Filter */
+ SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner),
+ SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner),
+
+ /* DAC 5-Band Equaliser */
+ SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Softmute */
+ SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate),
+ SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Noise Gate */
+ SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate),
+ SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH,
+ DA7219_DAC_NG_OFF_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH,
+ DA7219_DAC_NG_ON_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* DACs */
+ SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_dac_dig_gain_tlv),
+ SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* CP */
+ SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode),
+ SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1,
+ DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX,
+ DA7219_NO_INVERT),
+
+ /* Headphones */
+ SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT,
+ DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_hp_gain_tlv),
+ SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+
+/*
+ * DAPM Mux Controls
+ */
+
+static const char * const da7219_out_sel_txt[] = {
+ "ADC", "Tone Generator", "DAIL", "DAIR"
+};
+
+static const struct soc_enum da7219_out_dail_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dail_sel_mux =
+ SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel);
+
+static const struct soc_enum da7219_out_dair_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dair_sel_mux =
+ SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel);
+
+static const struct soc_enum da7219_out_dacl_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacl_sel_mux =
+ SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel);
+
+static const struct soc_enum da7219_out_dacr_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacr_sel_mux =
+ SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel);
+
+
+/*
+ * DAPM Mixer Controls
+ */
+
+static const struct snd_kcontrol_new da7219_mixin_controls[] = {
+ SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT,
+ DA7219_MIXIN_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_l_controls[] = {
+ SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_r_controls[] = {
+ SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+#define DA7219_DMIX_ST_CTRLS(reg) \
+ SOC_DAPM_SINGLE("Out FilterL Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Out FilterR Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Sidetone Switch", reg, \
+ DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \
+
+static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+static int da7219_dai_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 pll_ctrl, pll_status;
+ int i = 0;
+ bool srm_lock = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (da7219->master)
+ /* Enable DAI clks for master mode */
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ DA7219_DAI_CLK_EN_MASK);
+
+ /* PC synchronised to DAI */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK, 0);
+
+ /* Slave mode, if SRM not enabled no need for status checks */
+ pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL);
+ if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM)
+ return 0;
+
+ /* Check SRM has locked */
+ do {
+ pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS);
+ if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) {
+ srm_lock = true;
+ } else {
+ ++i;
+ msleep(50);
+ }
+ } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock));
+
+ if (!srm_lock)
+ dev_warn(codec->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* PC free-running */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Disable DAI clks if in master mode */
+ if (da7219->master)
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK, 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * DAPM Widgets
+ */
+
+static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
+ /* Input Supplies */
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC"),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Input Filters */
+ SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT,
+ DA7219_NO_INVERT),
+
+ /* Tone Generator */
+ SND_SOC_DAPM_SIGGEN("TONE"),
+ SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1,
+ DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+ /* Sidetone Input */
+ SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT),
+
+ /* Input Mixer Supply */
+ SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Input Mixer */
+ SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0,
+ da7219_mixin_controls,
+ ARRAY_SIZE(da7219_mixin_controls)),
+
+ /* Input Muxes */
+ SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dail_sel_mux),
+ SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dair_sel_mux),
+
+ /* DAI Supply */
+ SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT,
+ DA7219_NO_INVERT, da7219_dai_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Muxes */
+ SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacl_sel_mux),
+ SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacr_sel_mux),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_l_controls,
+ ARRAY_SIZE(da7219_mixout_l_controls)),
+ SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_r_controls,
+ ARRAY_SIZE(da7219_mixout_r_controls)),
+
+ /* Sidetone Mixers */
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_st_out_filtl_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0,
+ 0, da7219_st_out_filtr_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT,
+ DA7219_NO_INVERT),
+ SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT,
+ DA7219_NO_INVERT),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+ /* Output Supplies */
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT,
+ DA7219_NO_INVERT, NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+
+/*
+ * DAPM Mux Routes
+ */
+
+#define DA7219_OUT_DAI_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIOUT"}, \
+ {name, "DAIR", "DAIOUT"}
+
+#define DA7219_OUT_DAC_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIIN"}, \
+ {name, "DAIR", "DAIIN"}
+
+/*
+ * DAPM Mixer Routes
+ */
+
+#define DA7219_DMIX_ST_ROUTES(name) \
+ {name, "Out FilterL Switch", "Mixer Out FilterL"}, \
+ {name, "Out FilterR Switch", "Mixer Out FilterR"}, \
+ {name, "Sidetone Switch", "Sidetone Filter"}
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7219_audio_map[] = {
+ /* Input paths */
+ {"MIC", NULL, "Mic Bias"},
+ {"Mic PGA", NULL, "MIC"},
+ {"Mixin PGA", NULL, "Mic PGA"},
+ {"ADC", NULL, "Mixin PGA"},
+
+ {"Sidetone Filter", NULL, "ADC"},
+ {"Mixer In", NULL, "Mixer In Supply"},
+ {"Mixer In", "Mic Switch", "ADC"},
+
+ {"Tone Generator", NULL, "TONE"},
+
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"),
+
+ {"DAIOUT", NULL, "Out DAIL Mux"},
+ {"DAIOUT", NULL, "Out DAIR Mux"},
+ {"DAIOUT", NULL, "DAI"},
+
+ /* Output paths */
+ {"DAIIN", NULL, "DAI"},
+
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"),
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"),
+
+ {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"},
+ {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"},
+
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"),
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"),
+
+ {"DACL", NULL, "ST Mixer Out FilterL"},
+ {"DACR", NULL, "ST Mixer Out FilterR"},
+
+ {"Mixout Left PGA", NULL, "DACL"},
+ {"Mixout Right PGA", NULL, "DACR"},
+
+ {"Headphone Left PGA", NULL, "Mixout Left PGA"},
+ {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+
+ {"HPL", NULL, "Headphone Left PGA"},
+ {"HPR", NULL, "Headphone Right PGA"},
+
+ {"HPL", NULL, "Charge Pump"},
+ {"HPR", NULL, "Charge Pump"},
+};
+
+
+/*
+ * DAI operations
+ */
+
+static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
+ return 0;
+
+ if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case DA7219_CLKSRC_MCLK_SQR:
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK,
+ DA7219_PLL_MCLK_SQR_EN_MASK);
+ break;
+ case DA7219_CLKSRC_MCLK:
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK, 0);
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ da7219->clk_src = clk_id;
+
+ if (da7219->mclk) {
+ freq = clk_round_rate(da7219->mclk, freq);
+ ret = clk_set_rate(da7219->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7219->mclk_rate = freq;
+
+ return 0;
+}
+
+static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ u8 pll_ctrl, indiv_bits, indiv;
+ u8 pll_frac_top, pll_frac_bot, pll_integer;
+ u32 freq_ref;
+ u64 frac_div;
+
+ /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7219->mclk_rate == 32768) {
+ indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+ } else if (da7219->mclk_rate < 2000000) {
+ dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ } else if (da7219->mclk_rate <= 5000000) {
+ indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 10000000) {
+ indiv_bits = DA7219_PLL_INDIV_5_10_MHZ;
+ indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 20000000) {
+ indiv_bits = DA7219_PLL_INDIV_10_20_MHZ;
+ indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 40000000) {
+ indiv_bits = DA7219_PLL_INDIV_20_40_MHZ;
+ indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 54000000) {
+ indiv_bits = DA7219_PLL_INDIV_40_54_MHZ;
+ indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL;
+ } else {
+ dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ }
+ freq_ref = (da7219->mclk_rate / indiv);
+ pll_ctrl = indiv_bits;
+
+ /* Configure PLL */
+ switch (source) {
+ case DA7219_SYSCLK_MCLK:
+ pll_ctrl |= DA7219_PLL_MODE_BYPASS;
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK |
+ DA7219_PLL_MODE_MASK, pll_ctrl);
+ return 0;
+ case DA7219_SYSCLK_PLL:
+ pll_ctrl |= DA7219_PLL_MODE_NORMAL;
+ break;
+ case DA7219_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7219_PLL_MODE_SRM;
+ break;
+ case DA7219_SYSCLK_PLL_32KHZ:
+ pll_ctrl |= DA7219_PLL_MODE_32KHZ;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid PLL config\n");
+ return -EINVAL;
+ }
+
+ /* Calculate dividers for PLL */
+ pll_integer = fout / freq_ref;
+ frac_div = (u64)(fout % freq_ref) * 8192ULL;
+ do_div(frac_div, freq_ref);
+ pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7219_BYTE_MASK;
+
+ /* Write PLL config & dividers */
+ snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer);
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK,
+ pll_ctrl);
+
+ return 0;
+}
+
+static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 dai_clk_mode = 0, dai_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da7219->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7219->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+ DA7219_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7219_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dai_ctrl |= DA7219_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default 64 BCLKs per WCLK is supported */
+ dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64;
+
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK |
+ DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK,
+ dai_clk_mode);
+ snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 dai_bclks_per_wclk;
+ u16 offset;
+ u32 frame_size;
+
+ /* No channels enabled so disable TDM, revert to 64-bit frames */
+ if (!tx_mask) {
+ snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ DA7219_DAI_BCLKS_PER_WCLK_64);
+ return 0;
+ }
+
+ /* Check we have valid slots */
+ if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
+ dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ DA7219_DAI_TDM_MAX_SLOTS);
+ return -EINVAL;
+ }
+
+ /* Check we have a valid offset given */
+ if (rx_mask > DA7219_DAI_OFFSET_MAX) {
+ dev_err(codec->dev, "Invalid slot offset, max = %d\n",
+ DA7219_DAI_OFFSET_MAX);
+ return -EINVAL;
+ }
+
+ /* Calculate & validate frame size based on slot info provided. */
+ frame_size = slots * slot_width;
+ switch (frame_size) {
+ case 32:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid frame size %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ dai_bclks_per_wclk);
+
+ offset = cpu_to_le16(rx_mask);
+ regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER,
+ &offset, sizeof(offset));
+
+ snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK,
+ (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
+ DA7219_DAI_TDM_MODE_EN_MASK);
+
+ return 0;
+}
+
+static int da7219_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;
+ u8 dai_ctrl = 0, fs;
+ unsigned int channels;
+
+ switch (params_width(params)) {
+ case 16:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
+ dev_err(codec->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7219_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7219_SR_8000;
+ break;
+ case 11025:
+ fs = DA7219_SR_11025;
+ break;
+ case 12000:
+ fs = DA7219_SR_12000;
+ break;
+ case 16000:
+ fs = DA7219_SR_16000;
+ break;
+ case 22050:
+ fs = DA7219_SR_22050;
+ break;
+ case 24000:
+ fs = DA7219_SR_24000;
+ break;
+ case 32000:
+ fs = DA7219_SR_32000;
+ break;
+ case 44100:
+ fs = DA7219_SR_44100;
+ break;
+ case 48000:
+ fs = DA7219_SR_48000;
+ break;
+ case 88200:
+ fs = DA7219_SR_88200;
+ break;
+ case 96000:
+ fs = DA7219_SR_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7219_DAI_CTRL,
+ DA7219_DAI_WORD_LENGTH_MASK |
+ DA7219_DAI_CH_NUM_MASK,
+ dai_ctrl);
+ snd_soc_write(codec, DA7219_SR, fs);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops da7219_dai_ops = {
+ .hw_params = da7219_hw_params,
+ .set_sysclk = da7219_set_dai_sysclk,
+ .set_pll = da7219_set_dai_pll,
+ .set_fmt = da7219_set_dai_fmt,
+ .set_tdm_slot = da7219_set_dai_tdm_slot,
+};
+
+#define DA7219_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 da7219_dai = {
+ .name = "da7219-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7219_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7219_FORMATS,
+ },
+ .ops = &da7219_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_channels = 1,
+ .symmetric_samplebits = 1,
+};
+
+
+/*
+ * DT
+ */
+
+static const struct of_device_id da7219_of_match[] = {
+ { .compatible = "dlg,da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7219_of_match);
+
+static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec,
+ u32 val)
+{
+ switch (val) {
+ case 1050:
+ return DA7219_LDO_LVL_SEL_1_05V;
+ case 1100:
+ return DA7219_LDO_LVL_SEL_1_10V;
+ case 1200:
+ return DA7219_LDO_LVL_SEL_1_20V;
+ case 1400:
+ return DA7219_LDO_LVL_SEL_1_40V;
+ default:
+ dev_warn(codec->dev, "Invalid LDO level");
+ return DA7219_LDO_LVL_SEL_1_05V;
+ }
+}
+
+static enum da7219_micbias_voltage
+ da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1800:
+ return DA7219_MICBIAS_1_8V;
+ case 2000:
+ return DA7219_MICBIAS_2_0V;
+ case 2200:
+ return DA7219_MICBIAS_2_2V;
+ case 2400:
+ return DA7219_MICBIAS_2_4V;
+ case 2600:
+ return DA7219_MICBIAS_2_6V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias level");
+ return DA7219_MICBIAS_2_2V;
+ }
+}
+
+static enum da7219_mic_amp_in_sel
+ da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "diff")) {
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ } else if (!strcmp(str, "se_p")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_P;
+ } else if (!strcmp(str, "se_n")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_N;
+ } else {
+ dev_warn(codec->dev, "Invalid mic input type selection");
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ }
+}
+
+static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct da7219_pdata *pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0)
+ pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32);
+
+ if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
+ pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
+
+ if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str))
+ pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str);
+ else
+ pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
+
+ return pdata;
+}
+
+
+/*
+ * Codec driver functions
+ */
+
+static int da7219_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ /* MCLK */
+ if (da7219->mclk) {
+ ret = clk_prepare_enable(da7219->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK,
+ DA7219_BIAS_EN_MASK);
+
+ /* Enable Internal Digital LDO */
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_EN_MASK,
+ DA7219_LDO_EN_MASK);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Only disable if jack detection not active */
+ if (!da7219->aad->jack) {
+ /* Bypass Internal Digital LDO */
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_EN_MASK, 0);
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK, 0);
+ }
+
+ /* MCLK */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
+ [DA7219_SUPPLY_VDD] = "VDD",
+ [DA7219_SUPPLY_VDDMIC] = "VDDMIC",
+ [DA7219_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7219_handle_supplies(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct regulator *vddio;
+ u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+ int i, ret;
+
+ /* Get required supplies */
+ for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
+ da7219->supplies[i].supply = da7219_supply_names[i];
+
+ ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES,
+ da7219->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to get supplies");
+ return ret;
+ }
+
+ /* Determine VDDIO voltage provided */
+ vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer;
+ ret = regulator_get_voltage(vddio);
+ if (ret < 1200000)
+ dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+ else if (ret < 2800000)
+ io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
+
+ /* Enable main supplies */
+ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies");
+ return ret;
+ }
+
+ /* Ensure device in active mode */
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK);
+
+ /* Update IO voltage level range */
+ snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl);
+
+ return 0;
+}
+
+static void da7219_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if (pdata) {
+ u8 micbias_lvl = 0;
+
+ /* Internal LDO */
+ switch (pdata->ldo_lvl_sel) {
+ case DA7219_LDO_LVL_SEL_1_05V:
+ case DA7219_LDO_LVL_SEL_1_10V:
+ case DA7219_LDO_LVL_SEL_1_20V:
+ case DA7219_LDO_LVL_SEL_1_40V:
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_LEVEL_SELECT_MASK,
+ (pdata->ldo_lvl_sel <<
+ DA7219_LDO_LEVEL_SELECT_SHIFT));
+ break;
+ }
+
+ /* Mic Bias voltages */
+ switch (pdata->micbias_lvl) {
+ case DA7219_MICBIAS_1_8V:
+ case DA7219_MICBIAS_2_0V:
+ case DA7219_MICBIAS_2_2V:
+ case DA7219_MICBIAS_2_4V:
+ case DA7219_MICBIAS_2_6V:
+ micbias_lvl |= (pdata->micbias_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ }
+
+ snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl);
+
+ /* Mic */
+ switch (pdata->mic_amp_in_sel) {
+ case DA7219_MIC_AMP_IN_SEL_DIFF:
+ case DA7219_MIC_AMP_IN_SEL_SE_P:
+ case DA7219_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_write(codec, DA7219_MIC_1_SELECT,
+ pdata->mic_amp_in_sel);
+ break;
+ }
+ }
+}
+
+static int da7219_probe(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_init(&da7219->lock);
+
+ /* Regulator configuration */
+ ret = da7219_handle_supplies(codec);
+ if (ret)
+ return ret;
+
+ /* Handle DT/Platform data */
+ if (codec->dev->of_node)
+ da7219->pdata = da7219_of_to_pdata(codec);
+ else
+ da7219->pdata = dev_get_platdata(codec->dev);
+
+ da7219_handle_pdata(codec);
+
+ /* Check if MCLK provided */
+ da7219->mclk = devm_clk_get(codec->dev, "mclk");
+ if (IS_ERR(da7219->mclk)) {
+ if (PTR_ERR(da7219->mclk) != -ENOENT)
+ return PTR_ERR(da7219->mclk);
+ else
+ da7219->mclk = NULL;
+ }
+
+ /* Default PC counter to free-running */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Default gain ramping */
+ snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
+ DA7219_ADC_L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
+ DA7219_DAC_L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
+ DA7219_DAC_R_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_RAMP_EN_MASK,
+ DA7219_HP_L_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_RAMP_EN_MASK,
+ DA7219_HP_R_AMP_RAMP_EN_MASK);
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+
+ /* Initialise AAD block */
+ return da7219_aad_init(codec);
+}
+
+static int da7219_remove(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ da7219_aad_exit(codec);
+
+ /* Supplies */
+ return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+}
+
+#ifdef CONFIG_PM
+static int da7219_suspend(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ /* Put device into standby mode if jack detection disabled */
+ if (!da7219->aad->jack)
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0);
+
+ return 0;
+}
+
+static int da7219_resume(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ /* Put device into active mode if previously pushed to standby */
+ if (!da7219->aad->jack)
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE,
+ DA7219_SYSTEM_ACTIVE_MASK);
+
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define da7219_suspend NULL
+#define da7219_resume NULL
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_da7219 = {
+ .probe = da7219_probe,
+ .remove = da7219_remove,
+ .suspend = da7219_suspend,
+ .resume = da7219_resume,
+ .set_bias_level = da7219_set_bias_level,
+
+ .controls = da7219_snd_controls,
+ .num_controls = ARRAY_SIZE(da7219_snd_controls),
+
+ .dapm_widgets = da7219_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
+ .dapm_routes = da7219_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
+};
+
+
+/*
+ * Regmap configs
+ */
+
+static struct reg_default da7219_reg_defaults[] = {
+ { DA7219_MIC_1_SELECT, 0x00 },
+ { DA7219_CIF_TIMEOUT_CTRL, 0x01 },
+ { DA7219_SR_24_48, 0x00 },
+ { DA7219_SR, 0x0A },
+ { DA7219_CIF_I2C_ADDR_CFG, 0x02 },
+ { DA7219_PLL_CTRL, 0x10 },
+ { DA7219_PLL_FRAC_TOP, 0x00 },
+ { DA7219_PLL_FRAC_BOT, 0x00 },
+ { DA7219_PLL_INTEGER, 0x20 },
+ { DA7219_DIG_ROUTING_DAI, 0x10 },
+ { DA7219_DAI_CLK_MODE, 0x01 },
+ { DA7219_DAI_CTRL, 0x28 },
+ { DA7219_DAI_TDM_CTRL, 0x40 },
+ { DA7219_DIG_ROUTING_DAC, 0x32 },
+ { DA7219_DAI_OFFSET_LOWER, 0x00 },
+ { DA7219_DAI_OFFSET_UPPER, 0x00 },
+ { DA7219_REFERENCES, 0x00 },
+ { DA7219_MIXIN_L_SELECT, 0x00 },
+ { DA7219_MIXIN_L_GAIN, 0x03 },
+ { DA7219_ADC_L_GAIN, 0x6F },
+ { DA7219_ADC_FILTERS1, 0x80 },
+ { DA7219_MIC_1_GAIN, 0x01 },
+ { DA7219_SIDETONE_CTRL, 0x40 },
+ { DA7219_SIDETONE_GAIN, 0x0E },
+ { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 },
+ { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 },
+ { DA7219_DAC_FILTERS5, 0x00 },
+ { DA7219_DAC_FILTERS2, 0x88 },
+ { DA7219_DAC_FILTERS3, 0x88 },
+ { DA7219_DAC_FILTERS4, 0x08 },
+ { DA7219_DAC_FILTERS1, 0x80 },
+ { DA7219_DAC_L_GAIN, 0x6F },
+ { DA7219_DAC_R_GAIN, 0x6F },
+ { DA7219_CP_CTRL, 0x20 },
+ { DA7219_HP_L_GAIN, 0x39 },
+ { DA7219_HP_R_GAIN, 0x39 },
+ { DA7219_MIXOUT_L_SELECT, 0x00 },
+ { DA7219_MIXOUT_R_SELECT, 0x00 },
+ { DA7219_MICBIAS_CTRL, 0x03 },
+ { DA7219_MIC_1_CTRL, 0x40 },
+ { DA7219_MIXIN_L_CTRL, 0x40 },
+ { DA7219_ADC_L_CTRL, 0x40 },
+ { DA7219_DAC_L_CTRL, 0x40 },
+ { DA7219_DAC_R_CTRL, 0x40 },
+ { DA7219_HP_L_CTRL, 0x40 },
+ { DA7219_HP_R_CTRL, 0x40 },
+ { DA7219_MIXOUT_L_CTRL, 0x10 },
+ { DA7219_MIXOUT_R_CTRL, 0x10 },
+ { DA7219_CHIP_ID1, 0x23 },
+ { DA7219_CHIP_ID2, 0x93 },
+ { DA7219_CHIP_REVISION, 0x00 },
+ { DA7219_LDO_CTRL, 0x00 },
+ { DA7219_IO_CTRL, 0x00 },
+ { DA7219_GAIN_RAMP_CTRL, 0x00 },
+ { DA7219_PC_COUNT, 0x02 },
+ { DA7219_CP_VOL_THRESHOLD1, 0x0E },
+ { DA7219_DIG_CTRL, 0x00 },
+ { DA7219_ALC_CTRL2, 0x00 },
+ { DA7219_ALC_CTRL3, 0x00 },
+ { DA7219_ALC_NOISE, 0x3F },
+ { DA7219_ALC_TARGET_MIN, 0x3F },
+ { DA7219_ALC_TARGET_MAX, 0x00 },
+ { DA7219_ALC_GAIN_LIMITS, 0xFF },
+ { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7219_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7219_ALC_ANTICLIP_LEVEL, 0x00 },
+ { DA7219_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7219_DAC_NG_OFF_THRESH, 0x00 },
+ { DA7219_DAC_NG_ON_THRESH, 0x00 },
+ { DA7219_DAC_NG_CTRL, 0x00 },
+ { DA7219_TONE_GEN_CFG1, 0x00 },
+ { DA7219_TONE_GEN_CFG2, 0x00 },
+ { DA7219_TONE_GEN_CYCLES, 0x00 },
+ { DA7219_TONE_GEN_FREQ1_L, 0x55 },
+ { DA7219_TONE_GEN_FREQ1_U, 0x15 },
+ { DA7219_TONE_GEN_FREQ2_L, 0x00 },
+ { DA7219_TONE_GEN_FREQ2_U, 0x40 },
+ { DA7219_TONE_GEN_ON_PER, 0x02 },
+ { DA7219_TONE_GEN_OFF_PER, 0x01 },
+ { DA7219_ACCDET_IRQ_MASK_A, 0x00 },
+ { DA7219_ACCDET_IRQ_MASK_B, 0x00 },
+ { DA7219_ACCDET_CONFIG_1, 0xD6 },
+ { DA7219_ACCDET_CONFIG_2, 0x34 },
+ { DA7219_ACCDET_CONFIG_3, 0x0A },
+ { DA7219_ACCDET_CONFIG_4, 0x16 },
+ { DA7219_ACCDET_CONFIG_5, 0x21 },
+ { DA7219_ACCDET_CONFIG_6, 0x3E },
+ { DA7219_ACCDET_CONFIG_7, 0x01 },
+ { DA7219_SYSTEM_ACTIVE, 0x00 },
+};
+
+static bool da7219_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7219_MIC_1_GAIN_STATUS:
+ case DA7219_MIXIN_L_GAIN_STATUS:
+ case DA7219_ADC_L_GAIN_STATUS:
+ case DA7219_DAC_L_GAIN_STATUS:
+ case DA7219_DAC_R_GAIN_STATUS:
+ case DA7219_HP_L_GAIN_STATUS:
+ case DA7219_HP_R_GAIN_STATUS:
+ case DA7219_CIF_CTRL:
+ case DA7219_PLL_SRM_STS:
+ case DA7219_ALC_CTRL1:
+ case DA7219_SYSTEM_MODES_INPUT:
+ case DA7219_SYSTEM_MODES_OUTPUT:
+ case DA7219_ALC_OFFSET_AUTO_M_L:
+ case DA7219_ALC_OFFSET_AUTO_U_L:
+ case DA7219_TONE_GEN_CFG1:
+ case DA7219_ACCDET_STATUS_A:
+ case DA7219_ACCDET_STATUS_B:
+ case DA7219_ACCDET_IRQ_EVENT_A:
+ case DA7219_ACCDET_IRQ_EVENT_B:
+ case DA7219_ACCDET_CONFIG_8:
+ case DA7219_SYSTEM_STATUS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_config da7219_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA7219_SYSTEM_ACTIVE,
+ .reg_defaults = da7219_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults),
+ .volatile_reg = da7219_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+/*
+ * I2C layer
+ */
+
+static int da7219_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da7219_priv *da7219;
+ int ret;
+
+ da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
+ GFP_KERNEL);
+ if (!da7219)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7219);
+
+ da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
+ if (IS_ERR(da7219->regmap)) {
+ ret = PTR_ERR(da7219->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219,
+ &da7219_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static int da7219_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id da7219_i2c_id[] = {
+ { "da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7219_i2c_id);
+
+static struct i2c_driver da7219_i2c_driver = {
+ .driver = {
+ .name = "da7219",
+ .of_match_table = of_match_ptr(da7219_of_match),
+ },
+ .probe = da7219_i2c_probe,
+ .remove = da7219_i2c_remove,
+ .id_table = da7219_i2c_id,
+};
+
+module_i2c_driver(da7219_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7219 Codec Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
new file mode 100644
index 000000000000..b514268c6c56
--- /dev/null
+++ b/sound/soc/codecs/da7219.h
@@ -0,0 +1,820 @@
+/*
+ * da7219.h - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __DA7219_H
+#define __DA7219_H
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7219.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_MIC_1_GAIN_STATUS 0x6
+#define DA7219_MIXIN_L_GAIN_STATUS 0x8
+#define DA7219_ADC_L_GAIN_STATUS 0xA
+#define DA7219_DAC_L_GAIN_STATUS 0xC
+#define DA7219_DAC_R_GAIN_STATUS 0xD
+#define DA7219_HP_L_GAIN_STATUS 0xE
+#define DA7219_HP_R_GAIN_STATUS 0xF
+#define DA7219_MIC_1_SELECT 0x10
+#define DA7219_CIF_TIMEOUT_CTRL 0x12
+#define DA7219_CIF_CTRL 0x13
+#define DA7219_SR_24_48 0x16
+#define DA7219_SR 0x17
+#define DA7219_CIF_I2C_ADDR_CFG 0x1B
+#define DA7219_PLL_CTRL 0x20
+#define DA7219_PLL_FRAC_TOP 0x22
+#define DA7219_PLL_FRAC_BOT 0x23
+#define DA7219_PLL_INTEGER 0x24
+#define DA7219_PLL_SRM_STS 0x25
+#define DA7219_DIG_ROUTING_DAI 0x2A
+#define DA7219_DAI_CLK_MODE 0x2B
+#define DA7219_DAI_CTRL 0x2C
+#define DA7219_DAI_TDM_CTRL 0x2D
+#define DA7219_DIG_ROUTING_DAC 0x2E
+#define DA7219_ALC_CTRL1 0x2F
+#define DA7219_DAI_OFFSET_LOWER 0x30
+#define DA7219_DAI_OFFSET_UPPER 0x31
+#define DA7219_REFERENCES 0x32
+#define DA7219_MIXIN_L_SELECT 0x33
+#define DA7219_MIXIN_L_GAIN 0x34
+#define DA7219_ADC_L_GAIN 0x36
+#define DA7219_ADC_FILTERS1 0x38
+#define DA7219_MIC_1_GAIN 0x39
+#define DA7219_SIDETONE_CTRL 0x3A
+#define DA7219_SIDETONE_GAIN 0x3B
+#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C
+#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D
+#define DA7219_DAC_FILTERS5 0x40
+#define DA7219_DAC_FILTERS2 0x41
+#define DA7219_DAC_FILTERS3 0x42
+#define DA7219_DAC_FILTERS4 0x43
+#define DA7219_DAC_FILTERS1 0x44
+#define DA7219_DAC_L_GAIN 0x45
+#define DA7219_DAC_R_GAIN 0x46
+#define DA7219_CP_CTRL 0x47
+#define DA7219_HP_L_GAIN 0x48
+#define DA7219_HP_R_GAIN 0x49
+#define DA7219_MIXOUT_L_SELECT 0x4B
+#define DA7219_MIXOUT_R_SELECT 0x4C
+#define DA7219_SYSTEM_MODES_INPUT 0x50
+#define DA7219_SYSTEM_MODES_OUTPUT 0x51
+#define DA7219_MICBIAS_CTRL 0x62
+#define DA7219_MIC_1_CTRL 0x63
+#define DA7219_MIXIN_L_CTRL 0x65
+#define DA7219_ADC_L_CTRL 0x67
+#define DA7219_DAC_L_CTRL 0x69
+#define DA7219_DAC_R_CTRL 0x6A
+#define DA7219_HP_L_CTRL 0x6B
+#define DA7219_HP_R_CTRL 0x6C
+#define DA7219_MIXOUT_L_CTRL 0x6E
+#define DA7219_MIXOUT_R_CTRL 0x6F
+#define DA7219_CHIP_ID1 0x81
+#define DA7219_CHIP_ID2 0x82
+#define DA7219_CHIP_REVISION 0x83
+#define DA7219_LDO_CTRL 0x90
+#define DA7219_IO_CTRL 0x91
+#define DA7219_GAIN_RAMP_CTRL 0x92
+#define DA7219_PC_COUNT 0x94
+#define DA7219_CP_VOL_THRESHOLD1 0x95
+#define DA7219_CP_DELAY 0x96
+#define DA7219_DIG_CTRL 0x99
+#define DA7219_ALC_CTRL2 0x9A
+#define DA7219_ALC_CTRL3 0x9B
+#define DA7219_ALC_NOISE 0x9C
+#define DA7219_ALC_TARGET_MIN 0x9D
+#define DA7219_ALC_TARGET_MAX 0x9E
+#define DA7219_ALC_GAIN_LIMITS 0x9F
+#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA7219_ALC_ANTICLIP_CTRL 0xA1
+#define DA7219_ALC_ANTICLIP_LEVEL 0xA2
+#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3
+#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4
+#define DA7219_DAC_NG_SETUP_TIME 0xAF
+#define DA7219_DAC_NG_OFF_THRESH 0xB0
+#define DA7219_DAC_NG_ON_THRESH 0xB1
+#define DA7219_DAC_NG_CTRL 0xB2
+#define DA7219_TONE_GEN_CFG1 0xB4
+#define DA7219_TONE_GEN_CFG2 0xB5
+#define DA7219_TONE_GEN_CYCLES 0xB6
+#define DA7219_TONE_GEN_FREQ1_L 0xB7
+#define DA7219_TONE_GEN_FREQ1_U 0xB8
+#define DA7219_TONE_GEN_FREQ2_L 0xB9
+#define DA7219_TONE_GEN_FREQ2_U 0xBA
+#define DA7219_TONE_GEN_ON_PER 0xBB
+#define DA7219_TONE_GEN_OFF_PER 0xBC
+#define DA7219_SYSTEM_STATUS 0xE0
+#define DA7219_SYSTEM_ACTIVE 0xFD
+
+
+/*
+ * Bit Fields
+ */
+
+#define DA7219_SWITCH_EN_MAX 0x1
+
+/* DA7219_MIC_1_GAIN_STATUS = 0x6 */
+#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0)
+#define DA7219_MIC_1_AMP_GAIN_MAX 0x7
+
+/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0)
+
+/* DA7219_ADC_L_GAIN_STATUS = 0xA */
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_L_GAIN_STATUS = 0xC */
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_R_GAIN_STATUS = 0xD */
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_HP_L_GAIN_STATUS = 0xE */
+#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_HP_R_GAIN_STATUS = 0xF */
+#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_MIC_1_SELECT = 0x10 */
+#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0
+#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */
+#define DA7219_I2C_TIMEOUT_EN_SHIFT 0
+#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7219_CIF_CTRL = 0x13 */
+#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0
+#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0)
+#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7
+#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7)
+
+/* DA7219_SR_24_48 = 0x16 */
+#define DA7219_SR_24_48_SHIFT 0
+#define DA7219_SR_24_48_MASK (0x1 << 0)
+
+/* DA7219_SR = 0x17 */
+#define DA7219_SR_SHIFT 0
+#define DA7219_SR_MASK (0xF << 0)
+#define DA7219_SR_8000 (0x01 << 0)
+#define DA7219_SR_11025 (0x02 << 0)
+#define DA7219_SR_12000 (0x03 << 0)
+#define DA7219_SR_16000 (0x05 << 0)
+#define DA7219_SR_22050 (0x06 << 0)
+#define DA7219_SR_24000 (0x07 << 0)
+#define DA7219_SR_32000 (0x09 << 0)
+#define DA7219_SR_44100 (0x0A << 0)
+#define DA7219_SR_48000 (0x0B << 0)
+#define DA7219_SR_88200 (0x0E << 0)
+#define DA7219_SR_96000 (0x0F << 0)
+
+/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */
+#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0
+#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0)
+
+/* DA7219_PLL_CTRL = 0x20 */
+#define DA7219_PLL_INDIV_SHIFT 2
+#define DA7219_PLL_INDIV_MASK (0x7 << 2)
+#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2)
+#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2)
+#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2)
+#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2)
+#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2)
+#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5
+#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5)
+#define DA7219_PLL_MODE_SHIFT 6
+#define DA7219_PLL_MODE_MASK (0x3 << 6)
+#define DA7219_PLL_MODE_BYPASS (0x0 << 6)
+#define DA7219_PLL_MODE_NORMAL (0x1 << 6)
+#define DA7219_PLL_MODE_SRM (0x2 << 6)
+#define DA7219_PLL_MODE_32KHZ (0x3 << 6)
+
+/* DA7219_PLL_FRAC_TOP = 0x22 */
+#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0)
+
+/* DA7219_PLL_FRAC_BOT = 0x23 */
+#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0)
+
+/* DA7219_PLL_INTEGER = 0x24 */
+#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0
+#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0)
+
+/* DA7219_PLL_SRM_STS = 0x25 */
+#define DA7219_PLL_SRM_STATE_SHIFT 0
+#define DA7219_PLL_SRM_STATE_MASK (0xF << 0)
+#define DA7219_PLL_SRM_STATUS_SHIFT 4
+#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4)
+#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7)
+
+/* DA7219_DIG_ROUTING_DAI = 0x2A */
+#define DA7219_DAI_L_SRC_SHIFT 0
+#define DA7219_DAI_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAI_R_SRC_SHIFT 4
+#define DA7219_DAI_R_SRC_MASK (0x3 << 4)
+#define DA7219_OUT_SRC_MAX 4
+
+/* DA7219_DAI_CLK_MODE = 0x2B */
+#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0
+#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7219_DAI_CLK_POL_SHIFT 2
+#define DA7219_DAI_CLK_POL_MASK (0x1 << 2)
+#define DA7219_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7219_DAI_WCLK_POL_SHIFT 3
+#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3)
+#define DA7219_DAI_WCLK_POL_INV (0x1 << 3)
+#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4
+#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4)
+#define DA7219_DAI_CLK_EN_SHIFT 7
+#define DA7219_DAI_CLK_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_CTRL = 0x2C */
+#define DA7219_DAI_FORMAT_SHIFT 0
+#define DA7219_DAI_FORMAT_MASK (0x3 << 0)
+#define DA7219_DAI_FORMAT_I2S (0x0 << 0)
+#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0)
+#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7219_DAI_FORMAT_DSP (0x3 << 0)
+#define DA7219_DAI_WORD_LENGTH_SHIFT 2
+#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
+#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
+#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
+#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
+#define DA7219_DAI_CH_NUM_SHIFT 4
+#define DA7219_DAI_CH_NUM_MASK (0x3 << 4)
+#define DA7219_DAI_CH_NUM_MAX 2
+#define DA7219_DAI_EN_SHIFT 7
+#define DA7219_DAI_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_TDM_CTRL = 0x2D */
+#define DA7219_DAI_TDM_CH_EN_SHIFT 0
+#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0)
+#define DA7219_DAI_OE_SHIFT 6
+#define DA7219_DAI_OE_MASK (0x1 << 6)
+#define DA7219_DAI_TDM_MODE_EN_SHIFT 7
+#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7)
+#define DA7219_DAI_TDM_MAX_SLOTS 2
+
+/* DA7219_DIG_ROUTING_DAC = 0x2E */
+#define DA7219_DAC_L_SRC_SHIFT 0
+#define DA7219_DAC_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0)
+#define DA7219_DAC_L_MONO_SHIFT 3
+#define DA7219_DAC_L_MONO_MASK (0x1 << 3)
+#define DA7219_DAC_R_SRC_SHIFT 4
+#define DA7219_DAC_R_SRC_MASK (0x3 << 4)
+#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4)
+#define DA7219_DAC_R_MONO_SHIFT 7
+#define DA7219_DAC_R_MONO_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL1 = 0x2F */
+#define DA7219_ALC_OFFSET_EN_SHIFT 0
+#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0)
+#define DA7219_ALC_SYNC_MODE_SHIFT 1
+#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1)
+#define DA7219_ALC_EN_SHIFT 3
+#define DA7219_ALC_EN_MASK (0x1 << 3)
+#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4
+#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4)
+#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5
+#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5)
+
+/* DA7219_DAI_OFFSET_LOWER = 0x30 */
+#define DA7219_DAI_OFFSET_LOWER_SHIFT 0
+#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0)
+
+/* DA7219_DAI_OFFSET_UPPER = 0x31 */
+#define DA7219_DAI_OFFSET_UPPER_SHIFT 0
+#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0)
+#define DA7219_DAI_OFFSET_MAX 0x2FF
+
+/* DA7219_REFERENCES = 0x32 */
+#define DA7219_BIAS_EN_SHIFT 3
+#define DA7219_BIAS_EN_MASK (0x1 << 3)
+#define DA7219_VMID_FAST_CHARGE_SHIFT 4
+#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4)
+
+/* DA7219_MIXIN_L_SELECT = 0x33 */
+#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXIN_L_GAIN = 0x34 */
+#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0)
+#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF
+
+/* DA7219_ADC_L_GAIN = 0x36 */
+#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F
+
+/* DA7219_ADC_FILTERS1 = 0x38 */
+#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_VOICE_HPF_CORNER_MAX 8
+#define DA7219_ADC_VOICE_EN_SHIFT 3
+#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_AUDIO_HPF_CORNER_MAX 4
+#define DA7219_ADC_HPF_EN_SHIFT 7
+#define DA7219_ADC_HPF_EN_MASK (0x1 << 7)
+#define DA7219_HPF_MODE_SHIFT 0
+#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7))
+#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7))
+#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MAX 3
+
+/* DA7219_MIC_1_GAIN = 0x39 */
+#define DA7219_MIC_1_AMP_GAIN_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0)
+
+/* DA7219_SIDETONE_CTRL = 0x3A */
+#define DA7219_SIDETONE_MUTE_EN_SHIFT 6
+#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_SIDETONE_EN_SHIFT 7
+#define DA7219_SIDETONE_EN_MASK (0x1 << 7)
+
+/* DA7219_SIDETONE_GAIN = 0x3B */
+#define DA7219_SIDETONE_GAIN_SHIFT 0
+#define DA7219_SIDETONE_GAIN_MASK (0xF << 0)
+#define DA7219_SIDETONE_GAIN_MAX 0xE
+
+/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */
+#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0
+#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1
+#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2
+#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1)
+
+/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */
+#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0)
+
+/* DA7219_DAC_FILTERS5 = 0x40 */
+#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4
+#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4)
+#define DA7219_DAC_SOFTMUTE_RATE_MAX 7
+#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7
+#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS2 = 0x41 */
+#define DA7219_DAC_EQ_BAND1_SHIFT 0
+#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND2_SHIFT 4
+#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4)
+#define DA7219_DAC_EQ_BAND_MAX 0xF
+
+/* DA7219_DAC_FILTERS3 = 0x42 */
+#define DA7219_DAC_EQ_BAND3_SHIFT 0
+#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND4_SHIFT 4
+#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4)
+
+/* DA7219_DAC_FILTERS4 = 0x43 */
+#define DA7219_DAC_EQ_BAND5_SHIFT 0
+#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0)
+#define DA7219_DAC_EQ_EN_SHIFT 7
+#define DA7219_DAC_EQ_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS1 = 0x44 */
+#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_DAC_VOICE_EN_SHIFT 3
+#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_DAC_HPF_EN_SHIFT 7
+#define DA7219_DAC_HPF_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_GAIN = 0x45 */
+#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F
+#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0)
+
+/* DA7219_DAC_R_GAIN = 0x46 */
+#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7219_CP_CTRL = 0x47 */
+#define DA7219_CP_MCHANGE_SHIFT 4
+#define DA7219_CP_MCHANGE_MASK (0x3 << 4)
+#define DA7219_CP_MCHANGE_REL_MASK 0x3
+#define DA7219_CP_MCHANGE_MAX 3
+#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1
+#define DA7219_CP_MCHANGE_DAC_VOL 0x2
+#define DA7219_CP_MCHANGE_SIG_MAG 0x3
+#define DA7219_CP_EN_SHIFT 7
+#define DA7219_CP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_GAIN = 0x48 */
+#define DA7219_HP_L_AMP_GAIN_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0)
+#define DA7219_HP_AMP_GAIN_MAX 0x3F
+#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0)
+
+/* DA7219_HP_R_GAIN = 0x49 */
+#define DA7219_HP_R_AMP_GAIN_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0)
+
+/* DA7219_MIXOUT_L_SELECT = 0x4B */
+#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXOUT_R_SELECT = 0x4C */
+#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_SYSTEM_MODES_INPUT = 0x50 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_ADC_MODE_SHIFT 1
+#define DA7219_ADC_MODE_MASK (0x7F << 1)
+
+/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_DAC_MODE_SHIFT 1
+#define DA7219_DAC_MODE_MASK (0x7F << 1)
+
+/* DA7219_MICBIAS_CTRL = 0x62 */
+#define DA7219_MICBIAS1_LEVEL_SHIFT 0
+#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0)
+#define DA7219_MICBIAS1_EN_SHIFT 3
+#define DA7219_MICBIAS1_EN_MASK (0x1 << 3)
+
+/* DA7219_MIC_1_CTRL = 0x63 */
+#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIC_1_AMP_EN_SHIFT 7
+#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXIN_L_CTRL = 0x65 */
+#define DA7219_MIXIN_L_MIX_EN_SHIFT 3
+#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3)
+#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIXIN_L_AMP_EN_SHIFT 7
+#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_ADC_L_CTRL = 0x67 */
+#define DA7219_ADC_L_BIAS_SHIFT 0
+#define DA7219_ADC_L_BIAS_MASK (0x3 << 0)
+#define DA7219_ADC_L_RAMP_EN_SHIFT 5
+#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_ADC_L_MUTE_EN_SHIFT 6
+#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_ADC_L_EN_SHIFT 7
+#define DA7219_ADC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_CTRL = 0x69 */
+#define DA7219_DAC_L_RAMP_EN_SHIFT 5
+#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_L_MUTE_EN_SHIFT 6
+#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_L_EN_SHIFT 7
+#define DA7219_DAC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_R_CTRL = 0x6A */
+#define DA7219_DAC_R_RAMP_EN_SHIFT 5
+#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_R_MUTE_EN_SHIFT 6
+#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_R_EN_SHIFT 7
+#define DA7219_DAC_R_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_CTRL = 0x6B */
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_L_AMP_OE_SHIFT 3
+#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_L_AMP_EN_SHIFT 7
+#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_R_CTRL = 0x6C */
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_R_AMP_OE_SHIFT 3
+#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_R_AMP_EN_SHIFT 7
+#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_L_CTRL = 0x6E */
+#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_R_CTRL = 0x6F */
+#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_CHIP_ID1 = 0x81 */
+#define DA7219_CHIP_ID1_SHIFT 0
+#define DA7219_CHIP_ID1_MASK (0xFF << 0)
+
+/* DA7219_CHIP_ID2 = 0x82 */
+#define DA7219_CHIP_ID2_SHIFT 0
+#define DA7219_CHIP_ID2_MASK (0xFF << 0)
+
+/* DA7219_CHIP_REVISION = 0x83 */
+#define DA7219_CHIP_MINOR_SHIFT 0
+#define DA7219_CHIP_MINOR_MASK (0xF << 0)
+#define DA7219_CHIP_MAJOR_SHIFT 4
+#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
+
+/* DA7219_LDO_CTRL = 0x90 */
+#define DA7219_LDO_LEVEL_SELECT_SHIFT 4
+#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4)
+#define DA7219_LDO_EN_SHIFT 7
+#define DA7219_LDO_EN_MASK (0x1 << 7)
+
+/* DA7219_IO_CTRL = 0x91 */
+#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0
+#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
+#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0
+#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1
+
+/* DA7219_GAIN_RAMP_CTRL = 0x92 */
+#define DA7219_GAIN_RAMP_RATE_SHIFT 0
+#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0)
+#define DA7219_GAIN_RAMP_RATE_MAX 4
+
+/* DA7219_PC_COUNT = 0x94 */
+#define DA7219_PC_FREERUN_SHIFT 0
+#define DA7219_PC_FREERUN_MASK (0x1 << 0)
+#define DA7219_PC_RESYNC_AUTO_SHIFT 1
+#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1)
+
+/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */
+#define DA7219_CP_THRESH_VDD2_SHIFT 0
+#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0)
+#define DA7219_CP_THRESH_VDD2_MAX 0x3F
+
+/* DA7219_DIG_CTRL = 0x99 */
+#define DA7219_DAC_L_INV_SHIFT 3
+#define DA7219_DAC_L_INV_MASK (0x1 << 3)
+#define DA7219_DAC_R_INV_SHIFT 7
+#define DA7219_DAC_R_INV_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL2 = 0x9A */
+#define DA7219_ALC_ATTACK_SHIFT 0
+#define DA7219_ALC_ATTACK_MASK (0xF << 0)
+#define DA7219_ALC_ATTACK_MAX 13
+#define DA7219_ALC_RELEASE_SHIFT 4
+#define DA7219_ALC_RELEASE_MASK (0xF << 4)
+#define DA7219_ALC_RELEASE_MAX 11
+
+/* DA7219_ALC_CTRL3 = 0x9B */
+#define DA7219_ALC_HOLD_SHIFT 0
+#define DA7219_ALC_HOLD_MASK (0xF << 0)
+#define DA7219_ALC_HOLD_MAX 16
+#define DA7219_ALC_INTEG_ATTACK_SHIFT 4
+#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4)
+#define DA7219_ALC_INTEG_RELEASE_SHIFT 6
+#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6)
+#define DA7219_ALC_INTEG_MAX 4
+
+/* DA7219_ALC_NOISE = 0x9C */
+#define DA7219_ALC_NOISE_SHIFT 0
+#define DA7219_ALC_NOISE_MASK (0x3F << 0)
+#define DA7219_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7219_ALC_TARGET_MIN = 0x9D */
+#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0)
+
+/* DA7219_ALC_TARGET_MAX = 0x9E */
+#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0)
+
+/* DA7219_ALC_GAIN_LIMITS = 0x9F */
+#define DA7219_ALC_ATTEN_MAX_SHIFT 0
+#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0)
+#define DA7219_ALC_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4)
+#define DA7219_ALC_ATTEN_GAIN_MAX 0xF
+
+/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */
+#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0)
+#define DA7219_ALC_ANA_GAIN_MIN 0x1
+#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4)
+#define DA7219_ALC_ANA_GAIN_MAX 0x7
+
+/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */
+#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0
+#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0)
+#define DA7219_ALC_ANTICLIP_STEP_MAX 4
+#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7
+#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7)
+
+/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */
+#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0
+#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */
+#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */
+#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0)
+
+/* DA7219_DAC_NG_SETUP_TIME = 0xAF */
+#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0)
+#define DA7219_DAC_NG_SETUP_TIME_MAX 4
+#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2)
+#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3)
+#define DA7219_DAC_NG_RAMP_RATE_MAX 2
+
+/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */
+#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0)
+#define DA7219_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7219_DAC_NG_ON_THRESH = 0xB1 */
+#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0)
+
+/* DA7219_DAC_NG_CTRL = 0xB2 */
+#define DA7219_DAC_NG_EN_SHIFT 7
+#define DA7219_DAC_NG_EN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG1 = 0xB4 */
+#define DA7219_DTMF_REG_SHIFT 0
+#define DA7219_DTMF_REG_MASK (0xF << 0)
+#define DA7219_DTMF_REG_MAX 16
+#define DA7219_DTMF_EN_SHIFT 4
+#define DA7219_DTMF_EN_MASK (0x1 << 4)
+#define DA7219_START_STOPN_SHIFT 7
+#define DA7219_START_STOPN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG2 = 0xB5 */
+#define DA7219_SWG_SEL_SHIFT 0
+#define DA7219_SWG_SEL_MASK (0x3 << 0)
+#define DA7219_SWG_SEL_MAX 4
+#define DA7219_SWG_SEL_SRAMP (0x3 << 0)
+#define DA7219_TONE_GEN_GAIN_SHIFT 4
+#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4)
+#define DA7219_TONE_GEN_GAIN_MAX 0xF
+#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4)
+
+/* DA7219_TONE_GEN_CYCLES = 0xB6 */
+#define DA7219_BEEP_CYCLES_SHIFT 0
+#define DA7219_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7219_FREQ1_L_SHIFT 0
+#define DA7219_FREQ1_L_MASK (0xFF << 0)
+#define DA7219_FREQ_MAX 0xFFFF
+
+/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7219_FREQ1_U_SHIFT 0
+#define DA7219_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7219_FREQ2_L_SHIFT 0
+#define DA7219_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7219_FREQ2_U_SHIFT 0
+#define DA7219_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_ON_PER = 0xBB */
+#define DA7219_BEEP_ON_PER_SHIFT 0
+#define DA7219_BEEP_ON_PER_MASK (0x3F << 0)
+#define DA7219_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7219_TONE_GEN_OFF_PER = 0xBC */
+#define DA7219_BEEP_OFF_PER_SHIFT 0
+#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0)
+
+/* DA7219_SYSTEM_STATUS = 0xE0 */
+#define DA7219_SC1_BUSY_SHIFT 0
+#define DA7219_SC1_BUSY_MASK (0x1 << 0)
+#define DA7219_SC2_BUSY_SHIFT 1
+#define DA7219_SC2_BUSY_MASK (0x1 << 1)
+
+/* DA7219_SYSTEM_ACTIVE = 0xFD */
+#define DA7219_SYSTEM_ACTIVE_SHIFT 0
+#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0)
+
+
+/*
+ * General defines & data
+ */
+
+/* Register inversion */
+#define DA7219_NO_INVERT 0
+#define DA7219_INVERT 1
+
+/* Byte related defines */
+#define DA7219_BYTE_SHIFT 8
+#define DA7219_BYTE_MASK 0xFF
+
+/* PLL Output Frequencies */
+#define DA7219_PLL_FREQ_OUT_90316 90316800
+#define DA7219_PLL_FREQ_OUT_98304 98304000
+
+/* PLL Frequency Dividers */
+#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1
+#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2
+#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4
+#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8
+#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16
+
+/* SRM */
+#define DA7219_SRM_CHECK_RETRIES 8
+
+enum da7219_clk_src {
+ DA7219_CLKSRC_MCLK = 0,
+ DA7219_CLKSRC_MCLK_SQR,
+};
+
+enum da7219_sys_clk {
+ DA7219_SYSCLK_MCLK = 0,
+ DA7219_SYSCLK_PLL,
+ DA7219_SYSCLK_PLL_SRM,
+ DA7219_SYSCLK_PLL_32KHZ
+};
+
+/* Regulators */
+enum da7219_supplies {
+ DA7219_SUPPLY_VDD = 0,
+ DA7219_SUPPLY_VDDMIC,
+ DA7219_SUPPLY_VDDIO,
+ DA7219_NUM_SUPPLIES,
+};
+
+struct da7219_aad_priv;
+
+/* Private data */
+struct da7219_priv {
+ struct da7219_aad_priv *aad;
+ struct da7219_pdata *pdata;
+
+ struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct mutex lock;
+
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ int clk_src;
+
+ bool master;
+ bool alc_en;
+};
+
+#endif /* __DA7219_H */
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 6a091016e0fc..969e337dc17c 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
int ret;
if (deemph > 1)
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
deleted file mode 100644
index bd42ad34e004..000000000000
--- a/sound/soc/codecs/hdmi.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * ALSA SoC codec driver for HDMI audio codecs.
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Ricardo Neri <ricardo.neri@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#define DRV_NAME "hdmi-audio-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 |
- 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 | SNDRV_PCM_FMTBIT_S32_LE,
- .sig_bits = 24,
- },
- .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,
- },
-
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id hdmi_audio_codec_ids[] = {
- { .compatible = "linux,hdmi-audio", },
- { }
-};
-MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
-#endif
-
-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),
- .ignore_pmdown_time = true,
-};
-
-static int hdmi_codec_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
- &hdmi_codec_dai, 1);
-}
-
-static int hdmi_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver hdmi_codec_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(hdmi_audio_codec_ids),
- },
-
- .probe = hdmi_codec_probe,
- .remove = hdmi_codec_remove,
-};
-
-module_platform_driver(hdmi_codec_driver);
-
-MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
new file mode 100644
index 000000000000..7fc7b4e3f444
--- /dev/null
+++ b/sound/soc/codecs/nau8825.c
@@ -0,0 +1,1309 @@
+/*
+ * Nuvoton NAU8825 audio codec driver
+ *
+ * Copyright 2015 Google Chromium project.
+ * Author: Anatol Pomozov <anatol@chromium.org>
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+
+#include "nau8825.h"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+struct nau8825_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8825_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8825_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+ { 48, 0xd },
+ { 96, 0xe },
+ { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8825_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8825_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+static const struct reg_default nau8825_reg_defaults[] = {
+ { NAU8825_REG_ENA_CTRL, 0x00ff },
+ { NAU8825_REG_CLK_DIVIDER, 0x0050 },
+ { NAU8825_REG_FLL1, 0x0 },
+ { NAU8825_REG_FLL2, 0x3126 },
+ { NAU8825_REG_FLL3, 0x0008 },
+ { NAU8825_REG_FLL4, 0x0010 },
+ { NAU8825_REG_FLL5, 0x0 },
+ { NAU8825_REG_FLL6, 0x6000 },
+ { NAU8825_REG_FLL_VCO_RSV, 0xf13c },
+ { NAU8825_REG_HSD_CTRL, 0x000c },
+ { NAU8825_REG_JACK_DET_CTRL, 0x0 },
+ { NAU8825_REG_INTERRUPT_MASK, 0x0 },
+ { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff },
+ { NAU8825_REG_SAR_CTRL, 0x0015 },
+ { NAU8825_REG_KEYDET_CTRL, 0x0110 },
+ { NAU8825_REG_VDET_THRESHOLD_1, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_2, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_3, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_4, 0x0 },
+ { NAU8825_REG_GPIO34_CTRL, 0x0 },
+ { NAU8825_REG_GPIO12_CTRL, 0x0 },
+ { NAU8825_REG_TDM_CTRL, 0x0 },
+ { NAU8825_REG_I2S_PCM_CTRL1, 0x000b },
+ { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 },
+ { NAU8825_REG_LEFT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_BIQ_CTRL, 0x0 },
+ { NAU8825_REG_BIQ_COF1, 0x0 },
+ { NAU8825_REG_BIQ_COF2, 0x0 },
+ { NAU8825_REG_BIQ_COF3, 0x0 },
+ { NAU8825_REG_BIQ_COF4, 0x0 },
+ { NAU8825_REG_BIQ_COF5, 0x0 },
+ { NAU8825_REG_BIQ_COF6, 0x0 },
+ { NAU8825_REG_BIQ_COF7, 0x0 },
+ { NAU8825_REG_BIQ_COF8, 0x0 },
+ { NAU8825_REG_BIQ_COF9, 0x0 },
+ { NAU8825_REG_BIQ_COF10, 0x0 },
+ { NAU8825_REG_ADC_RATE, 0x0010 },
+ { NAU8825_REG_DAC_CTRL1, 0x0001 },
+ { NAU8825_REG_DAC_CTRL2, 0x0 },
+ { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 },
+ { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
+ { NAU8825_REG_MUTE_CTRL, 0x0 },
+ { NAU8825_REG_HSVOL_CTRL, 0x0 },
+ { NAU8825_REG_DACL_CTRL, 0x02cf },
+ { NAU8825_REG_DACR_CTRL, 0x00cf },
+ { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff },
+ { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 },
+ { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_IMM_MODE_CTRL, 0x0 },
+ { NAU8825_REG_CLASSG_CTRL, 0x0 },
+ { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
+ { NAU8825_REG_MISC_CTRL, 0x0 },
+ { NAU8825_REG_BIAS_ADJ, 0x0 },
+ { NAU8825_REG_TRIM_SETTINGS, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_2, 0x0 },
+ { NAU8825_REG_ANALOG_ADC_1, 0x0011 },
+ { NAU8825_REG_ANALOG_ADC_2, 0x0020 },
+ { NAU8825_REG_RDAC, 0x0008 },
+ { NAU8825_REG_MIC_BIAS, 0x0006 },
+ { NAU8825_REG_BOOST, 0x0 },
+ { NAU8825_REG_FEPGA, 0x0 },
+ { NAU8825_REG_POWER_UP_CONTROL, 0x0 },
+ { NAU8825_REG_CHARGE_PUMP, 0x0 },
+};
+
+static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_ENA_CTRL:
+ case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
+ case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK:
+ case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET:
+ case NAU8825_REG_IRQ_STATUS:
+ case NAU8825_REG_INT_CLR_KEY_STATUS:
+ case NAU8825_REG_IMM_RMS_L:
+ case NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_I2C_DEVICE_ID:
+ case NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
+ case NAU8825_REG_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(10);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char * const nau8825_adc_decimation[] = {
+ "32", "64", "128", "256"
+};
+
+static const struct soc_enum nau8825_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT,
+ ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation);
+
+static const char * const nau8825_dac_oversampl[] = {
+ "64", "256", "128", "", "32"
+};
+
+static const struct soc_enum nau8825_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT,
+ ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400);
+static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400);
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+ SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 0, 0xff, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 12, 8, 0x0f, 0, sidetone_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL,
+ 6, 0, 0x3f, 1, dac_vol_tlv),
+ SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL,
+ 8, 37, 0, fepga_gain_tlv),
+ SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL,
+ 0, 8, 0xff, 0, crosstalk_vol_tlv),
+
+ SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+};
+
+/* DAC Mux 0x33[9] and 0x34[9] */
+static const char * const nau8825_dac_src[] = {
+ "DACL", "DACR",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacl_enum, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacr_enum, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src);
+
+static const struct snd_kcontrol_new nau8825_dacl_mux =
+ SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum);
+
+static const struct snd_kcontrol_new nau8825_dacr_mux =
+ SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2,
+ 15, 1),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
+
+ SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
+ 0),
+
+ /* ADC for button press detection */
+ SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_ADC_EN_SFT, 0),
+
+ SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
+ SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
+ SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR_SFT, 0),
+ SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACL_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
+ SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
+
+ SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
+ nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA("Output Driver R Stage 1",
+ NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver L Stage 1",
+ NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver R Stage 2",
+ NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver L Stage 2",
+ NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1,
+ NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1,
+ NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
+ {"Frontend PGA", NULL, "MIC"},
+ {"ADC", NULL, "Frontend PGA"},
+ {"ADC", NULL, "ADC Clock"},
+ {"ADC", NULL, "ADC Power"},
+ {"AIFTX", NULL, "ADC"},
+
+ {"DDACL", NULL, "Playback"},
+ {"DDACR", NULL, "Playback"},
+ {"DDACL", NULL, "DDAC Clock"},
+ {"DDACR", NULL, "DDAC Clock"},
+ {"DACL Mux", "DACL", "DDACL"},
+ {"DACL Mux", "DACR", "DDACR"},
+ {"DACR Mux", "DACL", "DDACL"},
+ {"DACR Mux", "DACR", "DDACR"},
+ {"HP amp L", NULL, "DACL Mux"},
+ {"HP amp R", NULL, "DACR Mux"},
+ {"HP amp L", NULL, "HP amp power"},
+ {"HP amp R", NULL, "HP amp power"},
+ {"ADACL", NULL, "HP amp L"},
+ {"ADACR", NULL, "HP amp R"},
+ {"ADACL", NULL, "ADACL Clock"},
+ {"ADACR", NULL, "ADACR Clock"},
+ {"Output Driver L Stage 1", NULL, "ADACL"},
+ {"Output Driver R Stage 1", NULL, "ADACR"},
+ {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+ {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+ {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+ {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+ {"Output DACL", NULL, "Output Driver L Stage 3"},
+ {"Output DACR", NULL, "Output Driver R Stage 3"},
+ {"HPOL", NULL, "Output DACL"},
+ {"HPOR", NULL, "Output DACR"},
+ {"HPOL", NULL, "Charge Pump"},
+ {"HPOR", NULL, "Charge Pump"},
+};
+
+static int nau8825_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 nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8825_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8825_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8825_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8825_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8825_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8825_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8825_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8825_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8825_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8825_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK |
+ NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
+ ctrl1_val);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops nau8825_dai_ops = {
+ .hw_params = nau8825_hw_params,
+ .set_fmt = nau8825_set_dai_fmt,
+};
+
+#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8825_dai = {
+ .name = "nau8825-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .ops = &nau8825_dai_ops,
+};
+
+/**
+ * nau8825_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct regmap *regmap = nau8825->regmap;
+
+ nau8825->jack = jack;
+
+ /* Ground HP Outputs[1:0], needed for headset auto detection
+ * Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
+ */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
+
+
+static bool nau8825_is_jack_inserted(struct regmap *regmap)
+{
+ int status;
+
+ regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
+ return !(status & NAU8825_GPIO2JD1);
+}
+
+static void nau8825_restart_jack_detection(struct regmap *regmap)
+{
+ /* this will restart the entire jack detection process including MIC/GND
+ * switching and create interrupts. We have to go from 0 to 1 and back
+ * to 0 to restart.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, 0);
+}
+
+static void nau8825_eject_jack(struct nau8825 *nau8825)
+{
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ struct regmap *regmap = nau8825->regmap;
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ /* ground HPL/HPR, MICGRND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+static int nau8825_button_decode(int value)
+{
+ int buttons = 0;
+
+ /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */
+ if (value & BIT(0))
+ buttons |= SND_JACK_BTN_0;
+ if (value & BIT(1))
+ buttons |= SND_JACK_BTN_1;
+ if (value & BIT(2))
+ buttons |= SND_JACK_BTN_2;
+ if (value & BIT(3))
+ buttons |= SND_JACK_BTN_3;
+ if (value & BIT(4))
+ buttons |= SND_JACK_BTN_4;
+ if (value & BIT(5))
+ buttons |= SND_JACK_BTN_5;
+
+ return buttons;
+}
+
+static int nau8825_jack_insert(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ int jack_status_reg, mic_detected;
+ int type = 0;
+
+ regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
+ mic_detected = (jack_status_reg >> 10) & 3;
+
+ switch (mic_detected) {
+ case 0:
+ /* no mic */
+ type = SND_JACK_HEADPHONE;
+ break;
+ case 1:
+ dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 1 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ /* Attach SARADC to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKR2);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ case 2:
+ case 3:
+ dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 2 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ /* Attach SARADC to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKSLV);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ }
+
+ if (type & SND_JACK_HEADPHONE) {
+ /* Unground HPL/R */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
+ }
+
+ return type;
+}
+
+#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8825_interrupt(int irq, void *data)
+{
+ struct nau8825 *nau8825 = (struct nau8825 *)data;
+ struct regmap *regmap = nau8825->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+
+ if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) ==
+ NAU8825_JACK_EJECTION_DETECTED) {
+
+ nau8825_eject_jack(nau8825);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK;
+ } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) {
+ int key_status;
+
+ regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS,
+ &key_status);
+
+ /* upper 8 bits of the register are for short pressed keys,
+ * lower 8 bits - for long pressed buttons
+ */
+ nau8825->button_pressed = nau8825_button_decode(
+ key_status >> 8);
+
+ event |= nau8825->button_pressed;
+ event_mask |= NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) {
+ event_mask = NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_RELEASE_IRQ;
+ } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
+ if (nau8825_is_jack_inserted(regmap)) {
+ event |= nau8825_jack_insert(nau8825);
+ } else {
+ dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
+ nau8825_eject_jack(nau8825);
+ }
+
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8825->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static void nau8825_setup_buttons(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_TRACKING_GAIN_MASK,
+ nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_COMPARE_TIME_MASK,
+ nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_SAMPLING_TIME_MASK,
+ nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT);
+
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_LEVELS_NR_MASK,
+ (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_HYSTERESIS_MASK,
+ nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK,
+ nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT);
+
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1,
+ (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2,
+ (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3,
+ (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4,
+ (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]);
+
+ /* Enable short press and release interruptions */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN,
+ 0);
+}
+
+static void nau8825_init_regs(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Enable Bias/Vmid */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN);
+
+ /* VMID Tieoff */
+ regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID_SEL_MASK,
+ nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8825_REG_BOOST,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+ NAU8825_SHORT_SHUTDOWN_EN,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+ NAU8825_SHORT_SHUTDOWN_EN);
+
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_OUTPUT_EN,
+ nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_EN,
+ nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_UP,
+ nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_POLARITY,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY);
+
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_INSERT_DEBOUNCE_MASK,
+ nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_EJECT_DEBOUNCE_MASK,
+ nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT);
+
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff);
+
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage);
+
+ if (nau8825->sar_threshold_num)
+ nau8825_setup_buttons(nau8825);
+
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_ADC_RATE,
+ NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
+ regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+ NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
+}
+
+static const struct regmap_config nau8825_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 16,
+
+ .max_register = NAU8825_REG_MAX,
+ .readable_reg = nau8825_readable_reg,
+ .writeable_reg = nau8825_writeable_reg,
+ .volatile_reg = nau8825_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8825_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults),
+};
+
+static int nau8825_codec_probe(struct snd_soc_codec *codec)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ nau8825->dapm = dapm;
+
+ /* The interrupt clock is gated by x1[10:8],
+ * one of them needs to be enabled all the time for
+ * interrupts to happen.
+ */
+ snd_soc_dapm_force_enable_pin(dapm, "DDACR");
+ snd_soc_dapm_sync(dapm);
+
+ /* Unmask interruptions. Handler uses dapm object so we can enable
+ * interruptions only after dapm is fully initialized.
+ */
+ regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+ nau8825_restart_jack_detection(nau8825->regmap);
+
+ return 0;
+}
+
+/**
+ * nau8825_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
+ struct nau8825_fll *fll_param)
+{
+ u64 fvco;
+ unsigned int fref, i;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(mclk_src_scaling))
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[i].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8825_fll_apply(struct nau8825 *nau8825,
+ struct nau8825_fll *fll_param)
+{
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK, fll_param->ratio);
+ /* FLL 16-bit fractional input */
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
+ NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
+ /* select divided VCO input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_FILTER_SW_MASK, 0x0000);
+ /* FLL sigma delta modulator enable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825_fll fll_param;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8825_fll_apply(nau8825, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ return 0;
+}
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
+ unsigned int freq)
+{
+ struct regmap *regmap = nau8825->regmap;
+ int ret;
+
+ switch (clk_id) {
+ case NAU8825_CLK_MCLK:
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+
+ /* We selected MCLK source but the clock itself managed externally */
+ if (!nau8825->mclk)
+ break;
+
+ if (!nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ if (nau8825->mclk_freq != freq) {
+ nau8825->mclk_freq = freq;
+
+ freq = clk_round_rate(nau8825->mclk, freq);
+ ret = clk_set_rate(nau8825->mclk, freq);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to set mclk rate\n");
+ return ret;
+ }
+ }
+
+ break;
+ case NAU8825_CLK_INTERNAL:
+ regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+ NAU8825_DCO_EN);
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
+ default:
+ dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ clk_id);
+ return 0;
+}
+
+static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+ return nau8825_configure_sysclk(nau8825, clk_id, freq);
+}
+
+static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ ret = regcache_sync(nau8825->regmap);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ if (nau8825->mclk_freq)
+ clk_disable_unprepare(nau8825->mclk);
+
+ regcache_mark_dirty(nau8825->regmap);
+ break;
+ }
+ return 0;
+}
+
+static struct snd_soc_codec_driver nau8825_codec_driver = {
+ .probe = nau8825_codec_probe,
+ .set_sysclk = nau8825_set_sysclk,
+ .set_pll = nau8825_set_pll,
+ .set_bias_level = nau8825_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = nau8825_controls,
+ .num_controls = ARRAY_SIZE(nau8825_controls),
+ .dapm_widgets = nau8825_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+ .dapm_routes = nau8825_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+};
+
+static void nau8825_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+}
+
+static void nau8825_print_device_properties(struct nau8825 *nau8825)
+{
+ int i;
+ struct device *dev = nau8825->dev;
+
+ dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable);
+ dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable);
+ dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up);
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance);
+
+ dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num);
+ for (i = 0; i < nau8825->sar_threshold_num; i++)
+ dev_dbg(dev, "sar-threshold[%d]=%d\n", i,
+ nau8825->sar_threshold[i]);
+
+ dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis);
+ dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage);
+ dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time);
+ dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time);
+ dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce);
+ dev_dbg(dev, "jack-insert-debounce: %d\n",
+ nau8825->jack_insert_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8825->jack_eject_debounce);
+}
+
+static int nau8825_read_device_properties(struct device *dev,
+ struct nau8825 *nau8825) {
+
+ nau8825->jkdet_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-enable");
+ nau8825->jkdet_pull_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-enable");
+ nau8825->jkdet_pull_up = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-up");
+ device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8825->jkdet_polarity);
+ device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8825->micbias_voltage);
+ device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8825->vref_impedance);
+ device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ &nau8825->sar_threshold_num);
+ device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ nau8825->sar_threshold, nau8825->sar_threshold_num);
+ device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ &nau8825->sar_hysteresis);
+ device_property_read_u32(dev, "nuvoton,sar-voltage",
+ &nau8825->sar_voltage);
+ device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ &nau8825->sar_compare_time);
+ device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ &nau8825->sar_sampling_time);
+ device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ &nau8825->key_debounce);
+ device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ &nau8825->jack_insert_debounce);
+ device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8825->jack_eject_debounce);
+
+ nau8825->mclk = devm_clk_get(dev, "mclk");
+ if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+ /* The MCLK is managed externally or not used at all */
+ nau8825->mclk = NULL;
+ dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
+ } else if (IS_ERR(nau8825->mclk)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_setup_irq(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ int ret;
+
+ /* IRQ Output Enable */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
+
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+
+ /* Enable DDACR needed for interrupts
+ * It is the same as force_enable_pin("DDACR") we do later
+ */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR);
+
+ /* Chip needs one FSCLK cycle in order to generate interrupts,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+ ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
+ nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8825", nau8825);
+
+ if (ret) {
+ dev_err(nau8825->dev, "Cannot request irq %d (%d)\n",
+ nau8825->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nau8825_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
+ int ret, value;
+
+ if (!nau8825) {
+ nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL);
+ if (!nau8825)
+ return -ENOMEM;
+ ret = nau8825_read_device_properties(dev, nau8825);
+ if (ret)
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, nau8825);
+
+ nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config);
+ if (IS_ERR(nau8825->regmap))
+ return PTR_ERR(nau8825->regmap);
+ nau8825->dev = dev;
+ nau8825->irq = i2c->irq;
+
+ nau8825_print_device_properties(nau8825);
+
+ nau8825_reset_chip(nau8825->regmap);
+ ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU8825: %d\n",
+ ret);
+ return ret;
+ }
+ if ((value & NAU8825_SOFTWARE_ID_MASK) !=
+ NAU8825_SOFTWARE_ID_NAU8825) {
+ dev_err(dev, "Not a NAU8825 chip\n");
+ return -ENODEV;
+ }
+
+ nau8825_init_regs(nau8825);
+
+ if (i2c->irq)
+ nau8825_setup_irq(nau8825);
+
+ return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver,
+ &nau8825_dai, 1);
+}
+
+static int nau8825_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id nau8825_i2c_ids[] = {
+ { "nau8825", 0 },
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8825_of_ids[] = {
+ { .compatible = "nuvoton,nau8825", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8825_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8825_acpi_match[] = {
+ { "10508825", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match);
+#endif
+
+static struct i2c_driver nau8825_driver = {
+ .driver = {
+ .name = "nau8825",
+ .of_match_table = of_match_ptr(nau8825_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+ },
+ .probe = nau8825_i2c_probe,
+ .remove = nau8825_i2c_remove,
+ .id_table = nau8825_i2c_ids,
+};
+module_i2c_driver(nau8825_driver);
+
+MODULE_DESCRIPTION("ASoC nau8825 driver");
+MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
new file mode 100644
index 000000000000..dff8edb83bfd
--- /dev/null
+++ b/sound/soc/codecs/nau8825.h
@@ -0,0 +1,341 @@
+/*
+ * NAU8825 ALSA SoC audio driver
+ *
+ * Copyright 2015 Google Inc.
+ * Author: Anatol Pomozov <anatol.pomozov@chrominium.org>
+ *
+ * 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 __NAU8825_H__
+#define __NAU8825_H__
+
+#define NAU8825_REG_RESET 0x00
+#define NAU8825_REG_ENA_CTRL 0x01
+#define NAU8825_REG_CLK_DIVIDER 0x03
+#define NAU8825_REG_FLL1 0x04
+#define NAU8825_REG_FLL2 0x05
+#define NAU8825_REG_FLL3 0x06
+#define NAU8825_REG_FLL4 0x07
+#define NAU8825_REG_FLL5 0x08
+#define NAU8825_REG_FLL6 0x09
+#define NAU8825_REG_FLL_VCO_RSV 0x0a
+#define NAU8825_REG_HSD_CTRL 0x0c
+#define NAU8825_REG_JACK_DET_CTRL 0x0d
+#define NAU8825_REG_INTERRUPT_MASK 0x0f
+#define NAU8825_REG_IRQ_STATUS 0x10
+#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11
+#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12
+#define NAU8825_REG_SAR_CTRL 0x13
+#define NAU8825_REG_KEYDET_CTRL 0x14
+#define NAU8825_REG_VDET_THRESHOLD_1 0x15
+#define NAU8825_REG_VDET_THRESHOLD_2 0x16
+#define NAU8825_REG_VDET_THRESHOLD_3 0x17
+#define NAU8825_REG_VDET_THRESHOLD_4 0x18
+#define NAU8825_REG_GPIO34_CTRL 0x19
+#define NAU8825_REG_GPIO12_CTRL 0x1a
+#define NAU8825_REG_TDM_CTRL 0x1b
+#define NAU8825_REG_I2S_PCM_CTRL1 0x1c
+#define NAU8825_REG_I2S_PCM_CTRL2 0x1d
+#define NAU8825_REG_LEFT_TIME_SLOT 0x1e
+#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f
+#define NAU8825_REG_BIQ_CTRL 0x20
+#define NAU8825_REG_BIQ_COF1 0x21
+#define NAU8825_REG_BIQ_COF2 0x22
+#define NAU8825_REG_BIQ_COF3 0x23
+#define NAU8825_REG_BIQ_COF4 0x24
+#define NAU8825_REG_BIQ_COF5 0x25
+#define NAU8825_REG_BIQ_COF6 0x26
+#define NAU8825_REG_BIQ_COF7 0x27
+#define NAU8825_REG_BIQ_COF8 0x28
+#define NAU8825_REG_BIQ_COF9 0x29
+#define NAU8825_REG_BIQ_COF10 0x2a
+#define NAU8825_REG_ADC_RATE 0x2b
+#define NAU8825_REG_DAC_CTRL1 0x2c
+#define NAU8825_REG_DAC_CTRL2 0x2d
+#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f
+#define NAU8825_REG_ADC_DGAIN_CTRL 0x30
+#define NAU8825_REG_MUTE_CTRL 0x31
+#define NAU8825_REG_HSVOL_CTRL 0x32
+#define NAU8825_REG_DACL_CTRL 0x33
+#define NAU8825_REG_DACR_CTRL 0x34
+#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38
+#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39
+#define NAU8825_REG_ADC_DRC_SLOPES 0x3a
+#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b
+#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45
+#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46
+#define NAU8825_REG_DAC_DRC_SLOPES 0x47
+#define NAU8825_REG_DAC_DRC_ATKDCY 0x48
+#define NAU8825_REG_IMM_MODE_CTRL 0x4c
+#define NAU8825_REG_IMM_RMS_L 0x4d
+#define NAU8825_REG_IMM_RMS_R 0x4e
+#define NAU8825_REG_CLASSG_CTRL 0x50
+#define NAU8825_REG_OPT_EFUSE_CTRL 0x51
+#define NAU8825_REG_MISC_CTRL 0x55
+#define NAU8825_REG_I2C_DEVICE_ID 0x58
+#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59
+#define NAU8825_REG_BIAS_ADJ 0x66
+#define NAU8825_REG_TRIM_SETTINGS 0x68
+#define NAU8825_REG_ANALOG_CONTROL_1 0x69
+#define NAU8825_REG_ANALOG_CONTROL_2 0x6a
+#define NAU8825_REG_ANALOG_ADC_1 0x71
+#define NAU8825_REG_ANALOG_ADC_2 0x72
+#define NAU8825_REG_RDAC 0x73
+#define NAU8825_REG_MIC_BIAS 0x74
+#define NAU8825_REG_BOOST 0x76
+#define NAU8825_REG_FEPGA 0x77
+#define NAU8825_REG_POWER_UP_CONTROL 0x7f
+#define NAU8825_REG_CHARGE_PUMP 0x80
+#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
+#define NAU8825_REG_GENERAL_STATUS 0x82
+#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
+
+/* ENA_CTRL (0x1) */
+#define NAU8825_ENABLE_DACR_SFT 10
+#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
+#define NAU8825_ENABLE_DACL_SFT 9
+#define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_SAR_SFT 1
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8825_CLK_SRC_SFT 15
+#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0)
+
+/* FLL1 (0x04) */
+#define NAU8825_FLL_RATIO_MASK (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
+
+/* FLL4 (0x07) */
+#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
+
+/* FLL5 (0x08) */
+#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
+
+/* FLL6 (0x9) */
+#define NAU8825_DCO_EN_MASK (0x1 << 15)
+#define NAU8825_DCO_EN (0x1 << 15)
+#define NAU8825_DCO_DIS (0x0 << 15)
+#define NAU8825_SDM_EN_MASK (0x1 << 14)
+#define NAU8825_SDM_EN (0x1 << 14)
+#define NAU8825_SDM_DIS (0x0 << 14)
+
+/* HSD_CTRL (0xc) */
+#define NAU8825_HSD_AUTO_MODE (1 << 6)
+/* 0 - short to GND, 1 - open */
+#define NAU8825_SPKR_DWN1R (1 << 1)
+#define NAU8825_SPKR_DWN1L (1 << 0)
+
+/* JACK_DET_CTRL (0xd) */
+#define NAU8825_JACK_DET_RESTART (1 << 9)
+#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
+#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
+#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0xf) */
+#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
+#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
+#define NAU8825_IRQ_EJECT_EN (1 << 2)
+
+/* IRQ_STATUS (0x10) */
+#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
+#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9)
+#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8)
+#define NAU8825_KEY_IRQ_MASK (0x7 << 5)
+#define NAU8825_KEY_RELEASE_IRQ (1 << 7)
+#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6)
+#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5)
+#define NAU8825_MIC_DETECTION_IRQ (1 << 4)
+#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2)
+#define NAU8825_JACK_EJECTION_DETECTED (1 << 2)
+#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0)
+#define NAU8825_JACK_INSERTION_DETECTED (1 << 0)
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
+#define NAU8825_IRQ_EJECT_DIS (1 << 2)
+
+/* SAR_CTRL (0x13) */
+#define NAU8825_SAR_ADC_EN_SFT 12
+#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT)
+#define NAU8825_SAR_INPUT_MASK (1 << 11)
+#define NAU8825_SAR_INPUT_JKSLV (1 << 11)
+#define NAU8825_SAR_INPUT_JKR2 (0 << 11)
+#define NAU8825_SAR_TRACKING_GAIN_SFT 8
+#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT)
+#define NAU8825_SAR_COMPARE_TIME_SFT 2
+#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2)
+#define NAU8825_SAR_SAMPLING_TIME_SFT 0
+#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* KEYDET_CTRL (0x14) */
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8825_KEYDET_LEVELS_NR_SFT 8
+#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8)
+#define NAU8825_KEYDET_HYSTERESIS_SFT 0
+#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8825_I2S_BP_SFT 7
+#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_PCMB_SFT 6
+#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_DL_SFT 2
+#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DF_SFT 0
+#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT)
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_MS_SFT 3
+#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8825_ADC_SYNC_DOWN_SFT 0
+#define NAU8825_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8825_ADC_SYNC_DOWN_32 0
+#define NAU8825_ADC_SYNC_DOWN_64 1
+#define NAU8825_ADC_SYNC_DOWN_128 2
+#define NAU8825_ADC_SYNC_DOWN_256 3
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8825_DAC_CLIP_OFF (1 << 7)
+#define NAU8825_DAC_OVERSAMPLE_SFT 0
+#define NAU8825_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8825_DAC_OVERSAMPLE_64 0
+#define NAU8825_DAC_OVERSAMPLE_256 1
+#define NAU8825_DAC_OVERSAMPLE_128 2
+#define NAU8825_DAC_OVERSAMPLE_32 4
+
+/* MUTE_CTRL (0x31) */
+#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
+#define NAU8825_DAC_SOFT_MUTE (1 << 9)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8825_HP_MUTE (1 << 15)
+
+/* DACL_CTRL (0x33) */
+#define NAU8825_DACL_CH_SEL_SFT 9
+
+/* DACR_CTRL (0x34) */
+#define NAU8825_DACR_CH_SEL_SFT 9
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8825_GPIO2JD1 (1 << 7)
+#define NAU8825_SOFTWARE_ID_MASK 0x3
+#define NAU8825_SOFTWARE_ID_NAU8825 0x0
+
+/* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_VMID (1 << 6)
+#define NAU8825_BIAS_VMID_SEL_SFT 4
+#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12)
+#define NAU8825_DAC_CAPACITOR_MSB (1 << 1)
+#define NAU8825_DAC_CAPACITOR_LSB (1 << 0)
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8)
+#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8)
+#define NAU8825_ADC_VREFSEL_VMID (1 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
+#define NAU8825_POWERUP_ADCL (1 << 6)
+
+/* MIC_BIAS (0x74) */
+#define NAU8825_MICBIAS_JKSLV (1 << 14)
+#define NAU8825_MICBIAS_JKR2 (1 << 12)
+#define NAU8825_MICBIAS_POWERUP_SFT 8
+#define NAU8825_MICBIAS_VOLTAGE_SFT 0
+#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8825_PRECHARGE_DIS (1 << 13)
+#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_HP_BOOST_G_DIS (1 << 8)
+#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8825_POWERUP_INTEGR_R (1 << 5)
+#define NAU8825_POWERUP_INTEGR_L (1 << 4)
+#define NAU8825_POWERUP_DRV_IN_R (1 << 3)
+#define NAU8825_POWERUP_DRV_IN_L (1 << 2)
+#define NAU8825_POWERUP_HP_DRV_R (1 << 1)
+#define NAU8825_POWERUP_HP_DRV_L (1 << 0)
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8825_JAMNODCLOW (1 << 10)
+#define NAU8825_POWER_DOWN_DACR (1 << 9)
+#define NAU8825_POWER_DOWN_DACL (1 << 8)
+#define NAU8825_CHANRGE_PUMP_EN (1 << 5)
+
+
+/* System Clock Source */
+enum {
+ NAU8825_CLK_MCLK = 0,
+ NAU8825_CLK_INTERNAL,
+};
+
+struct nau8825 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct clk *mclk;
+ int irq;
+ int mclk_freq; /* 0 - mclk is disabled */
+ int button_pressed;
+ int micbias_voltage;
+ int vref_impedance;
+ bool jkdet_enable;
+ bool jkdet_pull_enable;
+ bool jkdet_pull_up;
+ int jkdet_polarity;
+ int sar_threshold_num;
+ int sar_threshold[8];
+ int sar_hysteresis;
+ int sar_voltage;
+ int sar_compare_time;
+ int sar_sampling_time;
+ int key_debounce;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+};
+
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
+
+
+#endif /* __NAU8825_H__ */
diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c
index 91d5166bd3a1..a4b910efbd45 100644
--- a/sound/soc/codecs/rl6347a.c
+++ b/sound/soc/codecs/rl6347a.c
@@ -11,25 +11,8 @@
*/
#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
-#include <linux/dmi.h>
-#include <linux/acpi.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/jack.h>
-#include <linux/workqueue.h>
-#include <sound/hda_verbs.h>
+#include <linux/regmap.h>
#include "rl6347a.h"
diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h
index 1cb56e50b7f3..e127919cb36b 100644
--- a/sound/soc/codecs/rl6347a.h
+++ b/sound/soc/codecs/rl6347a.h
@@ -12,6 +12,8 @@
#ifndef __RL6347A_H__
#define __RL6347A_H__
+#include <sound/hda_verbs.h>
+
#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
#define RL6347A_VENDOR_REGISTERS 0x20
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index bd9365885f73..af2ed774b552 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -29,7 +29,6 @@
#include <sound/jack.h>
#include <linux/workqueue.h>
#include <sound/rt286.h>
-#include <sound/hda_verbs.h>
#include "rl6347a.h"
#include "rt286.h"
@@ -38,7 +37,7 @@
#define RT288_VENDOR_ID 0x10ec0288
struct rt286_priv {
- const struct reg_default *index_cache;
+ struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
struct snd_soc_codec *codec;
@@ -1161,7 +1160,11 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
return -ENODEV;
}
- rt286->index_cache = rt286_index_def;
+ rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def,
+ sizeof(rt286_index_def), GFP_KERNEL);
+ if (!rt286->index_cache)
+ return -ENOMEM;
+
rt286->index_cache_size = INDEX_CACHE_SIZE;
rt286->i2c = i2c;
i2c_set_clientdata(i2c, rt286);
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index f823eb502367..b3f795c60749 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -28,7 +28,6 @@
#include <sound/jack.h>
#include <linux/workqueue.h>
#include <sound/rt298.h>
-#include <sound/hda_verbs.h>
#include "rl6347a.h"
#include "rt298.h"
@@ -49,7 +48,7 @@ struct rt298_priv {
int is_hp_in;
};
-static struct reg_default rt298_index_def[] = {
+static const struct reg_default rt298_index_def[] = {
{ 0x01, 0xa5a8 },
{ 0x02, 0x8e95 },
{ 0x03, 0x0002 },
@@ -129,7 +128,7 @@ static bool rt298_volatile_register(struct device *dev, unsigned int reg)
case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0):
return true;
default:
- return true;
+ return false;
}
@@ -1165,7 +1164,11 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
return -ENODEV;
}
- rt298->index_cache = rt298_index_def;
+ rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def,
+ sizeof(rt298_index_def), GFP_KERNEL);
+ if (!rt298->index_cache)
+ return -ENOMEM;
+
rt298->index_cache_size = INDEX_CACHE_SIZE;
rt298->i2c = i2c;
i2c_set_clientdata(i2c, rt298);
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index e1ceeb885f7d..f2beb1aa5763 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -405,11 +405,14 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
175, 0, dac_vol_tlv),
- /* IN1/IN2 Control */
+ /* IN1/IN2/IN3 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2,
RT5640_BST_SFT1, 8, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4,
RT5640_BST_SFT2, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2,
+ RT5640_BST_SFT2, 8, 0, bst_tlv),
+
/* INL/INR Volume Control */
SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL,
RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT,
@@ -598,6 +601,8 @@ static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
RT5640_M_HP_L_RM_L_SFT, 1, 1),
SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER,
RT5640_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST2_RM_L_SFT, 1, 1),
SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER,
RT5640_M_BST4_RM_L_SFT, 1, 1),
SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER,
@@ -611,6 +616,8 @@ static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
RT5640_M_HP_R_RM_R_SFT, 1, 1),
SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER,
RT5640_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST2_RM_R_SFT, 1, 1),
SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER,
RT5640_M_BST4_RM_R_SFT, 1, 1),
SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER,
@@ -1065,6 +1072,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1N"),
SND_SOC_DAPM_INPUT("IN2P"),
SND_SOC_DAPM_INPUT("IN2N"),
+ SND_SOC_DAPM_INPUT("IN3P"),
+ SND_SOC_DAPM_INPUT("IN3N"),
SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -1081,6 +1090,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
RT5640_PWR_BST1_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2,
RT5640_PWR_BST4_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST2_BIT, 0, NULL, 0),
/* Input Volume */
SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL,
RT5640_PWR_IN_L_BIT, 0, NULL, 0),
@@ -1310,6 +1321,7 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"IN1P", NULL, "LDO2"},
{"IN2P", NULL, "LDO2"},
+ {"IN3P", NULL, "LDO2"},
{"DMIC L1", NULL, "DMIC1"},
{"DMIC R1", NULL, "DMIC1"},
@@ -1320,18 +1332,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"BST1", NULL, "IN1N"},
{"BST2", NULL, "IN2P"},
{"BST2", NULL, "IN2N"},
+ {"BST3", NULL, "IN3P"},
+ {"BST3", NULL, "IN3N"},
{"INL VOL", NULL, "IN2P"},
{"INR VOL", NULL, "IN2N"},
{"RECMIXL", "HPOL Switch", "HPOL"},
{"RECMIXL", "INL Switch", "INL VOL"},
+ {"RECMIXL", "BST3 Switch", "BST3"},
{"RECMIXL", "BST2 Switch", "BST2"},
{"RECMIXL", "BST1 Switch", "BST1"},
{"RECMIXL", "OUT MIXL Switch", "OUT MIXL"},
{"RECMIXR", "HPOR Switch", "HPOR"},
{"RECMIXR", "INR Switch", "INR VOL"},
+ {"RECMIXR", "BST3 Switch", "BST3"},
{"RECMIXR", "BST2 Switch", "BST2"},
{"RECMIXR", "BST1 Switch", "BST1"},
{"RECMIXR", "OUT MIXR Switch", "OUT MIXR"},
@@ -2260,6 +2276,10 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
RT5640_IN_DF2, RT5640_IN_DF2);
+ if (rt5640->pdata.in3_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
rt5640->hp_mute = 1;
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 5c101af0ac63..28132375e427 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -42,6 +42,8 @@
#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
+#define RT5645_HWEQ_NUM 57
+
static const struct regmap_range_cfg rt5645_ranges[] = {
{
.name = "PR",
@@ -224,6 +226,11 @@ static const struct reg_default rt5645_reg[] = {
{ 0xff, 0x6308 },
};
+struct rt5645_eq_param_s {
+ unsigned short reg;
+ unsigned short val;
+};
+
static const char *const rt5645_supply_names[] = {
"avdd",
"cpvdd",
@@ -240,6 +247,7 @@ struct rt5645_priv {
struct snd_soc_jack *btn_jack;
struct delayed_work jack_detect_work;
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
+ struct rt5645_eq_param_s *eq_param;
int codec_type;
int sysclk;
@@ -469,6 +477,94 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
);
+/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */
+static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv,
+ 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0)
+);
+
+static int rt5645_hweq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s);
+
+ return 0;
+}
+
+static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s *eq_param =
+ (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg);
+ eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val);
+ }
+
+ return 0;
+}
+
+static bool rt5645_validate_hweq(unsigned short reg)
+{
+ if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) |
+ (reg == RT5645_EQ_CTRL2))
+ return true;
+
+ return false;
+}
+
+static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s *eq_param =
+ (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
+ eq_param[i].val = be16_to_cpu(eq_param[i].val);
+ }
+
+ /* The final setting of the table should be RT5645_EQ_CTRL2 */
+ for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) {
+ if (eq_param[i].reg == 0)
+ continue;
+ else if (eq_param[i].reg != RT5645_EQ_CTRL2)
+ return 0;
+ else
+ break;
+ }
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (!rt5645_validate_hweq(eq_param[i].reg) &&
+ eq_param[i].reg != 0)
+ return 0;
+ else if (eq_param[i].reg == 0)
+ break;
+ }
+
+ memcpy(rt5645->eq_param, eq_param,
+ RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s));
+
+ return 0;
+}
+
+#define RT5645_HWEQ(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt5645_hweq_info, \
+ .get = rt5645_hweq_get, \
+ .put = rt5645_hweq_put \
+}
+
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -476,6 +572,10 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* ClassD modulator Speaker Gain Ratio */
+ SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO,
+ RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv),
+
/* Headphone Output Volume */
SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL,
RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
@@ -529,6 +629,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* I2S2 function select */
SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
1, 1),
+ RT5645_HWEQ("Speaker HWEQ"),
};
/**
@@ -619,6 +720,22 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
}
+static int rt5645_enable_hweq(struct snd_soc_codec *codec)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (rt5645_validate_hweq(rt5645->eq_param[i].reg))
+ regmap_write(rt5645->regmap, rt5645->eq_param[i].reg,
+ rt5645->eq_param[i].val);
+ else
+ break;
+ }
+
+ return 0;
+}
+
/**
* rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
* @codec: SoC audio codec device.
@@ -1523,6 +1640,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ rt5645_enable_hweq(codec);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L,
@@ -1531,6 +1649,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L, 0);
@@ -2733,6 +2852,10 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
+ if (rt5645->en_button_func &&
+ snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work, msecs_to_jiffies(0));
break;
case SND_SOC_BIAS_OFF:
@@ -2829,6 +2952,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_sync(dapm);
rt5645->jack_type = SND_JACK_HEADPHONE;
}
+ if (rt5645->pdata.jd_invert)
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
} else { /* jack out */
rt5645->jack_type = 0;
@@ -2847,6 +2973,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_disable_pin(dapm, "LDO2");
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
+ if (rt5645->pdata.jd_invert)
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
}
return rt5645->jack_type;
@@ -3038,6 +3167,9 @@ static int rt5645_probe(struct snd_soc_codec *codec)
snd_soc_dapm_sync(dapm);
}
+ rt5645->eq_param = devm_kzalloc(codec->dev,
+ RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
+
return 0;
}
@@ -3098,7 +3230,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 4,
.rates = RT5645_STEREO_RATES,
.formats = RT5645_FORMATS,
},
@@ -3209,9 +3341,42 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"),
},
},
+ {
+ .ident = "Google Reks",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
+ },
+ },
{ }
};
+static struct rt5645_platform_data buddy_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 3,
+ .jd_invert = true,
+};
+
+static int buddy_quirk_cb(const struct dmi_system_id *id)
+{
+ rt5645_pdata = &buddy_platform_data;
+
+ return 1;
+}
+
+static struct dmi_system_id dmi_platform_intel_broadwell[] = {
+ {
+ .ident = "Chrome Buddy",
+ .callback = buddy_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
+ },
+ },
+ { }
+};
+
+
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
{
rt5645->pdata.in2_diff = device_property_read_bool(dev,
@@ -3244,7 +3409,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5645->pdata = *pdata;
- else if (dmi_check_system(dmi_platform_intel_braswell))
+ else if (dmi_check_system(dmi_platform_intel_braswell) ||
+ dmi_check_system(dmi_platform_intel_broadwell))
rt5645->pdata = *rt5645_pdata;
else
rt5645_parse_dt(rt5645, &i2c->dev);
@@ -3472,6 +3638,8 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
RT5645_CBJ_MN_JD);
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN,
0);
+ msleep(20);
+ regmap_write(rt5645->regmap, RT5645_RESET, 0);
}
static struct i2c_driver rt5645_i2c_driver = {
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 8c964cfb120d..093e46d559fb 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -621,14 +621,14 @@
#define RT5645_G_OM_L_SM_L_SFT 6
#define RT5645_M_BST1_L_SM_L (0x1 << 5)
#define RT5645_M_BST1_L_SM_L_SFT 5
+#define RT5645_M_BST3_L_SM_L (0x1 << 4)
+#define RT5645_M_BST3_L_SM_L_SFT 4
#define RT5645_M_IN_L_SM_L (0x1 << 3)
#define RT5645_M_IN_L_SM_L_SFT 3
-#define RT5645_M_DAC_L1_SM_L (0x1 << 1)
-#define RT5645_M_DAC_L1_SM_L_SFT 1
#define RT5645_M_DAC_L2_SM_L (0x1 << 2)
#define RT5645_M_DAC_L2_SM_L_SFT 2
-#define RT5645_M_BST3_L_SM_L (0x1 << 4)
-#define RT5645_M_BST3_L_SM_L_SFT 4
+#define RT5645_M_DAC_L1_SM_L (0x1 << 1)
+#define RT5645_M_DAC_L1_SM_L_SFT 1
/* SPK Right Mixer Control (0x47) */
#define RT5645_G_RM_R_SM_R_MASK (0x3 << 14)
@@ -643,14 +643,14 @@
#define RT5645_G_OM_R_SM_R_SFT 6
#define RT5645_M_BST2_R_SM_R (0x1 << 5)
#define RT5645_M_BST2_R_SM_R_SFT 5
+#define RT5645_M_BST3_R_SM_R (0x1 << 4)
+#define RT5645_M_BST3_R_SM_R_SFT 4
#define RT5645_M_IN_R_SM_R (0x1 << 3)
#define RT5645_M_IN_R_SM_R_SFT 3
-#define RT5645_M_DAC_R1_SM_R (0x1 << 1)
-#define RT5645_M_DAC_R1_SM_R_SFT 1
#define RT5645_M_DAC_R2_SM_R (0x1 << 2)
#define RT5645_M_DAC_R2_SM_R_SFT 2
-#define RT5645_M_BST3_R_SM_R (0x1 << 4)
-#define RT5645_M_BST3_R_SM_R_SFT 4
+#define RT5645_M_DAC_R1_SM_R (0x1 << 1)
+#define RT5645_M_DAC_R1_SM_R_SFT 1
/* SPOLMIX Control (0x48) */
#define RT5645_M_DAC_L1_SPM_L (0x1 << 15)
@@ -670,13 +670,17 @@
#define RT5645_M_SV_R_SPM_R (0x1 << 0)
#define RT5645_M_SV_R_SPM_R_SFT 0
+/* SPOMIX Ratio Control (0x4a) */
+#define RT5645_SPK_G_CLSD_MASK (0x7 << 0)
+#define RT5645_SPK_G_CLSD_SFT 0
+
/* Mono Output Mixer Control (0x4c) */
+#define RT5645_G_MONOMIX_MASK (0x1 << 10)
+#define RT5645_G_MONOMIX_SFT 10
#define RT5645_M_OV_L_MM (0x1 << 9)
#define RT5645_M_OV_L_MM_SFT 9
#define RT5645_M_DAC_L2_MA (0x1 << 8)
#define RT5645_M_DAC_L2_MA_SFT 8
-#define RT5645_G_MONOMIX_MASK (0x1 << 10)
-#define RT5645_G_MONOMIX_SFT 10
#define RT5645_M_BST2_MM (0x1 << 4)
#define RT5645_M_BST2_MM_SFT 4
#define RT5645_M_DAC_R1_MM (0x1 << 3)
@@ -779,8 +783,6 @@
#define RT5645_PWR_CLS_D_R_BIT 9
#define RT5645_PWR_CLS_D_L (0x1 << 8)
#define RT5645_PWR_CLS_D_L_BIT 8
-#define RT5645_PWR_ADC_R (0x1 << 1)
-#define RT5645_PWR_ADC_R_BIT 1
#define RT5645_PWR_DAC_L2 (0x1 << 7)
#define RT5645_PWR_DAC_L2_BIT 7
#define RT5645_PWR_DAC_R2 (0x1 << 6)
@@ -1628,6 +1630,10 @@
#define RT5645_OT_P_NOR (0x0 << 10)
#define RT5645_OT_P_INV (0x1 << 10)
#define RT5645_IRQ_JD_1_1_EN (0x1 << 9)
+#define RT5645_JD_1_1_MASK (0x1 << 7)
+#define RT5645_JD_1_1_SFT 7
+#define RT5645_JD_1_1_NOR (0x0 << 7)
+#define RT5645_JD_1_1_INV (0x1 << 7)
/* IRQ Control 2 (0xbe) */
#define RT5645_IRQ_MB1_OC_MASK (0x1 << 15)
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index ddb0203fc649..86b81a60ac52 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -723,17 +723,11 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = {
.num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
};
-static bool ssm2518_register_volatile(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
static const struct regmap_config ssm2518_regmap_config = {
.val_bits = 8,
.reg_bits = 8,
.max_register = SSM2518_REG_DRC_9,
- .volatile_reg = ssm2518_register_volatile,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = ssm2518_reg_defaults,
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 8739126a1f6f..a564759845f9 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -80,6 +80,7 @@ struct aic3x_priv {
unsigned int sysclk;
unsigned int dai_fmt;
unsigned int tdm_delay;
+ unsigned int slot_width;
struct list_head list;
int master;
int gpio_reset;
@@ -1025,10 +1026,14 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
int clk;
+ int width = aic3x->slot_width;
+
+ if (!width)
+ width = params_width(params);
/* select data word length */
data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
- switch (params_width(params)) {
+ switch (width) {
case 16:
break;
case 20:
@@ -1170,12 +1175,16 @@ static int aic3x_prepare(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int delay = 0;
+ int width = aic3x->slot_width;
+
+ if (!width)
+ width = substream->runtime->sample_bits;
/* TDM slot selection only valid in DSP_A/_B mode */
if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
- delay += (aic3x->tdm_delay + 1);
+ delay += (aic3x->tdm_delay*width + 1);
else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
- delay += aic3x->tdm_delay;
+ delay += aic3x->tdm_delay*width;
/* Configure data delay */
snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
@@ -1296,7 +1305,20 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- aic3x->tdm_delay = lsb * slot_width;
+ switch (slot_width) {
+ case 16:
+ case 20:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported slot width %d\n", slot_width);
+ return -EINVAL;
+ }
+
+
+ aic3x->tdm_delay = lsb;
+ aic3x->slot_width = slot_width;
/* DOUT in high-impedance on inactive bit clocks */
snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 2713e1845cbc..a5a4e9f75c57 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -1612,19 +1612,16 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
return;
/* Set the constraints according to the already configured stream */
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- twl4030->rate,
twl4030->rate);
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- twl4030->sample_bits,
twl4030->sample_bits);
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- twl4030->channels,
twl4030->channels);
}
@@ -1669,9 +1666,9 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
/* In option2 4 channel is not supported, set the
* constraint for the first stream for channels, the
* second stream will 'inherit' this cosntraint */
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- 2, 2);
+ 2);
}
twl4030->master_substream = substream;
}
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index e19026380534..e4c694c758b8 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -150,14 +150,12 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
master_runtime->sample_bits,
master_runtime->rate);
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- master_runtime->rate,
master_runtime->rate);
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- master_runtime->sample_bits,
master_runtime->sample_bits);
uda134x->slave_substream = substream;
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 80fb1dc81f6c..7693c1129bab 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -307,11 +307,10 @@ static int wl1273_startup(struct snd_pcm_substream *substream,
switch (wl1273->mode) {
case WL1273_MODE_BT:
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- 8000, 8000);
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 8000);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 1);
break;
case WL1273_MODE_FM_RX:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index 786abd02b140..a67ea10f41a1 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -620,7 +620,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- int anc_active = ucontrol->value.integer.value[0];
+ unsigned int anc_active = ucontrol->value.integer.value[0];
int ret;
if (anc_active > 1)
@@ -653,7 +653,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- int val = ucontrol->value.integer.value[0];
+ unsigned int val = ucontrol->value.integer.value[0];
int ret;
if (val > 1)
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 9756578fc752..c04c0bc6f58a 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -38,6 +38,12 @@
struct wm5110_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
+
+ unsigned int in_value;
+ int in_pre_pending;
+ int in_post_pending;
+
+ unsigned int in_pga_cache[6];
};
static const struct wm_adsp_region wm5110_dsp1_regions[] = {
@@ -428,6 +434,127 @@ err:
return ret;
}
+static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_card *card = dapm->card;
+ int ret;
+
+ /*
+ * PGA Volume is also used as part of the enable sequence, so
+ * usage of it should be avoided whilst that is running.
+ */
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+
+ mutex_unlock(&card->dapm_mutex);
+
+ return ret;
+}
+
+static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_card *card = dapm->card;
+ int ret;
+
+ /*
+ * PGA Volume is also used as part of the enable sequence, so
+ * usage of it should be avoided whilst that is running.
+ */
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+
+ mutex_unlock(&card->dapm_mutex);
+
+ return ret;
+}
+
+static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ unsigned int reg, mask;
+ struct reg_sequence analog_seq[] = {
+ { 0x80, 0x3 },
+ { 0x35d, 0 },
+ { 0x80, 0x0 },
+ };
+
+ reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4);
+ mask = ARIZONA_IN1L_PGA_VOL_MASK;
+
+ switch (event) {
+ case SND_SOC_DAPM_WILL_PMU:
+ wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2);
+ wm5110->in_pre_pending++;
+ wm5110->in_post_pending++;
+ return 0;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+
+ snd_soc_update_bits(codec, reg, mask,
+ 0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
+
+ wm5110->in_pre_pending--;
+ if (wm5110->in_pre_pending == 0) {
+ analog_seq[1].def = wm5110->in_value;
+ regmap_multi_reg_write_bypassed(arizona->regmap,
+ analog_seq,
+ ARRAY_SIZE(analog_seq));
+
+ msleep(55);
+
+ wm5110->in_value = 0;
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, reg, mask,
+ wm5110->in_pga_cache[w->shift]);
+
+ wm5110->in_post_pending--;
+ if (wm5110->in_post_pending == 0)
+ regmap_multi_reg_write_bypassed(arizona->regmap,
+ analog_seq,
+ ARRAY_SIZE(analog_seq));
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wm5110_in_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ switch (arizona->rev) {
+ case 0 ... 4:
+ if (arizona_input_analog(codec, w->shift))
+ wm5110_in_analog_ev(w, kcontrol, event);
+
+ break;
+ default:
+ break;
+ }
+
+ return arizona_in_ev(w, kcontrol, event);
+}
+
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);
@@ -454,18 +581,24 @@ 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),
-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_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
- ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
- ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
@@ -896,29 +1029,35 @@ 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,
+ 0, NULL, 0, wm5110_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_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_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_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_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_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_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_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_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_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_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_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index ace8645245a0..07cf1bd7913a 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -132,7 +132,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
int ret = 0;
if (deemph > 1)
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index b011253459af..e4cc41e6c23e 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -452,7 +452,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
int ret = 0;
if (deemph > 1)
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index b783743dc97e..2aa23f1b9e3c 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -534,7 +534,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 12e4435f00f8..9db00d53abe7 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -402,7 +402,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index dbd88408861a..056375339ea3 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -201,7 +201,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
new file mode 100644
index 000000000000..8782dfb628ab
--- /dev/null
+++ b/sound/soc/codecs/wm8998.c
@@ -0,0 +1,1430 @@
+/*
+ * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@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 "wm8998.h"
+
+struct wm8998_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = snd_soc_read(codec, ARIZONA_ASRC_RATE1);
+ val &= ARIZONA_ASRC_RATE1_MASK;
+ val >>= ARIZONA_ASRC_RATE1_SHIFT;
+
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ val = snd_soc_read(codec,
+ ARIZONA_SAMPLE_RATE_1 + val);
+ if (val >= 0x11) {
+ dev_warn(codec->dev,
+ "Unsupported ASRC rate1 (%s)\n",
+ arizona_sample_rate_val_to_name(val));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->dev,
+ "Illegal ASRC rate1 selector (0x%x)\n",
+ val);
+ return -EINVAL;
+ }
+
+ val = snd_soc_read(codec, ARIZONA_ASRC_RATE2);
+ val &= ARIZONA_ASRC_RATE2_MASK;
+ val >>= ARIZONA_ASRC_RATE2_SHIFT;
+
+ switch (val) {
+ case 8:
+ case 9:
+ val -= 0x8;
+ val = snd_soc_read(codec,
+ ARIZONA_ASYNC_SAMPLE_RATE_1 + val);
+ if (val >= 0x11) {
+ dev_warn(codec->dev,
+ "Unsupported ASRC rate2 (%s)\n",
+ arizona_sample_rate_val_to_name(val));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->dev,
+ "Illegal ASRC rate2 selector (0x%x)\n",
+ val);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = wm8998->core.arizona;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, inmode;
+ unsigned int mode_val, src_val;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ /* L and R registers have same shift and mask */
+ inmode = arizona->pdata.inmode[2 * mux];
+ src_val = mux << ARIZONA_IN1L_SRC_SHIFT;
+ if (inmode & ARIZONA_INMODE_SE)
+ src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT;
+
+ switch (arizona->pdata.inmode[0]) {
+ case ARIZONA_INMODE_DMIC:
+ if (mux)
+ mode_val = 0; /* B always analogue */
+ else
+ mode_val = 1 << ARIZONA_IN1_MODE_SHIFT;
+
+ snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1_MODE_MASK, mode_val);
+
+ /* IN1A is digital so L and R must change together */
+ /* src_val setting same for both registers */
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK, src_val);
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_SRC_MASK |
+ ARIZONA_IN1R_SRC_SE_MASK, src_val);
+ break;
+ default:
+ /* both analogue */
+ snd_soc_update_bits(codec,
+ e->reg,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK,
+ src_val);
+ break;
+ }
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = wm8998->core.arizona;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, inmode, src_val, mode_val;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ inmode = arizona->pdata.inmode[1 + (2 * mux)];
+ if (inmode & ARIZONA_INMODE_DMIC)
+ mode_val = 1 << ARIZONA_IN2_MODE_SHIFT;
+ else
+ mode_val = 0;
+
+ src_val = mux << ARIZONA_IN2L_SRC_SHIFT;
+ if (inmode & ARIZONA_INMODE_SE)
+ src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT;
+
+ snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2_MODE_MASK, mode_val);
+
+ snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK,
+ src_val);
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static const char * const wm8998_inmux_texts[] = {
+ "A",
+ "B",
+};
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const struct snd_kcontrol_new wm8998_in1mux[2] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+};
+
+static const struct snd_kcontrol_new wm8998_in2mux =
+ SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in2mux_put);
+
+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(ng_tlv, -10200, 600, 0);
+
+#define WM8998_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUTL Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUTR Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG LINEOUTL Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG LINEOUTR Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0)
+
+static const struct snd_kcontrol_new wm8998_snd_controls[] = {
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+
+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("IN2 Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_HPF_SHIFT, 1, 0),
+
+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("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_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_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0),
+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),
+
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0),
+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),
+
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0),
+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),
+
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0),
+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_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_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),
+
+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_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),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
+SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
+SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+
+ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_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_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+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),
+
+WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L),
+WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R),
+WM8998_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8998_NG_SRC("SPKDATR", 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("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_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(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(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATR, 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(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_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 * const wm8998_aec_loopback_texts[] = {
+ "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT",
+ "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR",
+};
+
+static const unsigned int wm8998_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 6, 7, 8, 9,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
+ ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ wm8998_aec_loopback_texts,
+ wm8998_aec_loopback_values);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
+ ARIZONA_DAC_AEC_CONTROL_2,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ wm8998_aec_loopback_texts,
+ wm8998_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+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("DBVDD3", 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("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1AL"),
+SND_SOC_DAPM_INPUT("IN1AR"),
+SND_SOC_DAPM_INPUT("IN1BL"),
+SND_SOC_DAPM_INPUT("IN1BR"),
+SND_SOC_DAPM_INPUT("IN2A"),
+SND_SOC_DAPM_INPUT("IN2B"),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]),
+SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux),
+
+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 |
+ 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("IN2 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_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_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB1_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("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_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+
+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("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT3_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("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC3_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_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[0]),
+
+SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[1]),
+
+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_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_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_OUT("AIF2TX3", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_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_IN("AIF2RX3", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_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_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("AIF3TX1", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+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("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3", 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),
+
+SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+ARIZONA_MUX_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MUX_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MUX_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MUX_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MUX_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(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"),
+ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"),
+
+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(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"),
+
+ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"),
+ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"),
+
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDATL"),
+SND_SOC_DAPM_OUTPUT("SPKDATR"),
+SND_SOC_DAPM_OUTPUT("SPDIF"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2 PGA" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { 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, "ASRC1L", "ASRC1L" }, \
+ { name, "ASRC1R", "ASRC1R" }, \
+ { name, "ASRC2L", "ASRC2L" }, \
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT2L", NULL, "CPVDD" },
+ { "OUT2R", NULL, "CPVDD" },
+ { "OUT3", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDDL" },
+ { "OUT4R", NULL, "SPKVDDR" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "IN1AL", NULL, "SYSCLK" },
+ { "IN1AR", NULL, "SYSCLK" },
+ { "IN1BL", NULL, "SYSCLK" },
+ { "IN1BR", NULL, "SYSCLK" },
+ { "IN2A", NULL, "SYSCLK" },
+ { "IN2B", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "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" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+
+ { "SLIMRX3", NULL, "Slim2 Playback" },
+ { "SLIMRX4", NULL, "Slim2 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+
+ { "IN1L Mux", "A", "IN1AL" },
+ { "IN1R Mux", "A", "IN1AR" },
+ { "IN1L Mux", "B", "IN1BL" },
+ { "IN1R Mux", "B", "IN1BR" },
+
+ { "IN2 Mux", "A", "IN2A" },
+ { "IN2 Mux", "B", "IN2B" },
+
+ { "IN1L PGA", NULL, "IN1L Mux" },
+ { "IN1R PGA", NULL, "IN1R Mux" },
+ { "IN2 PGA", NULL, "IN2 Mux" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT3", "EPOUT"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"),
+
+ 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("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+
+ ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"),
+
+ ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"),
+ ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"),
+
+ ARIZONA_MUX_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MUX_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MUX_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MUX_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC1 Loopback", "HPOUTL", "OUT1L" },
+ { "AEC1 Loopback", "HPOUTR", "OUT1R" },
+ { "AEC2 Loopback", "HPOUTL", "OUT1L" },
+ { "AEC2 Loopback", "HPOUTR", "OUT1R" },
+ { "HPOUTL", NULL, "OUT1L" },
+ { "HPOUTR", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "LINEOUTL", "OUT2L" },
+ { "AEC1 Loopback", "LINEOUTR", "OUT2R" },
+ { "AEC2 Loopback", "LINEOUTL", "OUT2L" },
+ { "AEC2 Loopback", "LINEOUTR", "OUT2R" },
+ { "LINEOUTL", NULL, "OUT2L" },
+ { "LINEOUTR", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "EPOUT", "OUT3" },
+ { "AEC2 Loopback", "EPOUT", "OUT3" },
+ { "EPOUT", NULL, "OUT3" },
+
+ { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTLN", NULL, "OUT4L" },
+ { "SPKOUTLP", NULL, "OUT4L" },
+
+ { "AEC1 Loopback", "SPKOUTR", "OUT4R" },
+ { "AEC2 Loopback", "SPKOUTR", "OUT4R" },
+ { "SPKOUTRN", NULL, "OUT4R" },
+ { "SPKOUTRP", NULL, "OUT4R" },
+
+ { "SPDIF", NULL, "SPD1" },
+
+ { "AEC1 Loopback", "SPKDATL", "OUT5L" },
+ { "AEC1 Loopback", "SPKDATR", "OUT5R" },
+ { "AEC2 Loopback", "SPKDATL", "OUT5L" },
+ { "AEC2 Loopback", "SPKDATR", "OUT5R" },
+ { "SPKDATL", NULL, "OUT5L" },
+ { "SPKDATR", NULL, "OUT5R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+};
+
+#define WM8998_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8998_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 wm8998_dai[] = {
+ {
+ .name = "wm8998-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-aif3",
+ .id = 3,
+ .base = ARIZONA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8998-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM8998_FLL1:
+ return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout);
+ case WM8998_FLL2:
+ return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout);
+ case WM8998_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref,
+ Fout);
+ case WM8998_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int wm8998_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ priv->core.arizona->dapm = dapm;
+
+ arizona_init_spk(codec);
+ arizona_init_gpio(codec);
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ return 0;
+}
+
+static int wm8998_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM8998_DIG_VU 0x0200
+
+static unsigned int wm8998_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct regmap *wm8998_get_regmap(struct device *dev)
+{
+ struct wm8998_priv *priv = dev_get_drvdata(dev);
+
+ return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
+ .probe = wm8998_codec_probe,
+ .remove = wm8998_codec_remove,
+ .get_regmap = wm8998_get_regmap,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8998_set_fll,
+
+ .controls = wm8998_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8998_snd_controls),
+ .dapm_widgets = wm8998_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets),
+ .dapm_routes = wm8998_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes),
+};
+
+static int wm8998_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm8998_priv *wm8998;
+ int i;
+
+ wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
+ GFP_KERNEL);
+ if (!wm8998)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm8998);
+
+ wm8998->core.arizona = arizona;
+ wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */
+
+ for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++)
+ wm8998->fll[i].vco_mult = 1;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm8998->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm8998->fll[1]);
+
+ for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++)
+ arizona_init_dai(&wm8998->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm8998_digital_vu[i],
+ WM8998_DIG_VU, WM8998_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
+ wm8998_dai, ARRAY_SIZE(wm8998_dai));
+}
+
+static int wm8998_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm8998_codec_driver = {
+ .driver = {
+ .name = "wm8998-codec",
+ },
+ .probe = wm8998_probe,
+ .remove = wm8998_remove,
+};
+
+module_platform_driver(wm8998_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8998 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:wm8998-codec");
diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h
new file mode 100644
index 000000000000..1e8647252162
--- /dev/null
+++ b/sound/soc/codecs/wm8998.h
@@ -0,0 +1,23 @@
+/*
+ * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@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 _WM8998_H
+#define _WM8998_H
+
+#include "arizona.h"
+
+#define WM8998_FLL1 1
+#define WM8998_FLL2 2
+#define WM8998_FLL1_REFCLK 3
+#define WM8998_FLL2_REFCLK 4
+
+#endif
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 7d45d98a861f..4495a40a9468 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -80,12 +80,13 @@ struct davinci_mcasp {
/* McASP specific data */
int tdm_slots;
+ u32 tdm_mask[2];
+ int slot_width;
u8 op_mode;
u8 num_serializer;
u8 *serial_dir;
u8 version;
u8 bclk_div;
- u16 bclk_lrclk_ratio;
int streams;
u32 irq_request[2];
int dma_request[2];
@@ -556,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
mcasp->bclk_div = div;
break;
- case 2: /* BCLK/LRCLK ratio */
- mcasp->bclk_lrclk_ratio = div;
+ case 2: /*
+ * BCLK/LRCLK ratio descries how many bit-clock cycles
+ * fit into one frame. The clock ratio is given for a
+ * full period of data (for I2S format both left and
+ * right channels), so it has to be divided by number
+ * of tdm-slots (for I2S - divided by 2).
+ * Instead of storing this ratio, we calculate a new
+ * tdm_slot width by dividing the the ratio by the
+ * number of configured tdm slots.
+ */
+ mcasp->slot_width = div / mcasp->tdm_slots;
+ if (div % mcasp->tdm_slots)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
+ __func__, div, mcasp->tdm_slots);
break;
default:
@@ -596,12 +610,92 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
return 0;
}
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
+ int serializers)
+{
+ struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
+ unsigned int *list = (unsigned int *) cl->list;
+ int slots = mcasp->tdm_slots;
+ int i, count = 0;
+
+ if (mcasp->tdm_mask[stream])
+ slots = hweight32(mcasp->tdm_mask[stream]);
+
+ for (i = 2; i <= slots; i++)
+ list[count++] = i;
+
+ for (i = 2; i <= serializers; i++)
+ list[count++] = i*slots;
+
+ cl->count = count;
+
+ return 0;
+}
+
+static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp)
+{
+ int rx_serializers = 0, tx_serializers = 0, ret, i;
+
+ for (i = 0; i < mcasp->num_serializer; i++)
+ if (mcasp->serial_dir[i] == TX_MODE)
+ tx_serializers++;
+ else if (mcasp->serial_dir[i] == RX_MODE)
+ rx_serializers++;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK,
+ tx_serializers);
+ if (ret)
+ return ret;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
+ rx_serializers);
+
+ return ret;
+}
+
+
+static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(mcasp->dev,
+ "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+ dev_err(mcasp->dev,
+ "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+ tx_mask, rx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (slot_width &&
+ (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
+ dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ mcasp->tdm_slots = slots;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask;
+ mcasp->slot_width = slot_width;
+
+ return davinci_mcasp_set_ch_constraints(mcasp);
+}
+
static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
- int word_length)
+ int sample_width)
{
u32 fmt;
- u32 tx_rotate = (word_length / 4) & 0x7;
- u32 mask = (1ULL << word_length) - 1;
+ u32 tx_rotate = (sample_width / 4) & 0x7;
+ u32 mask = (1ULL << sample_width) - 1;
+ u32 slot_width = sample_width;
+
/*
* For captured data we should not rotate, inversion and masking is
* enoguh to get the data to the right position:
@@ -614,28 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
u32 rx_rotate = 0;
/*
- * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
- * callback, take it into account here. That allows us to for example
- * send 32 bits per channel to the codec, while only 16 of them carry
- * audio payload.
- * The clock ratio is given for a full period of data (for I2S format
- * both left and right channels), so it has to be divided by number of
- * tdm-slots (for I2S - divided by 2).
+ * Setting the tdm slot width either with set_clkdiv() or
+ * set_tdm_slot() allows us to for example send 32 bits per
+ * channel to the codec, while only 16 of them carry audio
+ * payload.
*/
- if (mcasp->bclk_lrclk_ratio) {
- u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
-
+ if (mcasp->slot_width) {
/*
- * When we have more bclk then it is needed for the data, we
- * need to use the rotation to move the received samples to have
- * correct alignment.
+ * When we have more bclk then it is needed for the
+ * data, we need to use the rotation to move the
+ * received samples to have correct alignment.
*/
- rx_rotate = (slot_length - word_length) / 4;
- word_length = slot_length;
+ slot_width = mcasp->slot_width;
+ rx_rotate = (slot_width - sample_width) / 4;
}
/* mapping of the XSSZ bit-field as described in the datasheet */
- fmt = (word_length >> 1) - 1;
+ fmt = (slot_width >> 1) - 1;
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
@@ -776,33 +865,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
/*
* If more than one serializer is needed, then use them with
- * their specified tdm_slots count. Otherwise, one serializer
- * can cope with the transaction using as many slots as channels
- * in the stream, requires channels symmetry
+ * all the specified tdm_slots. Otherwise, one serializer can
+ * cope with the transaction using just as many slots as there
+ * are channels in the stream.
*/
- active_serializers = (channels + total_slots - 1) / total_slots;
- if (active_serializers == 1)
- active_slots = channels;
- else
- active_slots = total_slots;
-
- for (i = 0; i < active_slots; i++)
- mask |= (1 << i);
+ if (mcasp->tdm_mask[stream]) {
+ active_slots = hweight32(mcasp->tdm_mask[stream]);
+ active_serializers = (channels + active_slots - 1) /
+ active_slots;
+ if (active_serializers == 1) {
+ active_slots = channels;
+ for (i = 0; i < total_slots; i++) {
+ if ((1 << i) & mcasp->tdm_mask[stream]) {
+ mask |= (1 << i);
+ if (--active_slots <= 0)
+ break;
+ }
+ }
+ }
+ } else {
+ active_serializers = (channels + total_slots - 1) / total_slots;
+ if (active_serializers == 1)
+ active_slots = channels;
+ else
+ active_slots = total_slots;
+ for (i = 0; i < active_slots; i++)
+ mask |= (1 << i);
+ }
mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (!mcasp->dat_port)
busel = TXSEL;
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(total_slots), FSXMOD(0x1FF));
-
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
- FSRMOD(total_slots), FSRMOD(0x1FF));
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+ FSRMOD(total_slots), FSRMOD(0x1FF));
+ }
return 0;
}
@@ -922,6 +1028,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
int sbits = params_width(params);
int ppm, div;
+ if (mcasp->slot_width)
+ sbits = mcasp->slot_width;
+
div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
&ppm);
if (ppm)
@@ -1027,6 +1136,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval range;
int i;
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
snd_interval_any(&range);
range.empty = 1;
@@ -1069,10 +1181,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
if (snd_mask_test(fmt, i)) {
- uint bclk_freq = snd_pcm_format_width(i)*slots*rate;
+ uint sbits = snd_pcm_format_width(i);
int ppm;
- davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
+ davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
+ &ppm);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
snd_mask_set(&nfmt, i);
count++;
@@ -1094,6 +1210,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
&mcasp->ruledata[substream->stream];
u32 max_channels = 0;
int i, dir;
+ int tdm_slots = mcasp->tdm_slots;
+
+ if (mcasp->tdm_mask[substream->stream])
+ tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
mcasp->substreams[substream->stream] = substream;
@@ -1114,7 +1234,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
max_channels++;
}
ruledata->serializers = max_channels;
- max_channels *= mcasp->tdm_slots;
+ max_channels *= tdm_slots;
/*
* If the already active stream has less channels than the calculated
* limnit based on the seirializers * tdm_slots, we need to use that as
@@ -1124,15 +1244,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
*/
if (mcasp->channels && mcasp->channels < max_channels)
max_channels = mcasp->channels;
+ /*
+ * But we can always allow channels upto the amount of
+ * the available tdm_slots.
+ */
+ if (max_channels < tdm_slots)
+ max_channels = tdm_slots;
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
2, max_channels);
- if (mcasp->chconstr[substream->stream].count)
- snd_pcm_hw_constraint_list(substream->runtime,
- 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &mcasp->chconstr[substream->stream]);
+ snd_pcm_hw_constraint_list(substream->runtime,
+ 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &mcasp->chconstr[substream->stream]);
+
+ if (mcasp->slot_width)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ 8, mcasp->slot_width);
/*
* If we rely on implicit BCLK divider setting we should
@@ -1184,6 +1314,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
.set_fmt = davinci_mcasp_set_dai_fmt,
.set_clkdiv = davinci_mcasp_set_clkdiv,
.set_sysclk = davinci_mcasp_set_sysclk,
+ .set_tdm_slot = davinci_mcasp_set_tdm_slot,
};
static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
@@ -1514,59 +1645,6 @@ nodata:
return pdata;
}
-/* All serializers must have equal number of channels */
-static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp,
- struct snd_pcm_hw_constraint_list *cl,
- int serializers)
-{
- unsigned int *list;
- int i, count = 0;
-
- if (serializers <= 1)
- return 0;
-
- list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
- (mcasp->tdm_slots + serializers - 2),
- GFP_KERNEL);
- if (!list)
- return -ENOMEM;
-
- for (i = 2; i <= mcasp->tdm_slots; i++)
- list[count++] = i;
-
- for (i = 2; i <= serializers; i++)
- list[count++] = i*mcasp->tdm_slots;
-
- cl->count = count;
- cl->list = list;
-
- return 0;
-}
-
-
-static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
-{
- int rx_serializers = 0, tx_serializers = 0, ret, i;
-
- for (i = 0; i < mcasp->num_serializer; i++)
- if (mcasp->serial_dir[i] == TX_MODE)
- tx_serializers++;
- else if (mcasp->serial_dir[i] == RX_MODE)
- rx_serializers++;
-
- ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
- SNDRV_PCM_STREAM_PLAYBACK],
- tx_serializers);
- if (ret)
- return ret;
-
- ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
- SNDRV_PCM_STREAM_CAPTURE],
- rx_serializers);
-
- return ret;
-}
-
enum {
PCM_EDMA,
PCM_SDMA,
@@ -1783,7 +1861,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
}
- ret = davinci_mcasp_init_ch_constraints(mcasp);
+ /* Allocate memory for long enough list for all possible
+ * scenarios. Maximum number tdm slots is 32 and there cannot
+ * be more serializers than given in the configuration. The
+ * serializer directions could be taken into account, but it
+ * would make code much more complex and save only couple of
+ * bytes.
+ */
+ mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
+ devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+ (32 + mcasp->num_serializer - 2),
+ GFP_KERNEL);
+
+ mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
+ devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+ (32 + mcasp->num_serializer - 2),
+ GFP_KERNEL);
+
+ if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
+ !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list)
+ return -ENOMEM;
+
+ ret = davinci_mcasp_set_ch_constraints(mcasp);
if (ret)
goto err;
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index ba34252b7bba..6e6a70c5c2bd 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -282,23 +282,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
config->sample_rate = params_rate(params);
- if (dev->i2s_clk_cfg) {
- ret = dev->i2s_clk_cfg(config);
- if (ret < 0) {
- dev_err(dev->dev, "runtime audio clk config fail\n");
- return ret;
- }
- } else {
- u32 bitclk = config->sample_rate * config->data_width * 2;
-
- ret = clk_set_rate(dev->clk, bitclk);
- if (ret) {
- dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
- ret);
- return ret;
+ if (dev->capability & DW_I2S_MASTER) {
+ if (dev->i2s_clk_cfg) {
+ ret = dev->i2s_clk_cfg(config);
+ if (ret < 0) {
+ dev_err(dev->dev, "runtime audio clk config fail\n");
+ return ret;
+ }
+ } else {
+ u32 bitclk = config->sample_rate *
+ config->data_width * 2;
+
+ ret = clk_set_rate(dev->clk, bitclk);
+ if (ret) {
+ dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
+ ret);
+ return ret;
+ }
}
}
-
return 0;
}
@@ -348,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ if (dev->capability & DW_I2S_SLAVE)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ if (dev->capability & DW_I2S_MASTER)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ ret = -EINVAL;
+ break;
+ default:
+ dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static struct snd_soc_dai_ops dw_i2s_dai_ops = {
.startup = dw_i2s_startup,
.shutdown = dw_i2s_shutdown,
.hw_params = dw_i2s_hw_params,
.prepare = dw_i2s_prepare,
.trigger = dw_i2s_trigger,
+ .set_fmt = dw_i2s_set_fmt,
};
static const struct snd_soc_component_driver dw_i2s_component = {
@@ -366,7 +399,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- clk_disable(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable(dev->clk);
return 0;
}
@@ -374,7 +408,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- clk_enable(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_enable(dev->clk);
return 0;
}
@@ -452,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
dw_i2s_dai->capture.rates = rates;
}
+ if (COMP1_MODE_EN(comp1)) {
+ dev_dbg(dev->dev, "designware: i2s master mode supported\n");
+ dev->capability |= DW_I2S_MASTER;
+ } else {
+ dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
+ dev->capability |= DW_I2S_SLAVE;
+ }
+
return 0;
}
@@ -538,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
struct resource *res;
int ret;
struct snd_soc_dai_driver *dw_i2s_dai;
+ const char *clk_id;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
@@ -559,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev)
return PTR_ERR(dev->i2s_base);
dev->dev = &pdev->dev;
+
if (pdata) {
+ dev->capability = pdata->cap;
+ clk_id = NULL;
ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
- if (ret < 0)
- return ret;
+ } else {
+ clk_id = "i2sclk";
+ ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+ }
+ if (ret < 0)
+ return ret;
- dev->capability = pdata->cap;
- dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
- if (!dev->i2s_clk_cfg) {
- dev_err(&pdev->dev, "no clock configure method\n");
- return -ENODEV;
+ if (dev->capability & DW_I2S_MASTER) {
+ if (pdata) {
+ dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+ if (!dev->i2s_clk_cfg) {
+ dev_err(&pdev->dev, "no clock configure method\n");
+ return -ENODEV;
+ }
}
+ dev->clk = devm_clk_get(&pdev->dev, clk_id);
- dev->clk = devm_clk_get(&pdev->dev, NULL);
- } else {
- ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+
+ ret = clk_prepare_enable(dev->clk);
if (ret < 0)
return ret;
-
- dev->clk = devm_clk_get(&pdev->dev, "i2sclk");
}
- if (IS_ERR(dev->clk))
- return PTR_ERR(dev->clk);
-
- ret = clk_prepare_enable(dev->clk);
- if (ret < 0)
- return ret;
dev_set_drvdata(&pdev->dev, dev);
ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
@@ -606,7 +653,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
return 0;
err_clk_disable:
- clk_disable_unprepare(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(dev->clk);
return ret;
}
@@ -614,7 +662,8 @@ static int dw_i2s_remove(struct platform_device *pdev)
{
struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
- clk_disable_unprepare(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(dev->clk);
return 0;
}
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 96f55ae75c71..1b05d1c5d9fd 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -14,6 +14,9 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+#include <sound/ac97_codec.h>
+#endif
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
+static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv)
+{
+ return priv->dai_fmt == SND_SOC_DAIFMT_AC97;
+}
+
static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
* set_bias_level(), bypass the remaining settings in hw_params().
* Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
*/
- if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
+ if ((priv->card.set_bias_level &&
+ priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) ||
+ fsl_asoc_card_is_ac97(priv))
return 0;
/* Specific configurations of DAIs starts from here */
@@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
ext_port--;
/*
- * Use asynchronous mode (6 wires) for all cases.
+ * Use asynchronous mode (6 wires) for all cases except AC97.
* If only 4 wires are needed, just set SSI into
* synchronous mode and enable 4 PADs in IOMUX.
*/
@@ -346,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
default:
- return -EINVAL;
+ if (!fsl_asoc_card_is_ac97(priv))
+ return -EINVAL;
+ }
+
+ if (fsl_asoc_card_is_ac97(priv)) {
+ int_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR;
+ ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_TFSDIR;
}
/* Asynchronous mode can not be set along with RCLKDIR */
- ret = imx_audmux_v2_configure_port(int_port, 0,
- IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
- if (ret) {
- dev_err(dev, "audmux internal port setup failed\n");
- return ret;
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ unsigned int pdcr =
+ IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port);
+
+ ret = imx_audmux_v2_configure_port(int_port, 0,
+ pdcr);
+ if (ret) {
+ dev_err(dev, "audmux internal port setup failed\n");
+ return ret;
+ }
}
ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
@@ -364,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
return ret;
}
- ret = imx_audmux_v2_configure_port(ext_port, 0,
- IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
- if (ret) {
- dev_err(dev, "audmux external port setup failed\n");
- return ret;
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ unsigned int pdcr =
+ IMX_AUDMUX_V2_PDCR_RXDSEL(int_port);
+
+ ret = imx_audmux_v2_configure_port(ext_port, 0,
+ pdcr);
+ if (ret) {
+ dev_err(dev, "audmux external port setup failed\n");
+ return ret;
+ }
}
ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
@@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
struct device *dev = card->dev;
int ret;
+ if (fsl_asoc_card_is_ac97(priv)) {
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+ struct snd_soc_codec *codec = card->rtd[0].codec;
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * Use slots 3/4 for S/PDIF so SSI won't try to enable
+ * other slots and send some samples there
+ * due to SLOTREQ bits for S/PDIF received from codec
+ */
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4);
+#endif
+
+ return 0;
+ }
+
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
if (ret) {
@@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
struct i2c_client *codec_dev;
- struct clk *codec_clk;
const char *codec_dai_name;
u32 width;
int ret;
@@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Give a chance to old DT binding */
if (!cpu_np)
cpu_np = of_parse_phandle(np, "ssi-controller", 0);
- codec_np = of_parse_phandle(np, "audio-codec", 0);
- if (!cpu_np || !codec_np) {
- dev_err(&pdev->dev, "phandle missing or invalid\n");
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "CPU phandle missing or invalid\n");
ret = -EINVAL;
goto fail;
}
@@ -434,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto fail;
}
- codec_dev = of_find_i2c_device_by_node(codec_np);
- if (!codec_dev) {
- dev_err(&pdev->dev, "failed to find codec platform device\n");
- ret = -EINVAL;
- goto fail;
- }
+ codec_np = of_parse_phandle(np, "audio-codec", 0);
+ if (codec_np)
+ codec_dev = of_find_i2c_device_by_node(codec_np);
+ else
+ codec_dev = NULL;
asrc_np = of_parse_phandle(np, "audio-asrc", 0);
if (asrc_np)
asrc_pdev = of_find_device_by_node(asrc_np);
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
- codec_clk = clk_get(&codec_dev->dev, NULL);
- if (!IS_ERR(codec_clk)) {
- priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
- clk_put(codec_clk);
+ if (codec_dev) {
+ struct clk *codec_clk = clk_get(&codec_dev->dev, NULL);
+
+ if (!IS_ERR(codec_clk)) {
+ priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
+ clk_put(codec_clk);
+ }
}
/* Default sample rate and format, will be updated in hw_params() */
@@ -486,12 +533,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
+ codec_dai_name = "ac97-hifi";
+ priv->card.set_bias_level = NULL;
+ priv->dai_fmt = SND_SOC_DAIFMT_AC97;
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
ret = -EINVAL;
goto asrc_fail;
}
+ if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
+ dev_err(&pdev->dev, "failed to find codec device\n");
+ ret = -EINVAL;
+ goto asrc_fail;
+ }
+
/* Common settings for corresponding Freescale CPU DAI driver */
if (strstr(cpu_np->name, "ssi")) {
/* Only SSI needs to configure AUDMUX */
@@ -508,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
}
- sprintf(priv->name, "%s-audio", codec_dev->name);
+ snprintf(priv->name, sizeof(priv->name), "%s-audio",
+ fsl_asoc_card_is_ac97(priv) ? "ac97" :
+ codec_dev->name);
/* Initialize sound card */
priv->pdev = pdev;
@@ -532,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Normal DAI Link */
priv->dai_link[0].cpu_of_node = cpu_np;
- priv->dai_link[0].codec_of_node = codec_np;
priv->dai_link[0].codec_dai_name = codec_dai_name;
+
+ if (!fsl_asoc_card_is_ac97(priv))
+ priv->dai_link[0].codec_of_node = codec_np;
+ else {
+ u32 idx;
+
+ ret = of_property_read_u32(cpu_np, "cell-index", &idx);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "cannot get CPU index property\n");
+ goto asrc_fail;
+ }
+
+ priv->dai_link[0].codec_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "ac97-codec.%u",
+ (unsigned int)idx);
+ }
+
priv->dai_link[0].platform_of_node = cpu_np;
priv->dai_link[0].dai_fmt = priv->dai_fmt;
priv->card.num_links = 1;
@@ -544,6 +621,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->dai_link[1].platform_of_node = asrc_np;
priv->dai_link[2].codec_dai_name = codec_dai_name;
priv->dai_link[2].codec_of_node = codec_np;
+ priv->dai_link[2].codec_name =
+ priv->dai_link[0].codec_name;
priv->dai_link[2].cpu_of_node = cpu_np;
priv->dai_link[2].dai_fmt = priv->dai_fmt;
priv->card.num_links = 3;
@@ -579,20 +658,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
asrc_fail:
of_node_put(asrc_np);
-fail:
of_node_put(codec_np);
+fail:
of_node_put(cpu_np);
return ret;
}
static const struct of_device_id fsl_asoc_card_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-ac97", },
{ .compatible = "fsl,imx-audio-cs42888", },
{ .compatible = "fsl,imx-audio-sgtl5000", },
{ .compatible = "fsl,imx-audio-wm8962", },
{ .compatible = "fsl,imx-audio-wm8960", },
{}
};
+MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
static struct platform_driver fsl_asoc_card_driver = {
.probe = fsl_asoc_card_probe,
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 837979ea5c92..59f234e51971 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = {
.name = "fsl-esai",
};
+static const struct reg_default fsl_esai_reg_defaults[] = {
+ {0x8, 0x00000000},
+ {0x10, 0x00000000},
+ {0x18, 0x00000000},
+ {0x98, 0x00000000},
+ {0xd0, 0x00000000},
+ {0xd4, 0x00000000},
+ {0xd8, 0x00000000},
+ {0xdc, 0x00000000},
+ {0xe0, 0x00000000},
+ {0xe4, 0x0000ffff},
+ {0xe8, 0x0000ffff},
+ {0xec, 0x0000ffff},
+ {0xf0, 0x0000ffff},
+ {0xf8, 0x00000000},
+ {0xfc, 0x00000000},
+};
+
static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
}
}
+static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ESAI_ETDR:
+ case REG_ESAI_ERDR:
+ case REG_ESAI_ESR:
+ case REG_ESAI_TFSR:
+ case REG_ESAI_RFSR:
+ case REG_ESAI_TX0:
+ case REG_ESAI_TX1:
+ case REG_ESAI_TX2:
+ case REG_ESAI_TX3:
+ case REG_ESAI_TX4:
+ case REG_ESAI_TX5:
+ case REG_ESAI_RX0:
+ case REG_ESAI_RX1:
+ case REG_ESAI_RX2:
+ case REG_ESAI_RX3:
+ case REG_ESAI_SAISR:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = {
.val_bits = 32,
.max_register = REG_ESAI_PCRC,
+ .reg_defaults = fsl_esai_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults),
.readable_reg = fsl_esai_readable_reg,
+ .volatile_reg = fsl_esai_volatile_reg,
.writeable_reg = fsl_esai_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
static int fsl_esai_probe(struct platform_device *pdev)
@@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
+#ifdef CONFIG_PM_SLEEP
+static int fsl_esai_suspend(struct device *dev)
+{
+ struct fsl_esai *esai = dev_get_drvdata(dev);
+
+ regcache_cache_only(esai->regmap, true);
+ regcache_mark_dirty(esai->regmap);
+
+ return 0;
+}
+
+static int fsl_esai_resume(struct device *dev)
+{
+ struct fsl_esai *esai = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(esai->regmap, false);
+
+ /* FIFO reset for safety */
+ regmap_update_bits(esai->regmap, REG_ESAI_TFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+ regmap_update_bits(esai->regmap, REG_ESAI_RFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+
+ ret = regcache_sync(esai->regmap);
+ if (ret)
+ return ret;
+
+ /* FIFO reset done */
+ regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
+ regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_esai_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume)
+};
+
static struct platform_driver fsl_esai_driver = {
.probe = fsl_esai_probe,
.driver = {
.name = "fsl-esai-dai",
+ .pm = &fsl_esai_pm_ops,
.of_match_table = fsl_esai_dt_ids,
},
};
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index a18fd92c4a85..a4435f5e3be9 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -27,13 +27,13 @@
#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
FSL_SAI_CSR_FEIE)
-static u32 fsl_sai_rates[] = {
+static const unsigned int fsl_sai_rates[] = {
8000, 11025, 12000, 16000, 22050,
24000, 32000, 44100, 48000, 64000,
88200, 96000, 176400, 192000
};
-static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
+static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
.count = ARRAY_SIZE(fsl_sai_rates),
.list = fsl_sai_rates,
};
@@ -637,6 +637,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case FSL_SAI_TCSR:
+ case FSL_SAI_RCSR:
case FSL_SAI_TFR:
case FSL_SAI_RFR:
case FSL_SAI_TDR:
@@ -681,6 +683,7 @@ static const struct regmap_config fsl_sai_regmap_config = {
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,
+ .cache_type = REGCACHE_FLAT,
};
static int fsl_sai_probe(struct platform_device *pdev)
@@ -801,11 +804,42 @@ static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,imx6sx-sai", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_sai_ids);
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_sai_suspend(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, true);
+ regcache_mark_dirty(sai->regmap);
+
+ return 0;
+}
+
+static int fsl_sai_resume(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, false);
+ regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+ msleep(1);
+ regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+ return regcache_sync(sai->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_sai_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
+};
static struct platform_driver fsl_sai_driver = {
.probe = fsl_sai_probe,
.driver = {
.name = "fsl-sai",
+ .pm = &fsl_sai_pm_ops,
.of_match_table = fsl_sai_ids,
},
};
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index ab729f2426fe..3d59bb6719f2 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -108,6 +108,8 @@ struct fsl_spdif_priv {
struct clk *sysclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
+ /* regcache for SRPC */
+ u32 regcache_srpc;
};
/* DPLL locked and lock loss interrupt handler */
@@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
struct regmap *regmap = spdif_priv->regmap;
u32 val, cycle = 1000;
+ regcache_cache_bypass(regmap, true);
+
regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
/*
@@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
regmap_read(regmap, REG_SPDIF_SCR, &val);
} while ((val & SCR_SOFT_RESET) && cycle--);
+ regcache_cache_bypass(regmap, false);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+
if (cycle)
return 0;
else
@@ -997,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
};
/* FSL SPDIF REGMAP */
+static const struct reg_default fsl_spdif_reg_defaults[] = {
+ {0x0, 0x00000400},
+ {0x4, 0x00000000},
+ {0xc, 0x00000000},
+ {0x34, 0x00000000},
+ {0x38, 0x00000000},
+ {0x50, 0x00020f00},
+};
static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
{
@@ -1022,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
}
}
+static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SRPC:
+ 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_STL:
+ case REG_SPDIF_STR:
+ case REG_SPDIF_SRFM:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -1047,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = {
.val_bits = 32,
.max_register = REG_SPDIF_STC,
+ .reg_defaults = fsl_spdif_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults),
.readable_reg = fsl_spdif_readable_reg,
+ .volatile_reg = fsl_spdif_volatile_reg,
.writeable_reg = fsl_spdif_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
@@ -1271,6 +1311,38 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int fsl_spdif_suspend(struct device *dev)
+{
+ struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+ regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
+ &spdif_priv->regcache_srpc);
+
+ regcache_cache_only(spdif_priv->regmap, true);
+ regcache_mark_dirty(spdif_priv->regmap);
+
+ return 0;
+}
+
+static int fsl_spdif_resume(struct device *dev)
+{
+ struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdif_priv->regmap, false);
+
+ regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
+ SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+ spdif_priv->regcache_srpc);
+
+ return regcache_sync(spdif_priv->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_spdif_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
+};
+
static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx35-spdif", },
{ .compatible = "fsl,vf610-spdif", },
@@ -1282,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = {
.driver = {
.name = "fsl-spdif-dai",
.of_match_table = fsl_spdif_dt_ids,
+ .pm = &fsl_spdif_pm,
},
.probe = fsl_spdif_probe,
};
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 37c5cd4d0e59..95d2392303eb 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val {
struct fsl_ssi_reg_val rx;
struct fsl_ssi_reg_val tx;
};
+
+static const struct reg_default fsl_ssi_reg_defaults[] = {
+ {0x10, 0x00000000},
+ {0x18, 0x00003003},
+ {0x1c, 0x00000200},
+ {0x20, 0x00000200},
+ {0x24, 0x00040000},
+ {0x28, 0x00040000},
+ {0x38, 0x00000000},
+ {0x48, 0x00000000},
+ {0x4c, 0x00000000},
+ {0x54, 0x00000000},
+ {0x58, 0x00000000},
+};
+
+static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_SACCEN:
+ case CCSR_SSI_SACCDIS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_STX0:
+ case CCSR_SSI_STX1:
+ case CCSR_SSI_SRX0:
+ case CCSR_SSI_SRX1:
+ case CCSR_SSI_SISR:
+ case CCSR_SSI_SFCSR:
+ case CCSR_SSI_SACADD:
+ case CCSR_SSI_SACDAT:
+ case CCSR_SSI_SATAG:
+ case CCSR_SSI_SACCST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_SRX0:
+ case CCSR_SSI_SRX1:
+ case CCSR_SSI_SACCST:
+ return false;
+ default:
+ return true;
+ }
+}
+
static const struct regmap_config fsl_ssi_regconfig = {
.max_register = CCSR_SSI_SACCDIS,
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .reg_defaults = fsl_ssi_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+ .readable_reg = fsl_ssi_readable_reg,
+ .volatile_reg = fsl_ssi_volatile_reg,
+ .writeable_reg = fsl_ssi_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
struct fsl_ssi_soc_data {
@@ -176,6 +239,9 @@ struct fsl_ssi_private {
unsigned int baudclk_streams;
unsigned int bitclk_freq;
+ /*regcache for SFCSR*/
+ u32 regcache_sfcsr;
+
/* DMA params */
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
@@ -1514,10 +1580,46 @@ static int fsl_ssi_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int fsl_ssi_suspend(struct device *dev)
+{
+ struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+ struct regmap *regs = ssi_private->regs;
+
+ regmap_read(regs, CCSR_SSI_SFCSR,
+ &ssi_private->regcache_sfcsr);
+
+ regcache_cache_only(regs, true);
+ regcache_mark_dirty(regs);
+
+ return 0;
+}
+
+static int fsl_ssi_resume(struct device *dev)
+{
+ struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+ struct regmap *regs = ssi_private->regs;
+
+ regcache_cache_only(regs, false);
+
+ regmap_update_bits(regs, CCSR_SSI_SFCSR,
+ CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK |
+ CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK,
+ ssi_private->regcache_sfcsr);
+
+ return regcache_sync(regs);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_ssi_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
+};
+
static struct platform_driver fsl_ssi_driver = {
.driver = {
.name = "fsl-ssi-dai",
.of_match_table = fsl_ssi_ids,
+ .pm = &fsl_ssi_pm,
},
.probe = fsl_ssi_probe,
.remove = fsl_ssi_remove,
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
index 33da26a12457..a407e833c612 100644
--- a/sound/soc/fsl/imx-spdif.c
+++ b/sound/soc/fsl/imx-spdif.c
@@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
static struct platform_driver imx_spdif_driver = {
.driver = {
.name = "imx-spdif",
+ .pm = &snd_soc_pm_ops,
.of_match_table = imx_spdif_dt_ids,
},
.probe = imx_spdif_audio_probe,
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 3ff76d419436..54c33204541f 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -151,7 +151,9 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
}
if (set->slots) {
- ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
+ ret = snd_soc_dai_set_tdm_slot(dai,
+ set->tx_slot_mask,
+ set->rx_slot_mask,
set->slots,
set->slot_width);
if (ret && ret != -ENOTSUPP) {
@@ -243,7 +245,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
return ret;
/* Parse TDM slot */
- ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
+ ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
+ &dai->rx_slot_mask,
+ &dai->slots, &dai->slot_width);
if (ret)
return ret;
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 05fde5e6e257..7b778ab85f8b 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -12,6 +12,7 @@ config SND_MFLD_MACHINE
config SND_SST_MFLD_PLATFORM
tristate
+ select SND_SOC_COMPRESS
config SND_SST_IPC
tristate
@@ -138,4 +139,18 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
config SND_SOC_INTEL_SKYLAKE
tristate
select SND_HDA_EXT_CORE
+ select SND_SOC_TOPOLOGY
select SND_SOC_INTEL_SST
+
+config SND_SOC_INTEL_SKL_RT286_MACH
+ tristate "ASoC Audio driver for SKL with RT286 I2S mode"
+ depends on X86 && ACPI
+ select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SKYLAKE
+ select SND_SOC_RT286
+ select SND_SOC_DMIC
+ help
+ This adds support for ASoC machine driver for Skylake platforms
+ with RT286 I2S audio codec.
+ Say Y if you have such a device
+ If unsure select "N".
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 683e50116152..0487cfaac538 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -368,23 +368,6 @@ static void sst_media_close(struct snd_pcm_substream *substream,
kfree(stream);
}
-static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
- struct snd_pcm_substream *substream)
-{
- struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
- struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
- struct sst_runtime_stream *stream =
- substream->runtime->private_data;
- u32 str_id = stream->stream_info.str_id;
- unsigned int pipe_id;
-
- pipe_id = map[str_id].device_id;
-
- dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
- pipe_id, str_id);
- return pipe_id;
-}
-
static int sst_media_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -529,7 +512,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
},
{
.name = "compress-cpu-dai",
- .compress_dai = 1,
+ .compress_new = snd_soc_new_compress,
.ops = &sst_compr_dai_ops,
.playback = {
.stream_name = "Compress Playback",
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index cb94895c9edb..371c4565cad8 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+snd-soc-skl_rt286-objs := skl_rt286.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
@@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
index 8bafaf6ceab1..3f8a1e10bed0 100644
--- a/sound/soc/intel/boards/broadwell.c
+++ b/sound/soc/intel/boards/broadwell.c
@@ -266,18 +266,11 @@ static int broadwell_audio_probe(struct platform_device *pdev)
{
broadwell_rt286.dev = &pdev->dev;
- return snd_soc_register_card(&broadwell_rt286);
-}
-
-static int broadwell_audio_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&broadwell_rt286);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
}
static struct platform_driver broadwell_audio = {
.probe = broadwell_audio_probe,
- .remove = broadwell_audio_remove,
.driver = {
.name = "broadwell-audio",
},
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index c4453120b11a..7a5c9a36c1db 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -117,20 +117,10 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int byt_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops byt_aif1_ops = {
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index 49f4869cec48..4e2fcf188dd1 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -193,20 +193,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int cht_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static int cht_max98090_headset_init(struct snd_soc_component *component)
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 7be8461e4d3b..38d65a3529c4 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -235,20 +235,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int cht_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops cht_aif1_ops = {
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index 23fe04075142..5621ccd92992 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -222,20 +222,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int cht_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops cht_aif1_ops = {
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
new file mode 100644
index 000000000000..a73a431bd8b7
--- /dev/null
+++ b/sound/soc/intel/boards/skl_rt286.c
@@ -0,0 +1,259 @@
+/*
+ * Intel Skylake I2S Machine Driver
+ *
+ * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ * Intel Broadwell Wildcatpoint SST Audio
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include "../../codecs/rt286.h"
+
+static struct snd_soc_jack skylake_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin skylake_headset_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new skylake_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static const struct snd_soc_dapm_widget skylake_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("DMIC2", NULL),
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route skylake_rt286_map[] = {
+ /* speaker */
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+
+ /* HP jack connectors - unknown if we have jack deteck */
+ {"Headphone Jack", NULL, "HPO Pin"},
+
+ /* other jacks */
+ {"MIC1", NULL, "Mic Jack"},
+
+ /* digital mics */
+ {"DMIC1 Pin", NULL, "DMIC2"},
+ {"DMIC AIF", NULL, "SoC DMIC"},
+
+ /* CODEC BE connections */
+ { "AIF1 Playback", NULL, "ssp0 Tx"},
+ { "ssp0 Tx", NULL, "codec0_out"},
+ { "ssp0 Tx", NULL, "codec1_out"},
+
+ { "codec0_in", NULL, "ssp0 Rx" },
+ { "codec1_in", NULL, "ssp0 Rx" },
+ { "ssp0 Rx", NULL, "AIF1 Capture" },
+
+ { "dmic01_hifi", NULL, "DMIC01 Rx" },
+ { "DMIC01 Rx", NULL, "Capture" },
+
+ { "hif1", NULL, "iDisp Tx"},
+ { "iDisp Tx", NULL, "iDisp_out"},
+
+};
+
+static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &skylake_headset,
+ skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
+
+ if (ret)
+ return ret;
+
+ rt286_mic_detect(codec, &skylake_headset);
+
+ return 0;
+}
+
+
+static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The output is 48KHz, stereo, 16bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int skylake_rt286_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_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static struct snd_soc_ops skylake_rt286_ops = {
+ .hw_params = skylake_rt286_hw_params,
+};
+
+/* skylake digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link skylake_rt286_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "Skl Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:1f.3",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Skl Audio Capture Port",
+ .stream_name = "Audio Record",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:1f.3",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Skl Audio Reference cap",
+ .stream_name = "refcap",
+ .cpu_dai_name = "Reference Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "SSP0-Codec",
+ .be_id = 0,
+ .cpu_dai_name = "SSP0 Pin",
+ .platform_name = "0000:00:1f.3",
+ .no_pcm = 1,
+ .codec_name = "i2c-INT343A:00",
+ .codec_dai_name = "rt286-aif1",
+ .init = skylake_rt286_codec_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = skylake_ssp0_fixup,
+ .ops = &skylake_rt286_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "dmic01",
+ .be_id = 1,
+ .cpu_dai_name = "DMIC01 Pin",
+ .codec_name = "dmic-codec",
+ .codec_dai_name = "dmic-hifi",
+ .platform_name = "0000:00:1f.3",
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ },
+};
+
+/* skylake audio machine driver for SPT + RT286S */
+static struct snd_soc_card skylake_rt286 = {
+ .name = "skylake-rt286",
+ .owner = THIS_MODULE,
+ .dai_link = skylake_rt286_dais,
+ .num_links = ARRAY_SIZE(skylake_rt286_dais),
+ .controls = skylake_controls,
+ .num_controls = ARRAY_SIZE(skylake_controls),
+ .dapm_widgets = skylake_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
+ .dapm_routes = skylake_rt286_map,
+ .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
+ .fully_routed = true,
+};
+
+static int skylake_audio_probe(struct platform_device *pdev)
+{
+ skylake_rt286.dev = &pdev->dev;
+
+ return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
+}
+
+static struct platform_driver skylake_audio = {
+ .probe = skylake_audio_probe,
+ .driver = {
+ .name = "skl_alc286s_i2s",
+ },
+};
+
+module_platform_driver(skylake_audio)
+
+/* Module information */
+MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
+MODULE_DESCRIPTION("Intel SST Audio for Skylake");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl_alc286s_i2s");
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index f24154ca4e98..d9105584c51f 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -1,7 +1,11 @@
-snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-dsp-objs := sst-dsp.o
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-ipc-objs := sst-ipc.o
+ifneq ($(CONFIG_DW_DMAC_CORE),)
+snd-soc-sst-dsp-objs += sst-firmware.o
+endif
+
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h
index cbd568eac033..2151652d37b7 100644
--- a/sound/soc/intel/common/sst-dsp-priv.h
+++ b/sound/soc/intel/common/sst-dsp-priv.h
@@ -314,6 +314,7 @@ struct sst_dsp {
int sst_state;
struct skl_cl_dev cl_dev;
u32 intr_status;
+ const struct firmware *fw;
};
/* Size optimised DRAM/IRAM memcpy */
diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c
index a627236dd1f5..c9452e02e0dd 100644
--- a/sound/soc/intel/common/sst-dsp.c
+++ b/sound/soc/intel/common/sst-dsp.c
@@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
}
EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
{
@@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst)
sst_dma_free(sst->dma);
}
EXPORT_SYMBOL_GPL(sst_dsp_free);
+#endif
/* Module information */
MODULE_AUTHOR("Liam Girdwood");
diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h
index 1f45f18715c0..859f0de00339 100644
--- a/sound/soc/intel/common/sst-dsp.h
+++ b/sound/soc/intel/common/sst-dsp.h
@@ -216,10 +216,12 @@ struct sst_pdata {
void *dsp;
};
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
/* Initialization */
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
void sst_dsp_free(struct sst_dsp *sst);
+#endif
/* SHIM Read / Write */
void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
index ebcca6dc48d1..1636a1eeb002 100644
--- a/sound/soc/intel/common/sst-firmware.c
+++ b/sound/soc/intel/common/sst-firmware.c
@@ -26,7 +26,6 @@
#include <linux/acpi.h>
/* supported DMA engine drivers */
-#include <linux/platform_data/dma-dw.h>
#include <linux/dma/dw.h>
#include <asm/page.h>
@@ -169,12 +168,6 @@ err:
return ret;
}
-static struct dw_dma_platform_data dw_pdata = {
- .is_private = 1,
- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
- .chan_priority = CHAN_PRIORITY_ASCENDING,
-};
-
static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
int irq)
{
@@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
return ERR_PTR(err);
chip->dev = dev;
- err = dw_dma_probe(chip, &dw_pdata);
+
+ err = dw_dma_probe(chip, NULL);
if (err)
return ERR_PTR(err);
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
index 27db22178204..914b6dab9bea 100644
--- a/sound/soc/intel/skylake/Makefile
+++ b/sound/soc/intel/skylake/Makefile
@@ -1,4 +1,5 @@
-snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o
+snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \
+skl-topology.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 826d4fd8930a..50a109503a3f 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
return 0;
}
+#define NOTIFICATION_PARAM_ID 3
+#define NOTIFICATION_MASK 0xf
+
+/* disable notfication for underruns/overruns from firmware module */
+static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
+{
+ struct notification_mask mask;
+ struct skl_ipc_large_config_msg msg = {0};
+
+ mask.notify = NOTIFICATION_MASK;
+ mask.enable = enable;
+
+ msg.large_param_id = NOTIFICATION_PARAM_ID;
+ msg.param_data_size = sizeof(mask);
+
+ skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
+}
+
int skl_init_dsp(struct skl *skl)
{
void __iomem *mmio_base;
@@ -79,7 +97,10 @@ int skl_init_dsp(struct skl *skl)
ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
loader_ops, &skl->skl_sst);
+ if (ret < 0)
+ return ret;
+ skl_dsp_enable_notification(skl->skl_sst, false);
dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
return ret;
@@ -122,6 +143,7 @@ int skl_suspend_dsp(struct skl *skl)
int skl_resume_dsp(struct skl *skl)
{
struct skl_sst *ctx = skl->skl_sst;
+ int ret;
/* if ppcap is not supported return 0 */
if (!skl->ebus.ppcap)
@@ -131,7 +153,12 @@ int skl_resume_dsp(struct skl *skl)
snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
- return skl_dsp_wake(ctx->dsp);
+ ret = skl_dsp_wake(ctx->dsp);
+ if (ret < 0)
+ return ret;
+
+ skl_dsp_enable_notification(skl->skl_sst, false);
+ return ret;
}
enum skl_bitdepth skl_get_bit_depth(int params)
@@ -294,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
(mconfig->formats_config.caps_size) / 4;
}
+#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF
/*
* Calculate the gatewat settings required for copier module, type of
* gateway and index of gateway to use
@@ -303,6 +331,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
struct skl_cpr_cfg *cpr_mconfig)
{
union skl_connector_node_id node_id = {0};
+ union skl_ssp_dma_node ssp_node = {0};
struct skl_pipe_params *params = mconfig->pipe->p_params;
switch (mconfig->dev_type) {
@@ -320,9 +349,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
(SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
SKL_DMA_I2S_LINK_OUTPUT_CLASS :
SKL_DMA_I2S_LINK_INPUT_CLASS;
- node_id.node.vindex = params->host_dma_id +
- (mconfig->time_slot << 1) +
- (mconfig->vbus_id << 3);
+ ssp_node.dma_node.time_slot_index = mconfig->time_slot;
+ ssp_node.dma_node.i2s_instance = mconfig->vbus_id;
+ node_id.node.vindex = ssp_node.val;
break;
case SKL_DEVICE_DMIC:
@@ -339,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
node_id.node.vindex = params->link_dma_id;
break;
- default:
+ case SKL_DEVICE_HDAHOST:
node_id.node.dma_type =
(SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
SKL_DMA_HDA_HOST_OUTPUT_CLASS :
SKL_DMA_HDA_HOST_INPUT_CLASS;
node_id.node.vindex = params->host_dma_id;
break;
+
+ default:
+ cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
+ cpr_mconfig->cpr_feature_mask = 0;
+ return;
}
cpr_mconfig->gtw_cfg.node_id = node_id.val;
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 13036b19d7e5..b0c7bd113aac 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
-void __iomem *skl_nhlt_init(struct device *dev)
+void *skl_nhlt_init(struct device *dev)
{
acpi_handle handle;
union acpi_object *obj;
@@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev)
if (obj && obj->type == ACPI_TYPE_BUFFER) {
nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
- return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length);
+ return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+ MEMREMAP_WB);
}
dev_err(dev, "device specific method to extract NHLT blob failed\n");
return NULL;
}
-void skl_nhlt_free(void __iomem *addr)
+void skl_nhlt_free(void *addr)
{
- iounmap(addr);
- addr = NULL;
+ memunmap(addr);
}
static struct nhlt_specific_cfg *skl_get_specific_cfg(
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 7d617bf493bc..a2f94ce1679d 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -24,6 +24,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "skl.h"
+#include "skl-topology.h"
#define HDA_MONO 1
#define HDA_STEREO 2
@@ -115,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
ret = pm_runtime_get_sync(dai->dev);
- if (ret)
+ if (ret < 0)
return ret;
stream = snd_hdac_ext_stream_assign(ebus, substream,
@@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct skl_pipe_params p_params = {0};
+ struct skl_module_cfg *m_cfg;
int ret, dma_id;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
dma_id = hdac_stream(stream)->stream_tag - 1;
dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.host_dma_id = dma_id;
+ p_params.stream = substream->stream;
+
+ m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
+ if (m_cfg)
+ skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params);
+
return 0;
}
@@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
}
+static int skl_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct skl_pipe_params p_params = {0};
+
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.stream = substream->stream;
+ skl_tplg_be_update_params(dai, &p_params);
+
+ return 0;
+}
+
+static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_module_cfg *mconfig;
+
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+ if (!mconfig)
+ return -EIO;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ return skl_run_pipe(ctx, mconfig->pipe);
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ return skl_stop_pipe(ctx, mconfig->pipe);
+
+ default:
+ return 0;
+ }
+}
+
static int skl_link_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct skl_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int dma_id;
+ struct skl_pipe_params p_params = {0};
- pr_debug("%s\n", __func__);
link_dev = snd_hdac_ext_stream_assign(ebus, substream,
HDAC_EXT_STREAM_TYPE_LINK);
if (!link_dev)
@@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
if (dma_params)
dma_params->stream_tag = hdac_stream(link_dev)->stream_tag;
snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
- dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.stream = substream->stream;
+ p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+ skl_tplg_be_update_params(dai, &p_params);
return 0;
}
@@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
unsigned int format_val = 0;
struct skl_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_pcm_hw_params *params;
- struct snd_interval *channels, *rate;
struct hdac_ext_link *link;
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
if (link_dev->link_prepared) {
dev_dbg(dai->dev, "already stream is prepared - returning\n");
return 0;
}
- params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL);
- if (params == NULL)
- return -ENOMEM;
-
- channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- channels->min = channels->max = substream->runtime->channels;
- rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- rate->min = rate->max = substream->runtime->rate;
- snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- substream->runtime->format);
-
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
@@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
return 0;
}
-static int skl_hda_be_startup(struct snd_pcm_substream *substream,
+static int skl_be_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return pm_runtime_get_sync(dai->dev);
}
-static void skl_hda_be_shutdown(struct snd_pcm_substream *substream,
+static void skl_be_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
pm_runtime_mark_last_busy(dai->dev);
@@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = {
.prepare = skl_pcm_prepare,
.hw_params = skl_pcm_hw_params,
.hw_free = skl_pcm_hw_free,
+ .trigger = skl_pcm_trigger,
};
static struct snd_soc_dai_ops skl_dmic_dai_ops = {
- .startup = skl_hda_be_startup,
- .shutdown = skl_hda_be_shutdown,
+ .startup = skl_be_startup,
+ .hw_params = skl_be_hw_params,
+ .shutdown = skl_be_shutdown,
+};
+
+static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
+ .startup = skl_be_startup,
+ .hw_params = skl_be_hw_params,
+ .shutdown = skl_be_shutdown,
};
static struct snd_soc_dai_ops skl_link_dai_ops = {
- .startup = skl_hda_be_startup,
+ .startup = skl_be_startup,
.prepare = skl_link_pcm_prepare,
.hw_params = skl_link_hw_params,
.hw_free = skl_link_hw_free,
.trigger = skl_link_pcm_trigger,
- .shutdown = skl_hda_be_shutdown,
+ .shutdown = skl_be_shutdown,
};
static struct snd_soc_dai_driver skl_platform_dai[] = {
@@ -488,6 +540,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
/* BE CPU Dais */
{
+ .name = "SSP0 Pin",
+ .ops = &skl_be_ssp_dai_ops,
+ .playback = {
+ .stream_name = "ssp0 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp0 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
.name = "iDisp Pin",
.ops = &skl_link_dai_ops,
.playback = {
@@ -510,17 +580,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
},
{
- .name = "DMIC23 Pin",
- .ops = &skl_dmic_dai_ops,
- .capture = {
- .stream_name = "DMIC23 Rx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
.name = "HD-Codec Pin",
.ops = &skl_link_dai_ops,
.playback = {
@@ -538,28 +597,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
-{
- .name = "HD-Codec-SPK Pin",
- .ops = &skl_link_dai_ops,
- .playback = {
- .stream_name = "HD-Codec-SPK Tx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "HD-Codec-AMIC Pin",
- .ops = &skl_link_dai_ops,
- .capture = {
- .stream_name = "HD-Codec-AMIC Rx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
};
static int skl_platform_open(struct snd_pcm_substream *substream)
@@ -577,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream)
return 0;
}
-static int skl_pcm_trigger(struct snd_pcm_substream *substream,
+static int skl_coupled_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -651,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static int skl_dsp_trigger(struct snd_pcm_substream *substream,
+static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -708,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
if (ebus->ppcap)
- return skl_dsp_trigger(substream, cmd);
+ return skl_decoupled_trigger(substream, cmd);
else
- return skl_pcm_trigger(substream, cmd);
+ return skl_coupled_trigger(substream, cmd);
}
/* calculate runtime delay from LPIB */
@@ -877,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
return retval;
}
+static int skl_platform_soc_probe(struct snd_soc_platform *platform)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+
+ if (ebus->ppcap)
+ return skl_tplg_init(platform, ebus);
+
+ return 0;
+}
static struct snd_soc_platform_driver skl_platform_drv = {
+ .probe = skl_platform_soc_probe,
.ops = &skl_platform_ops,
.pcm_new = skl_pcm_new,
.pcm_free = skl_pcm_free,
@@ -890,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = {
int skl_platform_register(struct device *dev)
{
int ret;
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ INIT_LIST_HEAD(&skl->ppl_list);
+ INIT_LIST_HEAD(&skl->dapm_path_list);
ret = snd_soc_register_platform(dev, &skl_platform_drv);
if (ret) {
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index 94875b008b0b..1bfb7f63b572 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx)
/* poll with timeout to check if operation successful */
return sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_SPA_MASK,
+ SKL_ADSPCS_CPA_MASK,
0,
SKL_DSP_PD_TO,
"Power down");
@@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS);
ctx->intr_status = val;
+ if (val == 0xffffffff) {
+ spin_unlock(&ctx->spinlock);
+ return IRQ_NONE;
+ }
+
if (val & SKL_ADSPIS_IPC) {
skl_ipc_int_disable(ctx);
result = IRQ_WAKE_THREAD;
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c
index 937a0a3a63a0..3345ea0d4414 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ b/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx)
SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY);
}
+void skl_ipc_op_int_disable(struct sst_dsp *ctx)
+{
+ /* disable IPC DONE interrupt */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_DONE, 0);
+
+ /* Disable IPC BUSY interrupt */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_BUSY, 0);
+
+}
+
bool skl_ipc_int_status(struct sst_dsp *ctx)
{
return sst_dsp_shim_read_unlocked(ctx,
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
index 9f5f67202858..f1a154e45dc3 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ b/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
void skl_ipc_int_enable(struct sst_dsp *dsp);
void skl_ipc_op_int_enable(struct sst_dsp *ctx);
+void skl_ipc_op_int_disable(struct sst_dsp *ctx);
void skl_ipc_int_disable(struct sst_dsp *dsp);
bool skl_ipc_int_status(struct sst_dsp *dsp);
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index c18ea51b7484..3b83dc99f1d4 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
static int skl_load_base_firmware(struct sst_dsp *ctx)
{
int ret = 0, i;
- const struct firmware *fw = NULL;
struct skl_sst *skl = ctx->thread_context;
u32 reg;
- ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev);
+ skl->boot_complete = false;
+ init_waitqueue_head(&skl->boot_wait);
+
+ if (ctx->fw == NULL) {
+ ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+ skl_dsp_disable_core(ctx);
+ return -EIO;
+ }
+ }
+
+ ret = skl_dsp_boot(ctx);
if (ret < 0) {
- dev_err(ctx->dev, "Request firmware failed %d\n", ret);
- skl_dsp_disable_core(ctx);
- return -EIO;
+ dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
+ goto skl_load_base_firmware_failed;
+ }
+
+ ret = skl_cldma_prepare(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "CL dma prepare failed : %d", ret);
+ goto skl_load_base_firmware_failed;
}
/* enable Interrupt */
@@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
goto skl_load_base_firmware_failed;
}
- ret = skl_transfer_firmware(ctx, fw->data, fw->size);
+ ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
if (ret < 0) {
dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
goto skl_load_base_firmware_failed;
@@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
}
- release_firmware(fw);
-
return 0;
skl_load_base_firmware_failed:
skl_dsp_disable_core(ctx);
- release_firmware(fw);
+ release_firmware(ctx->fw);
+ ctx->fw = NULL;
return ret;
}
@@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+ /* disable Interrupt */
+ ctx->cl_dev.ops.cl_cleanup_controller(ctx);
+ skl_cldma_int_disable(ctx);
+ skl_ipc_op_int_disable(ctx);
+ skl_ipc_int_disable(ctx);
+
return ret;
}
@@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
if (ret)
return ret;
- skl->boot_complete = false;
- init_waitqueue_head(&skl->boot_wait);
-
- ret = skl_dsp_boot(sst);
- if (ret < 0) {
- dev_err(skl->dev, "Boot dsp core failed ret: %d", ret);
- goto free_ipc;
- }
-
- ret = skl_cldma_prepare(sst);
- if (ret < 0) {
- dev_err(dev, "CL dma prepare failed : %d", ret);
- goto free_ipc;
- }
-
-
ret = sst->fw_ops.load_fw(sst);
if (ret < 0) {
dev_err(dev, "Load base fw failed : %d", ret);
@@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
return 0;
-free_ipc:
skl_ipc_free(&skl->ipc);
return ret;
}
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
new file mode 100644
index 000000000000..a7854c8fc523
--- /dev/null
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -0,0 +1,1252 @@
+/*
+ * skl-topology.c - Implements Platform component ALSA controls/widget
+ * handlers.
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <sound/soc.h>
+#include <sound/soc-topology.h>
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
+#include "skl-topology.h"
+#include "skl.h"
+#include "skl-tplg-interface.h"
+
+#define SKL_CH_FIXUP_MASK (1 << 0)
+#define SKL_RATE_FIXUP_MASK (1 << 1)
+#define SKL_FMT_FIXUP_MASK (1 << 2)
+
+/*
+ * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
+ * ignore. This helpers checks if the SKL driver handles this widget type
+ */
+static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dai_out:
+ case snd_soc_dapm_switch:
+ return false;
+ default:
+ return true;
+ }
+}
+
+/*
+ * Each pipelines needs memory to be allocated. Check if we have free memory
+ * from available pool. Then only add this to pool
+ * This is freed when pipe is deleted
+ * Note: DSP does actual memory management we only keep track for complete
+ * pool
+ */
+static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+
+ if (skl->resource.mem + mconfig->pipe->memory_pages >
+ skl->resource.max_mem) {
+ dev_err(ctx->dev,
+ "%s: module_id %d instance %d\n", __func__,
+ mconfig->id.module_id,
+ mconfig->id.instance_id);
+ dev_err(ctx->dev,
+ "exceeds ppl memory available %d mem %d\n",
+ skl->resource.max_mem, skl->resource.mem);
+ return false;
+ }
+
+ skl->resource.mem += mconfig->pipe->memory_pages;
+ return true;
+}
+
+/*
+ * Pipeline needs needs DSP CPU resources for computation, this is
+ * quantified in MCPS (Million Clocks Per Second) required for module/pipe
+ *
+ * Each pipelines needs mcps to be allocated. Check if we have mcps for this
+ * pipe. This adds the mcps to driver counter
+ * This is removed on pipeline delete
+ */
+static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+
+ if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) {
+ dev_err(ctx->dev,
+ "%s: module_id %d instance %d\n", __func__,
+ mconfig->id.module_id, mconfig->id.instance_id);
+ dev_err(ctx->dev,
+ "exceeds ppl memory available %d > mem %d\n",
+ skl->resource.max_mcps, skl->resource.mcps);
+ return false;
+ }
+
+ skl->resource.mcps += mconfig->mcps;
+ return true;
+}
+
+/*
+ * Free the mcps when tearing down
+ */
+static void
+skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ skl->resource.mcps -= mconfig->mcps;
+}
+
+/*
+ * Free the memory when tearing down
+ */
+static void
+skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ skl->resource.mem -= mconfig->pipe->memory_pages;
+}
+
+
+static void skl_dump_mconfig(struct skl_sst *ctx,
+ struct skl_module_cfg *mcfg)
+{
+ dev_dbg(ctx->dev, "Dumping config\n");
+ dev_dbg(ctx->dev, "Input Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
+ dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
+ dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n",
+ mcfg->in_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "Output Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
+ dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n",
+ mcfg->out_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
+}
+
+static void skl_tplg_update_params(struct skl_module_fmt *fmt,
+ struct skl_pipe_params *params, int fixup)
+{
+ if (fixup & SKL_RATE_FIXUP_MASK)
+ fmt->s_freq = params->s_freq;
+ if (fixup & SKL_CH_FIXUP_MASK)
+ fmt->channels = params->ch;
+ if (fixup & SKL_FMT_FIXUP_MASK)
+ fmt->valid_bit_depth = params->s_fmt;
+}
+
+/*
+ * A pipeline may have modules which impact the pcm parameters, like SRC,
+ * channel converter, format converter.
+ * We need to calculate the output params by applying the 'fixup'
+ * Topology will tell driver which type of fixup is to be applied by
+ * supplying the fixup mask, so based on that we calculate the output
+ *
+ * Now In FE the pcm hw_params is source/target format. Same is applicable
+ * for BE with its hw_params invoked.
+ * here based on FE, BE pipeline and direction we calculate the input and
+ * outfix and then apply that for a module
+ */
+static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
+ struct skl_pipe_params *params, bool is_fe)
+{
+ int in_fixup, out_fixup;
+ struct skl_module_fmt *in_fmt, *out_fmt;
+
+ in_fmt = &m_cfg->in_fmt;
+ out_fmt = &m_cfg->out_fmt;
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (is_fe) {
+ in_fixup = m_cfg->params_fixup;
+ out_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ } else {
+ out_fixup = m_cfg->params_fixup;
+ in_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ }
+ } else {
+ if (is_fe) {
+ out_fixup = m_cfg->params_fixup;
+ in_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ } else {
+ in_fixup = m_cfg->params_fixup;
+ out_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ }
+ }
+
+ skl_tplg_update_params(in_fmt, params, in_fixup);
+ skl_tplg_update_params(out_fmt, params, out_fixup);
+}
+
+/*
+ * A module needs input and output buffers, which are dependent upon pcm
+ * params, so once we have calculate params, we need buffer calculation as
+ * well.
+ */
+static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
+ struct skl_module_cfg *mcfg)
+{
+ int multiplier = 1;
+
+ if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
+ multiplier = 5;
+
+ mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
+ (mcfg->in_fmt.channels) *
+ (mcfg->in_fmt.bit_depth >> 3) *
+ multiplier;
+
+ mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
+ (mcfg->out_fmt.channels) *
+ (mcfg->out_fmt.bit_depth >> 3) *
+ multiplier;
+}
+
+static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
+ struct skl_sst *ctx)
+{
+ struct skl_module_cfg *m_cfg = w->priv;
+ struct skl_pipe_params *params = m_cfg->pipe->p_params;
+ int p_conn_type = m_cfg->pipe->conn_type;
+ bool is_fe;
+
+ if (!m_cfg->params_fixup)
+ return;
+
+ dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n",
+ w->name);
+
+ skl_dump_mconfig(ctx, m_cfg);
+
+ if (p_conn_type == SKL_PIPE_CONN_TYPE_FE)
+ is_fe = true;
+ else
+ is_fe = false;
+
+ skl_tplg_update_params_fixup(m_cfg, params, is_fe);
+ skl_tplg_update_buffer_size(ctx, m_cfg);
+
+ dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n",
+ w->name);
+
+ skl_dump_mconfig(ctx, m_cfg);
+}
+
+/*
+ * A pipe can have multiple modules, each of them will be a DAPM widget as
+ * well. While managing a pipeline we need to get the list of all the
+ * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
+ * to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_alloc_pipe_widget(struct device *dev,
+ struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
+{
+ struct skl_module_cfg *src_module = NULL;
+ struct snd_soc_dapm_path *p = NULL;
+ struct skl_pipe_module *p_module = NULL;
+
+ p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
+ if (!p_module)
+ return -ENOMEM;
+
+ p_module->w = w;
+ list_add_tail(&p_module->node, &pipe->w_list);
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if ((p->sink->priv == NULL)
+ && (!is_skl_dsp_widget_type(w)))
+ continue;
+
+ if ((p->sink->priv != NULL) && p->connect
+ && is_skl_dsp_widget_type(p->sink)) {
+
+ src_module = p->sink->priv;
+ if (pipe->ppl_id == src_module->pipe->ppl_id)
+ skl_tplg_alloc_pipe_widget(dev,
+ p->sink, pipe);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Inside a pipe instance, we can have various modules. These modules need
+ * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
+ * skl_init_module() routine, so invoke that for all modules in a pipeline
+ */
+static int
+skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
+{
+ struct skl_pipe_module *w_module;
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret = 0;
+
+ list_for_each_entry(w_module, &pipe->w_list, node) {
+ w = w_module->w;
+ mconfig = w->priv;
+
+ /* check resource available */
+ if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ return -ENOMEM;
+
+ /*
+ * apply fix/conversion to module params based on
+ * FE/BE params
+ */
+ skl_tplg_update_module_params(w, ctx);
+ ret = skl_init_module(ctx, mconfig, NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
+ * need create the pipeline. So we do following:
+ * - check the resources
+ * - Create the pipeline
+ * - Initialize the modules in pipeline
+ * - finally bind all modules together
+ */
+static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret;
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ /* check resource available */
+ if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ return -EBUSY;
+
+ if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+ return -ENOMEM;
+
+ /*
+ * Create a list of modules for pipe.
+ * This list contains modules from source to sink
+ */
+ ret = skl_create_pipeline(ctx, mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * we create a w_list of all widgets in that pipe. This list is not
+ * freed on PMD event as widgets within a pipe are static. This
+ * saves us cycles to get widgets in pipe every time.
+ *
+ * So if we have already initialized all the widgets of a pipeline
+ * we skip, so check for list_empty and create the list if empty
+ */
+ if (list_empty(&s_pipe->w_list)) {
+ ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Init all pipe modules from source to sink */
+ ret = skl_tplg_init_pipe_modules(skl, s_pipe);
+ if (ret < 0)
+ return ret;
+
+ /* Bind modules from source to sink */
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_bind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ return 0;
+}
+
+/*
+ * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
+ * we need to do following:
+ * - Bind to sink pipeline
+ * Since the sink pipes can be running and we don't get mixer event on
+ * connect for already running mixer, we need to find the sink pipes
+ * here and bind to them. This way dynamic connect works.
+ * - Start sink pipeline, if not running
+ * - Then run current pipe
+ */
+static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_path *p;
+ struct skl_dapm_path_list *path_list;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret = 0;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ /*
+ * find which sink it is connected to, bind with the sink,
+ * if sink is not started, start sink pipe first, then start
+ * this pipe
+ */
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
+ dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->sink->priv != NULL) &&
+ is_skl_dsp_widget_type(p->sink)) {
+
+ sink = p->sink;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+
+ /* Bind source to sink, mixin is always source */
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ /* Start sinks pipe first */
+ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+ }
+
+ path_list = kzalloc(
+ sizeof(struct skl_dapm_path_list),
+ GFP_KERNEL);
+ if (path_list == NULL)
+ return -ENOMEM;
+
+ /* Add connected path to one global list */
+ path_list->dapm_path = p;
+ list_add_tail(&path_list->node, &skl->dapm_path_list);
+ break;
+ }
+ }
+
+ /* Start source pipe last after starting all sinks */
+ ret = skl_run_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * in the Post-PMU event of mixer we need to do following:
+ * - Check if this pipe is running
+ * - if not, then
+ * - bind this pipeline to its source pipeline
+ * if source pipe is already running, this means it is a dynamic
+ * connection and we need to bind only to that pipe
+ * - start this pipeline
+ */
+static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret = 0;
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int src_pipe_started = 0;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /*
+ * If source pipe is already started, that means source is driving
+ * one more sink before this sink got connected, Since source is
+ * started, bind this sink to source and start this pipe.
+ */
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
+ dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->source->priv != NULL) &&
+ is_skl_dsp_widget_type(p->source)) {
+ source = p->source;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+ src_pipe_started = 1;
+
+ /*
+ * check pipe state, then no need to bind or start
+ * the pipe
+ */
+ if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
+ src_pipe_started = 0;
+ }
+ }
+
+ if (src_pipe_started) {
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Pre-PMD event of mixer we need to do following:
+ * - Stop the pipe
+ * - find the source connections and remove that from dapm_path_list
+ * - unbind with source pipelines if still connected
+ */
+static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /* Stop the pipe */
+ ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ /*
+ * This list, dapm_path_list handling here does not need any locks
+ * as we are under dapm lock while handling widget events.
+ * List can be manipulated safely only under dapm widgets handler
+ * routines
+ */
+ list_for_each_entry_safe(path_list, tmp_list,
+ &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->sink == sink) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ source = path_list->dapm_path->source;
+ src_mconfig = source->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * If path_found == 1, that means pmd for source pipe has
+ * not occurred, source is connected to some other sink.
+ * so its responsibility of sink to unbind itself from source.
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of mixer we need to do following:
+ * - Free the mcps used
+ * - Free the mem used
+ * - Unbind the modules within the pipeline
+ * - Delete the pipeline (modules are not required to be explicitly
+ * deleted, pipeline delete is enough here
+ */
+static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ int ret = 0;
+
+ skl_tplg_free_pipe_mcps(skl, mconfig);
+
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_unbind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ ret = skl_delete_pipe(ctx, mconfig->pipe);
+ skl_tplg_free_pipe_mem(skl, mconfig);
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of PGA we need to do following:
+ * - Free the mcps used
+ * - Stop the pipeline
+ * - In source pipe is connected, unbind with source pipelines
+ */
+static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_path_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ skl_tplg_free_pipe_mcps(skl, src_mconfig);
+ /* Stop the pipe since this is a mixin module */
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->source == source) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ sink = path_list->dapm_path->sink;
+ sink_mconfig = sink->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * This is a connector and if path is found that means
+ * unbind between source and sink has not happened yet
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If
+ * mixer is not required then it is treated as static mixer aka vmixer with
+ * a hard path to source module
+ * So we don't need to check if source is started or not as hard path puts
+ * dependency on each other
+ */
+static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
+ * second one is required that is created as another pipe entity.
+ * The mixer is responsible for pipe management and represent a pipeline
+ * instance
+ */
+static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMU:
+ return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_PRE_PMD:
+ return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assumed rest of the modules in pipeline are PGA. But we
+ * are interested in last PGA (leaf PGA) in a pipeline to disconnect with
+ * the sink when it is running (two FE to one BE or one FE to two BE)
+ * scenarios
+ */
+static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_pga_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_pga_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * The FE params are passed by hw_params of the DAI.
+ * On hw_params, the params are stored in Gateway module of the FE and we
+ * need to calculate the format in DSP module configuration, that
+ * conversion is done here
+ */
+int skl_tplg_update_pipe_params(struct device *dev,
+ struct skl_module_cfg *mconfig,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mconfig->pipe;
+ struct skl_module_fmt *format = NULL;
+
+ memcpy(pipe->p_params, params, sizeof(*params));
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ format = &mconfig->in_fmt;
+ else
+ format = &mconfig->out_fmt;
+
+ /* set the hw_params */
+ format->s_freq = params->s_freq;
+ format->channels = params->ch;
+ format->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
+
+ /*
+ * 16 bit is 16 bit container whereas 24 bit is in 32 bit
+ * container so update bit depth accordingly
+ */
+ switch (format->valid_bit_depth) {
+ case SKL_DEPTH_16BIT:
+ format->bit_depth = format->valid_bit_depth;
+ break;
+
+ case SKL_DEPTH_24BIT:
+ format->bit_depth = SKL_DEPTH_32BIT;
+ break;
+
+ default:
+ dev_err(dev, "Invalid bit depth %x for pipe\n",
+ format->valid_bit_depth);
+ return -EINVAL;
+ }
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mconfig->ibs = (format->s_freq / 1000) *
+ (format->channels) *
+ (format->bit_depth >> 3);
+ } else {
+ mconfig->obs = (format->s_freq / 1000) *
+ (format->channels) *
+ (format->bit_depth >> 3);
+ }
+
+ return 0;
+}
+
+/*
+ * Query the module config for the FE DAI
+ * This is used to find the hw_params set for that DAI and apply to FE
+ * pipeline
+ */
+struct skl_module_cfg *
+skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
+{
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p = NULL;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w = dai->playback_widget;
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->connect && p->sink->power &&
+ is_skl_dsp_widget_type(p->sink))
+ continue;
+
+ if (p->sink->priv) {
+ dev_dbg(dai->dev, "set params for %s\n",
+ p->sink->name);
+ return p->sink->priv;
+ }
+ }
+ } else {
+ w = dai->capture_widget;
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->connect && p->source->power &&
+ is_skl_dsp_widget_type(p->source))
+ continue;
+
+ if (p->source->priv) {
+ dev_dbg(dai->dev, "set params for %s\n",
+ p->source->name);
+ return p->source->priv;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static u8 skl_tplg_be_link_type(int dev_type)
+{
+ int ret;
+
+ switch (dev_type) {
+ case SKL_DEVICE_BT:
+ ret = NHLT_LINK_SSP;
+ break;
+
+ case SKL_DEVICE_DMIC:
+ ret = NHLT_LINK_DMIC;
+ break;
+
+ case SKL_DEVICE_I2S:
+ ret = NHLT_LINK_SSP;
+ break;
+
+ case SKL_DEVICE_HDALINK:
+ ret = NHLT_LINK_HDA;
+ break;
+
+ default:
+ ret = NHLT_LINK_INVALID;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Fill the BE gateway parameters
+ * The BE gateway expects a blob of parameters which are kept in the ACPI
+ * NHLT blob, so query the blob for interface type (i2s/pdm) and instance.
+ * The port can have multiple settings so pick based on the PCM
+ * parameters
+ */
+static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
+ struct skl_module_cfg *mconfig,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mconfig->pipe;
+ struct nhlt_specific_cfg *cfg;
+ struct skl *skl = get_skl_ctx(dai->dev);
+ int link_type = skl_tplg_be_link_type(mconfig->dev_type);
+
+ memcpy(pipe->p_params, params, sizeof(*params));
+
+ /* update the blob based on virtual bus_id*/
+ cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
+ params->s_fmt, params->ch,
+ params->s_freq, params->stream);
+ if (cfg) {
+ mconfig->formats_config.caps_size = cfg->size;
+ mconfig->formats_config.caps = (u32 *) &cfg->caps;
+ } else {
+ dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n",
+ mconfig->vbus_id, link_type,
+ params->stream);
+ dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n",
+ params->ch, params->s_freq, params->s_fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
+ struct snd_soc_dapm_widget *w,
+ struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_path *p;
+ int ret = -EIO;
+
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->connect && is_skl_dsp_widget_type(p->source) &&
+ p->source->priv) {
+
+ if (!p->source->power) {
+ ret = skl_tplg_be_fill_pipe_params(
+ dai, p->source->priv,
+ params);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EBUSY;
+ }
+ } else {
+ ret = skl_tplg_be_set_src_pipe_params(
+ dai, p->source, params);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
+ struct snd_soc_dapm_widget *w, struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_path *p = NULL;
+ int ret = -EIO;
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->connect && is_skl_dsp_widget_type(p->sink) &&
+ p->sink->priv) {
+
+ if (!p->sink->power) {
+ ret = skl_tplg_be_fill_pipe_params(
+ dai, p->sink->priv, params);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EBUSY;
+ }
+
+ } else {
+ ret = skl_tplg_be_set_sink_pipe_params(
+ dai, p->sink, params);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * BE hw_params can be a source parameters (capture) or sink parameters
+ * (playback). Based on sink and source we need to either find the source
+ * list or the sink list and set the pipeline parameters
+ */
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_widget *w;
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w = dai->playback_widget;
+
+ return skl_tplg_be_set_src_pipe_params(dai, w, params);
+
+ } else {
+ w = dai->capture_widget;
+
+ return skl_tplg_be_set_sink_pipe_params(dai, w, params);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
+ {SKL_MIXER_EVENT, skl_tplg_mixer_event},
+ {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
+ {SKL_PGA_EVENT, skl_tplg_pga_event},
+};
+
+/*
+ * The topology binary passes the pin info for a module so initialize the pin
+ * info passed into module instance
+ */
+static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
+ struct skl_module_pin *m_pin,
+ bool is_dynamic, int max_pin)
+{
+ int i;
+
+ for (i = 0; i < max_pin; i++) {
+ m_pin[i].id.module_id = dfw_pin[i].module_id;
+ m_pin[i].id.instance_id = dfw_pin[i].instance_id;
+ m_pin[i].in_use = false;
+ m_pin[i].is_dynamic = is_dynamic;
+ }
+}
+
+/*
+ * Add pipeline from topology binary into driver pipeline list
+ *
+ * If already added we return that instance
+ * Otherwise we create a new instance and add into driver list
+ */
+static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
+ struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
+{
+ struct skl_pipeline *ppl;
+ struct skl_pipe *pipe;
+ struct skl_pipe_params *params;
+
+ list_for_each_entry(ppl, &skl->ppl_list, node) {
+ if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
+ return ppl->pipe;
+ }
+
+ ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
+ if (!ppl)
+ return NULL;
+
+ pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
+ if (!pipe)
+ return NULL;
+
+ params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return NULL;
+
+ pipe->ppl_id = dfw_pipe->pipe_id;
+ pipe->memory_pages = dfw_pipe->memory_pages;
+ pipe->pipe_priority = dfw_pipe->pipe_priority;
+ pipe->conn_type = dfw_pipe->conn_type;
+ pipe->state = SKL_PIPE_INVALID;
+ pipe->p_params = params;
+ INIT_LIST_HEAD(&pipe->w_list);
+
+ ppl->pipe = pipe;
+ list_add(&ppl->node, &skl->ppl_list);
+
+ return ppl->pipe;
+}
+
+/*
+ * Topology core widget load callback
+ *
+ * This is used to save the private data for each widget which gives
+ * information to the driver about module and pipeline parameters which DSP
+ * FW expects like ids, resource values, formats etc
+ */
+static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ int ret;
+ struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl_module_cfg *mconfig;
+ struct skl_pipe *pipe;
+ struct skl_dfw_module *dfw_config =
+ (struct skl_dfw_module *)tplg_w->priv.data;
+
+ if (!tplg_w->priv.size)
+ goto bind_event;
+
+ mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL);
+
+ if (!mconfig)
+ return -ENOMEM;
+
+ w->priv = mconfig;
+ mconfig->id.module_id = dfw_config->module_id;
+ mconfig->id.instance_id = dfw_config->instance_id;
+ mconfig->mcps = dfw_config->max_mcps;
+ mconfig->ibs = dfw_config->ibs;
+ mconfig->obs = dfw_config->obs;
+ mconfig->core_id = dfw_config->core_id;
+ mconfig->max_in_queue = dfw_config->max_in_queue;
+ mconfig->max_out_queue = dfw_config->max_out_queue;
+ mconfig->is_loadable = dfw_config->is_loadable;
+ mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
+ mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
+ mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
+ mconfig->in_fmt.valid_bit_depth =
+ dfw_config->in_fmt.valid_bit_depth;
+ mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
+ mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
+ mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
+ mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
+ mconfig->out_fmt.valid_bit_depth =
+ dfw_config->out_fmt.valid_bit_depth;
+ mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
+ mconfig->params_fixup = dfw_config->params_fixup;
+ mconfig->converter = dfw_config->converter;
+ mconfig->m_type = dfw_config->module_type;
+ mconfig->vbus_id = dfw_config->vbus_id;
+
+ pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
+ if (pipe)
+ mconfig->pipe = pipe;
+
+ mconfig->dev_type = dfw_config->dev_type;
+ mconfig->hw_conn_type = dfw_config->hw_conn_type;
+ mconfig->time_slot = dfw_config->time_slot;
+ mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
+
+ mconfig->m_in_pin = devm_kzalloc(bus->dev,
+ (mconfig->max_in_queue) *
+ sizeof(*mconfig->m_in_pin),
+ GFP_KERNEL);
+ if (!mconfig->m_in_pin)
+ return -ENOMEM;
+
+ mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
+ sizeof(*mconfig->m_out_pin),
+ GFP_KERNEL);
+ if (!mconfig->m_out_pin)
+ return -ENOMEM;
+
+ skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
+ dfw_config->is_dynamic_in_pin,
+ mconfig->max_in_queue);
+
+ skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
+ dfw_config->is_dynamic_out_pin,
+ mconfig->max_out_queue);
+
+
+ if (mconfig->formats_config.caps_size == 0)
+ goto bind_event;
+
+ mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
+ mconfig->formats_config.caps_size, GFP_KERNEL);
+
+ if (mconfig->formats_config.caps == NULL)
+ return -ENOMEM;
+
+ memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
+ dfw_config->caps.caps_size);
+
+bind_event:
+ if (tplg_w->event_type == 0) {
+ dev_dbg(bus->dev, "ASoC: No event handler required\n");
+ return 0;
+ }
+
+ ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops,
+ ARRAY_SIZE(skl_tplg_widget_ops),
+ tplg_w->event_type);
+
+ if (ret) {
+ dev_err(bus->dev, "%s: No matching event handlers found for %d\n",
+ __func__, tplg_w->event_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_tplg_ops skl_tplg_ops = {
+ .widget_load = skl_tplg_widget_load,
+};
+
+/* This will be read from topology manifest, currently defined here */
+#define SKL_MAX_MCPS 30000000
+#define SKL_FW_MAX_MEM 1000000
+
+/*
+ * SKL topology init routine
+ */
+int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
+{
+ int ret;
+ const struct firmware *fw;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+ if (ret < 0) {
+ dev_err(bus->dev, "tplg fw %s load failed with %d\n",
+ "dfw_sst.bin", ret);
+ return ret;
+ }
+
+ /*
+ * The complete tplg for SKL is loaded as index 0, we don't use
+ * any other index
+ */
+ ret = snd_soc_tplg_component_load(&platform->component,
+ &skl_tplg_ops, fw, 0);
+ if (ret < 0) {
+ dev_err(bus->dev, "tplg component load failed%d\n", ret);
+ return -EINVAL;
+ }
+
+ skl->resource.max_mcps = SKL_MAX_MCPS;
+ skl->resource.max_mem = SKL_FW_MAX_MEM;
+
+ return 0;
+}
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index 8c7767baa94f..76053a8de41c 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -129,6 +129,11 @@ struct skl_src_module_cfg {
enum skl_s_freq src_cfg;
} __packed;
+struct notification_mask {
+ u32 notify;
+ u32 enable;
+} __packed;
+
struct skl_up_down_mixer_cfg {
struct skl_base_cfg base_cfg;
enum skl_ch_cfg out_ch_cfg;
@@ -153,8 +158,7 @@ enum skl_dma_type {
union skl_ssp_dma_node {
u8 val;
struct {
- u8 dual_mono:1;
- u8 time_slot:3;
+ u8 time_slot_index:4;
u8 i2s_instance:4;
} dma_node;
};
@@ -263,6 +267,34 @@ struct skl_module_cfg {
struct skl_specific_cfg formats_config;
};
+struct skl_pipeline {
+ struct skl_pipe *pipe;
+ struct list_head node;
+};
+
+struct skl_dapm_path_list {
+ struct snd_soc_dapm_path *dapm_path;
+ struct list_head node;
+};
+
+static inline struct skl *get_skl_ctx(struct device *dev)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+
+ return ebus_to_skl(ebus);
+}
+
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params);
+void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params, int stream);
+int skl_tplg_init(struct snd_soc_platform *platform,
+ struct hdac_ext_bus *ebus);
+struct skl_module_cfg *skl_tplg_fe_get_cpr_module(
+ struct snd_soc_dai *dai, int stream);
+int skl_tplg_update_pipe_params(struct device *dev,
+ struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+
int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h
index a50689825bca..2bc396d54cbe 100644
--- a/sound/soc/intel/skylake/skl-tplg-interface.h
+++ b/sound/soc/intel/skylake/skl-tplg-interface.h
@@ -19,6 +19,29 @@
#ifndef __HDA_TPLG_INTERFACE_H__
#define __HDA_TPLG_INTERFACE_H__
+/*
+ * Default types range from 0~12. type can range from 0 to 0xff
+ * SST types start at higher to avoid any overlapping in future
+ */
+#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100
+#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101
+#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101
+#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103
+
+#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/
+#define MAX_IN_QUEUE 8
+#define MAX_OUT_QUEUE 8
+
+/* Event types goes here */
+/* Reserve event type 0 for no event handlers */
+enum skl_event_types {
+ SKL_EVENT_NONE = 0,
+ SKL_MIXER_EVENT,
+ SKL_MUX_EVENT,
+ SKL_VMIXER_EVENT,
+ SKL_PGA_EVENT
+};
+
/**
* enum skl_ch_cfg - channel configuration
*
@@ -83,6 +106,66 @@ enum skl_dev_type {
SKL_DEVICE_I2S = 0x2,
SKL_DEVICE_SLIMBUS = 0x3,
SKL_DEVICE_HDALINK = 0x4,
+ SKL_DEVICE_HDAHOST = 0x5,
SKL_DEVICE_NONE
};
+
+struct skl_dfw_module_pin {
+ u16 module_id;
+ u16 instance_id;
+} __packed;
+
+struct skl_dfw_module_fmt {
+ u32 channels;
+ u32 freq;
+ u32 bit_depth;
+ u32 valid_bit_depth;
+ u32 ch_cfg;
+} __packed;
+
+struct skl_dfw_module_caps {
+ u32 caps_size;
+ u32 caps[HDA_SST_CFG_MAX];
+};
+
+struct skl_dfw_pipe {
+ u8 pipe_id;
+ u8 pipe_priority;
+ u16 conn_type;
+ u32 memory_pages;
+} __packed;
+
+struct skl_dfw_module {
+ u16 module_id;
+ u16 instance_id;
+ u32 max_mcps;
+ u8 core_id;
+ u8 max_in_queue;
+ u8 max_out_queue;
+ u8 is_loadable;
+ u8 conn_type;
+ u8 dev_type;
+ u8 hw_conn_type;
+ u8 time_slot;
+ u32 obs;
+ u32 ibs;
+ u32 params_fixup;
+ u32 converter;
+ u32 module_type;
+ u32 vbus_id;
+ u8 is_dynamic_in_pin;
+ u8 is_dynamic_out_pin;
+ struct skl_dfw_pipe pipe;
+ struct skl_dfw_module_fmt in_fmt;
+ struct skl_dfw_module_fmt out_fmt;
+ struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
+ struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
+ struct skl_dfw_module_caps caps;
+} __packed;
+
+struct skl_dfw_algo_data {
+ u32 max;
+ char *params;
+} __packed;
+
#endif
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 348d094e81d6..5319529aedf7 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -166,12 +166,20 @@ static int skl_runtime_suspend(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+ int ret;
dev_dbg(bus->dev, "in %s\n", __func__);
/* enable controller wake up event */
snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
+ snd_hdac_ext_bus_link_power_down_all(ebus);
+
+ ret = skl_suspend_dsp(skl);
+ if (ret < 0)
+ return ret;
+
snd_hdac_bus_stop_chip(bus);
snd_hdac_bus_enter_link_reset(bus);
@@ -183,7 +191,7 @@ static int skl_runtime_resume(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl *hda = ebus_to_skl(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
int status;
dev_dbg(bus->dev, "in %s\n", __func__);
@@ -191,12 +199,12 @@ static int skl_runtime_resume(struct device *dev)
/* Read STATESTS before controller reset */
status = snd_hdac_chip_readw(bus, STATESTS);
- skl_init_pci(hda);
+ skl_init_pci(skl);
snd_hdac_bus_init_chip(bus, true);
/* disable controller Wake Up event */
snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
- return 0;
+ return skl_resume_dsp(skl);
}
#endif /* CONFIG_PM */
@@ -453,21 +461,28 @@ static int skl_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;
+ skl->nhlt = skl_nhlt_init(bus->dev);
+
+ if (skl->nhlt == NULL)
+ goto out_free;
+
pci_set_drvdata(skl->pci, ebus);
/* check if dsp is there */
if (ebus->ppcap) {
- /* TODO register with dsp IPC */
- dev_dbg(bus->dev, "Register dsp\n");
+ err = skl_init_dsp(skl);
+ if (err < 0) {
+ dev_dbg(bus->dev, "error failed to register dsp\n");
+ goto out_free;
+ }
}
-
if (ebus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(ebus);
/* create device for soc dmic */
err = skl_dmic_device_register(skl);
if (err < 0)
- goto out_free;
+ goto out_dsp_free;
/* register platform dai and controls */
err = skl_platform_register(bus->dev);
@@ -491,6 +506,8 @@ out_unregister:
skl_platform_unregister(bus->dev);
out_dmic_free:
skl_dmic_device_unregister(skl);
+out_dsp_free:
+ skl_free_dsp(skl);
out_free:
skl->init_failed = 1;
skl_free(ebus);
@@ -507,6 +524,7 @@ static void skl_remove(struct pci_dev *pci)
pm_runtime_get_noresume(&pci->dev);
pci_dev_put(pci);
skl_platform_unregister(&pci->dev);
+ skl_free_dsp(skl);
skl_dmic_device_unregister(skl);
skl_free(ebus);
dev_set_drvdata(&pci->dev, NULL);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index f7fdbb02947f..dd2e79ae45a8 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -48,6 +48,13 @@
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
+struct skl_dsp_resource {
+ u32 max_mcps;
+ u32 max_mem;
+ u32 mcps;
+ u32 mem;
+};
+
struct skl {
struct hdac_ext_bus ebus;
struct pci_dev *pci;
@@ -55,8 +62,12 @@ struct skl {
unsigned int init_failed:1; /* delayed init failed */
struct platform_device *dmic_dev;
- void __iomem *nhlt; /* nhlt ptr */
+ void *nhlt; /* nhlt ptr */
struct skl_sst *skl_sst; /* sst skl ctx */
+
+ struct skl_dsp_resource resource;
+ struct list_head ppl_list;
+ struct list_head dapm_path_list;
};
#define skl_to_ebus(s) (&(s)->ebus)
@@ -72,8 +83,8 @@ struct skl_dma_params {
int skl_platform_unregister(struct device *dev);
int skl_platform_register(struct device *dev);
-void __iomem *skl_nhlt_init(struct device *dev);
-void skl_nhlt_free(void __iomem *addr);
+void *skl_nhlt_init(struct device *dev);
+void skl_nhlt_free(void *addr);
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index b05fb1c1a848..794a3499e567 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -485,6 +485,7 @@ static const struct of_device_id jz4740_of_matches[] = {
{ .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, jz4740_of_matches);
#endif
static int jz4740_i2s_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c
index de7563bdc5c2..e0304d544f26 100644
--- a/sound/soc/kirkwood/armada-370-db.c
+++ b/sound/soc/kirkwood/armada-370-db.c
@@ -130,6 +130,7 @@ static const struct of_device_id a370db_dt_ids[] = {
{ .compatible = "marvell,a370db-audio" },
{ },
};
+MODULE_DEVICE_TABLE(of, a370db_dt_ids);
static struct platform_driver a370db_driver = {
.driver = {
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c
index 684e8a78bed0..71a1a35047ba 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173-max98090.c
@@ -179,21 +179,13 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
}
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
-static int mt8173_max98090_dev_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static const struct of_device_id mt8173_max98090_dt_match[] = {
{ .compatible = "mediatek,mt8173-max98090", },
{ }
@@ -209,7 +201,6 @@ static struct platform_driver mt8173_max98090_driver = {
#endif
},
.probe = mt8173_max98090_dev_probe,
- .remove = mt8173_max98090_dev_remove,
};
module_platform_driver(mt8173_max98090_driver);
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
index 86cf9752f18a..50ba538eccb3 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
@@ -246,21 +246,13 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
-static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = {
{ .compatible = "mediatek,mt8173-rt5650-rt5676", },
{ }
@@ -276,7 +268,6 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = {
#endif
},
.probe = mt8173_rt5650_rt5676_dev_probe,
- .remove = mt8173_rt5650_rt5676_dev_remove,
};
module_platform_driver(mt8173_rt5650_rt5676_driver);
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index 6e6fce6a14ba..2b23ffbac6b1 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
@@ -154,12 +154,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
static int mxs_sgtl5000_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
mxs_saif_put_mclk(0);
- snd_soc_unregister_card(card);
-
return 0;
}
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index dcb5336b5698..190f868e78b2 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -99,8 +99,7 @@ static int n810_startup(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
n810_ext_control(&rtd->card->dapm);
return clk_prepare_enable(sys_clkout2);
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 3bebfb1d3a6f..5e21f08579d8 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -107,8 +107,7 @@ static int rx51_startup(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
rx51_ext_control(&card->dapm);
return 0;
@@ -297,7 +296,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
return err;
}
- snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
+ snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
err = omap_mcbsp_st_add_controls(rtd, 2);
if (err < 0) {
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index 2b26318bc200..6147e86e9b0f 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -116,26 +116,19 @@ static int brownstone_probe(struct platform_device *pdev)
int ret;
brownstone.dev = &pdev->dev;
- ret = snd_soc_register_card(&brownstone);
+ ret = devm_snd_soc_register_card(&pdev->dev, &brownstone);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int brownstone_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&brownstone);
- return 0;
-}
-
static struct platform_driver mmp_driver = {
.driver = {
.name = "brownstone-audio",
.pm = &snd_soc_pm_ops,
},
.probe = brownstone_probe,
- .remove = brownstone_remove,
};
module_platform_driver(mmp_driver);
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 3580d10c9f28..c97dc13d3608 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -295,28 +295,19 @@ static int corgi_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int corgi_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver corgi_driver = {
.driver = {
.name = "corgi-audio",
.pm = &snd_soc_pm_ops,
},
.probe = corgi_probe,
- .remove = corgi_remove,
};
module_platform_driver(corgi_driver);
diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c
index d72e124a3676..1de876529aa1 100644
--- a/sound/soc/pxa/e740_wm9705.c
+++ b/sound/soc/pxa/e740_wm9705.c
@@ -138,7 +138,7 @@ static int e740_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -149,10 +149,7 @@ static int e740_probe(struct platform_device *pdev)
static int e740_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
index 48f2d7c2e68c..b7eb7cd5df7d 100644
--- a/sound/soc/pxa/e750_wm9705.c
+++ b/sound/soc/pxa/e750_wm9705.c
@@ -120,7 +120,7 @@ static int e750_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -131,10 +131,7 @@ static int e750_probe(struct platform_device *pdev)
static int e750_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 45d4bd46fff6..41bf71466a7b 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -119,7 +119,7 @@ static int e800_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -130,10 +130,7 @@ static int e800_probe(struct platform_device *pdev)
static int e800_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
index 9f8be7cd567e..ecbf2873b7ff 100644
--- a/sound/soc/pxa/hx4700.c
+++ b/sound/soc/pxa/hx4700.c
@@ -193,7 +193,7 @@ static int hx4700_audio_probe(struct platform_device *pdev)
return ret;
snd_soc_card_hx4700.dev = &pdev->dev;
- ret = snd_soc_register_card(&snd_soc_card_hx4700);
+ ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
if (ret)
gpio_free_array(hx4700_audio_gpios,
ARRAY_SIZE(hx4700_audio_gpios));
@@ -203,8 +203,6 @@ static int hx4700_audio_probe(struct platform_device *pdev)
static int hx4700_audio_remove(struct platform_device *pdev)
{
- snd_soc_unregister_card(&snd_soc_card_hx4700);
-
gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c
index 29fabbfd21f1..9d0e40771ef5 100644
--- a/sound/soc/pxa/imote2.c
+++ b/sound/soc/pxa/imote2.c
@@ -72,28 +72,19 @@ static int imote2_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int imote2_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver imote2_driver = {
.driver = {
.name = "imote2-audio",
.pm = &snd_soc_pm_ops,
},
.probe = imote2_probe,
- .remove = imote2_remove,
};
module_platform_driver(imote2_driver);
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index a9615a574546..29bc60e85e92 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -181,7 +181,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
return -ENODEV;
mioa701.dev = &pdev->dev;
- rc = snd_soc_register_card(&mioa701);
+ rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
if (!rc)
dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
"lead to overheating and possible destruction of your device."
@@ -189,17 +189,8 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
return rc;
}
-static int mioa701_wm9713_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver mioa701_wm9713_driver = {
.probe = mioa701_wm9713_probe,
- .remove = mioa701_wm9713_remove,
.driver = {
.name = "mioa701-wm9713",
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
index c20bbc042425..4e74d9573f03 100644
--- a/sound/soc/pxa/palm27x.c
+++ b/sound/soc/pxa/palm27x.c
@@ -140,22 +140,15 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
palm27x_asoc.dev = &pdev->dev;
- ret = snd_soc_register_card(&palm27x_asoc);
+ ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int palm27x_asoc_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&palm27x_asoc);
- return 0;
-}
-
static struct platform_driver palm27x_wm9712_driver = {
.probe = palm27x_asoc_probe,
- .remove = palm27x_asoc_remove,
.driver = {
.name = "palm27x-asoc",
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 80b457ac522a..84d0e2e50808 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -267,28 +267,19 @@ static int poodle_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int poodle_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver poodle_driver = {
.driver = {
.name = "poodle-audio",
.pm = &snd_soc_pm_ops,
},
.probe = poodle_probe,
- .remove = poodle_remove,
};
module_platform_driver(poodle_driver);
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 3da485ec1de7..da03fad1b9cd 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -809,6 +809,7 @@ static const struct of_device_id pxa_ssp_of_ids[] = {
{ .compatible = "mrvl,pxa-ssp-dai" },
{}
};
+MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
#endif
static int asoc_ssp_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 9e4b04e0fbd1..f3de615aacd7 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/ac97_codec.h>
@@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_cold_reset,
};
-static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 11,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
.filter_data = &pxa2xx_ac97_pcm_stereo_in_req,
};
-static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 12,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
.filter_data = &pxa2xx_ac97_pcm_stereo_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 10,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
.addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
.filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 9,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
.addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
.filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 8,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
.addr = __PREG(MCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -89,9 +107,8 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
.filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req,
};
-static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -105,9 +122,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -121,9 +137,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
@@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_48000)
static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_params,
+ .startup = pxa2xx_ac97_hifi_startup,
};
static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_aux_params,
+ .startup = pxa2xx_ac97_aux_startup,
};
static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_mic_params,
+ .startup = pxa2xx_ac97_mic_startup,
};
/*
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 6b4e40036910..0389cf7b4b1e 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
/* Along with FIFO servicing */
SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
+ snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
+ &pxa2xx_i2s_pcm_stereo_in);
+
return 0;
}
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 831ee37d2e3e..9f390398d518 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -15,8 +15,6 @@
#include <linux/dmaengine.h>
#include <linux/of.h>
-#include <mach/dma.h>
-
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
@@ -27,11 +25,8 @@
static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- 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 snd_dmaengine_dai_dma_data *dma;
- int ret;
dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
@@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
if (!dma)
return 0;
- /* this may get called several times by oss emulation
- * with different params */
- if (prtd->params == NULL) {
- prtd->params = dma;
- ret = pxa_request_dma("name", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- return ret;
- prtd->dma_ch = ret;
- } else if (prtd->params != dma) {
- pxa_free_dma(prtd->dma_ch);
- prtd->params = dma;
- ret = pxa_request_dma("name", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- return ret;
- prtd->dma_ch = ret;
- }
-
return __pxa2xx_pcm_hw_params(substream, params);
}
static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
__pxa2xx_pcm_hw_free(substream);
- if (prtd->dma_ch >= 0) {
- pxa_free_dma(prtd->dma_ch);
- prtd->dma_ch = -1;
- prtd->params = NULL;
- }
-
return 0;
}
@@ -132,6 +100,7 @@ static const struct of_device_id snd_soc_pxa_audio_match[] = {
{ .compatible = "mrvl,pxa-pcm-audio" },
{ }
};
+MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match);
#endif
static struct platform_driver pxa_pcm_driver = {
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 461123ad5ff2..b00222620fd0 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -305,7 +305,7 @@ static int spitz_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -322,9 +322,6 @@ err1:
static int spitz_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
gpio_free(spitz_mic_gpio);
return 0;
}
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index f59f566551ef..49518dd642aa 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -233,7 +233,7 @@ static int tosa_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -244,10 +244,7 @@ static int tosa_probe(struct platform_device *pdev)
static int tosa_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free(TOSA_GPIO_L_MUTE);
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c
index 1753c7d9e760..65c20f779177 100644
--- a/sound/soc/pxa/ttc-dkb.c
+++ b/sound/soc/pxa/ttc-dkb.c
@@ -128,7 +128,7 @@ static int ttc_dkb_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -136,22 +136,12 @@ static int ttc_dkb_probe(struct platform_device *pdev)
return ret;
}
-static int ttc_dkb_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
-}
-
static struct platform_driver ttc_dkb_driver = {
.driver = {
.name = "ttc-dkb-audio",
.pm = &snd_soc_pm_ops,
},
.probe = ttc_dkb_probe,
- .remove = ttc_dkb_remove,
};
module_platform_driver(ttc_dkb_driver);
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 97bc2023f08a..e5101e0d2d37 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -438,7 +438,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
dev_err(&pdev->dev,
"%s() error getting mi2s-bit-clk: %ld\n",
- __func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+ __func__,
+ PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
}
}
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index 58bae8e2cf5f..f1e0c703e0d2 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -15,9 +15,17 @@ config SND_SOC_ROCKCHIP_I2S
Rockchip I2S device. The device supports upto maximum of
8 channels each for play and record.
+config SND_SOC_ROCKCHIP_SPDIF
+ tristate "Rockchip SPDIF Device Driver"
+ depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for SPDIF driver for
+ Rockchip SPDIF transceiver device.
+
config SND_SOC_ROCKCHIP_MAX98090
tristate "ASoC support for Rockchip boards using a MAX98090 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
@@ -27,7 +35,7 @@ config SND_SOC_ROCKCHIP_MAX98090
config SND_SOC_ROCKCHIP_RT5645
tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_RT5645
help
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index 1bc1dc3c729a..c0bf560125f3 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -1,7 +1,9 @@
# ROCKCHIP Platform Support
-snd-soc-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-spdif-objs := rockchip_spdif.o
-obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
snd-soc-rockchip-max98090-objs := rockchip_max98090.o
snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index b93610212e3d..58ee64594f07 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -226,6 +226,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = to_info(dai);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int val = 0;
switch (params_format(params)) {
@@ -245,13 +246,46 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val);
- regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val);
+ switch (params_channels(params)) {
+ case 8:
+ val |= I2S_CHN_8;
+ break;
+ case 6:
+ val |= I2S_CHN_6;
+ break;
+ case 4:
+ val |= I2S_CHN_4;
+ break;
+ case 2:
+ val |= I2S_CHN_2;
+ break;
+ default:
+ dev_err(i2s->dev, "invalid channel: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ regmap_update_bits(i2s->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ val);
+ else
+ regmap_update_bits(i2s->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ val);
+
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
I2S_DMACR_TDL(16));
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
I2S_DMACR_RDL(16));
+ val = I2S_CKR_TRCM_TXRX;
+ if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
+ val = I2S_CKR_TRCM_TXSHARE;
+
+ regmap_update_bits(i2s->regmap, I2S_CKR,
+ I2S_CKR_TRCM_MASK,
+ val);
return 0;
}
@@ -415,10 +449,12 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
static int rockchip_i2s_probe(struct platform_device *pdev)
{
+ struct device_node *node = pdev->dev.of_node;
struct rk_i2s_dev *i2s;
struct resource *res;
void __iomem *regs;
int ret;
+ int val;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s) {
@@ -475,6 +511,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_pm_disable;
}
+ /* refine capture channels */
+ if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
+ if (val >= 2 && val <= 8)
+ rockchip_i2s_dai.capture.channels_max = val;
+ else
+ rockchip_i2s_dai.capture.channels_max = 2;
+ }
+
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_i2s_component,
&rockchip_i2s_dai, 1);
diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h
index 93f456f518a9..dc6e2c74d088 100644
--- a/sound/soc/rockchip/rockchip_i2s.h
+++ b/sound/soc/rockchip/rockchip_i2s.h
@@ -49,6 +49,9 @@
* RXCR
* receive operation control register
*/
+#define I2S_RXCR_CSR_SHIFT 15
+#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_HWT BIT(14)
#define I2S_RXCR_SJM_SHIFT 12
#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
@@ -75,6 +78,12 @@
* CKR
* clock generation register
*/
+#define I2S_CKR_TRCM_SHIFT 28
+#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_MSS_SHIFT 27
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
@@ -207,6 +216,13 @@ enum {
ROCKCHIP_DIV_BCLK,
};
+/* channel select */
+#define I2S_CSR_SHIFT 15
+#define I2S_CHN_2 (0 << I2S_CSR_SHIFT)
+#define I2S_CHN_4 (1 << I2S_CSR_SHIFT)
+#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
+#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
+
/* I2S REGS */
#define I2S_TXCR (0x0000)
#define I2S_RXCR (0x0004)
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
new file mode 100644
index 000000000000..a38a3029062c
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -0,0 +1,405 @@
+/* sound/soc/rockchip/rk_spdif.c
+ *
+ * ALSA SoC Audio Layer - Rockchip I2S Controller driver
+ *
+ * Copyright (c) 2014 Rockchip Electronics Co. Ltd.
+ * Author: Jianqun <jay.xu@rock-chips.com>
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "rockchip_spdif.h"
+
+enum rk_spdif_type {
+ RK_SPDIF_RK3066,
+ RK_SPDIF_RK3188,
+ RK_SPDIF_RK3288,
+};
+
+#define RK3288_GRF_SOC_CON2 0x24c
+
+struct rk_spdif_dev {
+ struct device *dev;
+
+ struct clk *mclk;
+ struct clk *hclk;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+
+ struct regmap *regmap;
+};
+
+static const struct of_device_id rk_spdif_match[] = {
+ { .compatible = "rockchip,rk3066-spdif",
+ .data = (void *) RK_SPDIF_RK3066 },
+ { .compatible = "rockchip,rk3188-spdif",
+ .data = (void *) RK_SPDIF_RK3188 },
+ { .compatible = "rockchip,rk3288-spdif",
+ .data = (void *) RK_SPDIF_RK3288 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk_spdif_match);
+
+static int rk_spdif_runtime_suspend(struct device *dev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(spdif->mclk);
+ clk_disable_unprepare(spdif->hclk);
+
+ return 0;
+}
+
+static int rk_spdif_runtime_resume(struct device *dev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdif->mclk);
+ if (ret) {
+ dev_err(spdif->dev, "mclk clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(spdif->hclk);
+ if (ret) {
+ dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
+ int srate, mclk;
+ int ret;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 32000:
+ case 48000:
+ case 96000:
+ mclk = 96000 * 128; /* 12288000 hz */
+ break;
+ case 44100:
+ mclk = 44100 * 256; /* 11289600 hz */
+ break;
+ case 192000:
+ mclk = 192000 * 128; /* 24576000 hz */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= SPDIF_CFGR_VDW_16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= SPDIF_CFGR_VDW_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= SPDIF_CFGR_VDW_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set clock and calculate divider */
+ ret = clk_set_rate(spdif->mclk, mclk);
+ if (ret != 0) {
+ dev_err(spdif->dev, "Failed to set module clock rate: %d\n",
+ ret);
+ return ret;
+ }
+
+ val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
+ ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
+ SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
+ SDPIF_CFGR_VDW_MASK,
+ val);
+
+ return ret;
+}
+
+static int rk_spdif_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+ SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_ENABLE);
+
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_START);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+ SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_DISABLE);
+
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_STOP);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &spdif->playback_dma_data;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
+ .hw_params = rk_spdif_hw_params,
+ .trigger = rk_spdif_trigger,
+};
+
+static struct snd_soc_dai_driver rk_spdif_dai = {
+ .probe = rk_spdif_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &rk_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver rk_spdif_component = {
+ .name = "rockchip-spdif",
+};
+
+static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_CFGR:
+ case SPDIF_DMACR:
+ case SPDIF_INTCR:
+ case SPDIF_XFER:
+ case SPDIF_SMPDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_CFGR:
+ case SPDIF_SDBLR:
+ case SPDIF_INTCR:
+ case SPDIF_INTSR:
+ case SPDIF_XFER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_INTSR:
+ case SPDIF_SDBLR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rk_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SPDIF_SMPDR,
+ .writeable_reg = rk_spdif_wr_reg,
+ .readable_reg = rk_spdif_rd_reg,
+ .volatile_reg = rk_spdif_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int rk_spdif_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct rk_spdif_dev *spdif;
+ const struct of_device_id *match;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ match = of_match_node(rk_spdif_match, np);
+ if ((int) match->data == RK_SPDIF_RK3288) {
+ struct regmap *grf;
+
+ grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev,
+ "rockchip_spdif missing 'rockchip,grf' \n");
+ return PTR_ERR(grf);
+ }
+
+ /* Select the 8 channel SPDIF solution on RK3288 as
+ * the 2 channel one does not appear to work
+ */
+ regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16);
+ }
+
+ spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
+ if (!spdif)
+ return -ENOMEM;
+
+ spdif->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(spdif->hclk)) {
+ dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n");
+ return PTR_ERR(spdif->hclk);
+ }
+ ret = clk_prepare_enable(spdif->hclk);
+ if (ret) {
+ dev_err(spdif->dev, "hclock enable failed %d\n", ret);
+ return ret;
+ }
+
+ spdif->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(spdif->mclk)) {
+ dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n");
+ return PTR_ERR(spdif->mclk);
+ }
+
+ ret = clk_prepare_enable(spdif->mclk);
+ if (ret) {
+ dev_err(spdif->dev, "clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs,
+ &rk_spdif_regmap_config);
+ if (IS_ERR(spdif->regmap)) {
+ dev_err(&pdev->dev,
+ "Failed to initialise managed register map\n");
+ return PTR_ERR(spdif->regmap);
+ }
+
+ spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR;
+ spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ spdif->playback_dma_data.maxburst = 4;
+
+ spdif->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_request_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rk_spdif_component,
+ &rk_spdif_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI\n");
+ goto err_pm_runtime;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM\n");
+ goto err_pm_runtime;
+ }
+
+ return 0;
+
+err_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int rk_spdif_remove(struct platform_device *pdev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ rk_spdif_runtime_suspend(&pdev->dev);
+
+ clk_disable_unprepare(spdif->mclk);
+ clk_disable_unprepare(spdif->hclk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rk_spdif_pm_ops = {
+ SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver rk_spdif_driver = {
+ .probe = rk_spdif_probe,
+ .remove = rk_spdif_remove,
+ .driver = {
+ .name = "rockchip-spdif",
+ .of_match_table = of_match_ptr(rk_spdif_match),
+ .pm = &rk_spdif_pm_ops,
+ },
+};
+module_platform_driver(rk_spdif_driver);
+
+MODULE_ALIAS("platform:rockchip-spdif");
+MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface");
+MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.co.uk>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
new file mode 100644
index 000000000000..07f86a21046a
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_spdif.h
@@ -0,0 +1,63 @@
+/*
+ * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
+ *
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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 _ROCKCHIP_SPDIF_H
+#define _ROCKCHIP_SPDIF_H
+
+/*
+ * CFGR
+ * transfer configuration register
+*/
+#define SPDIF_CFGR_CLK_DIV_SHIFT (16)
+#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT)
+#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT)
+
+#define SPDIF_CFGR_HALFWORD_SHIFT 2
+#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT)
+#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT)
+
+#define SPDIF_CFGR_VDW_SHIFT 0
+#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT)
+#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT)
+
+#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x00)
+#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x01)
+#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x10)
+
+/*
+ * DMACR
+ * DMA control register
+*/
+#define SPDIF_DMACR_TDE_SHIFT 5
+#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT)
+#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT)
+
+#define SPDIF_DMACR_TDL_SHIFT 0
+#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT)
+#define SPDIF_DMACR_TDL_MASK (0x1f << SDPIF_DMACR_TDL_SHIFT)
+
+/*
+ * XFER
+ * Transfer control register
+*/
+#define SPDIF_XFER_TXS_SHIFT 0
+#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT)
+#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT)
+
+#define SPDIF_CFGR (0x0000)
+#define SPDIF_SDBLR (0x0004)
+#define SPDIF_DMACR (0x0008)
+#define SPDIF_INTCR (0x000c)
+#define SPDIF_INTSR (0x0010)
+#define SPDIF_XFER (0x0018)
+#define SPDIF_SMPDR (0x0020)
+
+#endif /* _ROCKCHIP_SPDIF_H */
diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c
index c72e9fb26658..5f5825faeb2a 100644
--- a/sound/soc/samsung/h1940_uda1380.c
+++ b/sound/soc/samsung/h1940_uda1380.c
@@ -26,16 +26,15 @@
#include <mach/gpio-samsung.h>
#include "s3c24xx-i2s.h"
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
11025,
22050,
44100,
};
-static struct snd_pcm_hw_constraint_list hw_rates = {
+static const struct snd_pcm_hw_constraint_list hw_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
- .mask = 0,
};
static struct snd_soc_jack hp_jack;
diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c
index 35e37c457f1f..fa096abe9e75 100644
--- a/sound/soc/samsung/rx1950_uda1380.c
+++ b/sound/soc/samsung/rx1950_uda1380.c
@@ -38,16 +38,15 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream,
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
16000,
44100,
48000,
};
-static struct snd_pcm_hw_constraint_list hw_rates = {
+static const struct snd_pcm_hw_constraint_list hw_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
- .mask = 0,
};
static struct snd_soc_jack hp_jack;
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 07114b0b0dc1..206d1edab07c 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -37,10 +37,11 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on DMA_OF
+ depends on COMMON_CLK
select SND_SIMPLE_CARD
select REGMAP_MMIO
help
- This option enables R-Car SUR/SCU/SSIU/SSI sound support
+ This option enables R-Car SRU/SCU/SSIU/SSI sound support
config SND_SOC_RSRC_CARD
tristate "Renesas Sampling Rate Convert Sound Card"
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index fefc881dbac2..2a5b3a293cd2 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -7,7 +7,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
-#include <linux/sh_clk.h>
+#include <linux/clk-provider.h>
#include "rsnd.h"
#define CLKA 0
@@ -16,12 +16,26 @@
#define CLKI 3
#define CLKMAX 4
+#define CLKOUT 0
+#define CLKOUT1 1
+#define CLKOUT2 2
+#define CLKOUT3 3
+#define CLKOUTMAX 4
+
+#define BRRx_MASK(x) (0x3FF & x)
+
+static struct rsnd_mod_ops adg_ops = {
+ .name = "adg",
+};
+
struct rsnd_adg {
struct clk *clk[CLKMAX];
+ struct clk *clkout[CLKOUTMAX];
+ struct clk_onecell_data onecell;
+ struct rsnd_mod mod;
- int rbga_rate_for_441khz_div_6; /* RBGA */
- int rbgb_rate_for_48khz_div_6; /* RBGB */
- u32 ckr;
+ int rbga_rate_for_441khz; /* RBGA */
+ int rbgb_rate_for_48khz; /* RBGB */
};
#define for_each_rsnd_clk(pos, adg, i) \
@@ -29,17 +43,36 @@ struct rsnd_adg {
(i < CLKMAX) && \
((pos) = adg->clk[i]); \
i++)
+#define for_each_rsnd_clkout(pos, adg, i) \
+ for (i = 0; \
+ (i < CLKOUTMAX) && \
+ ((pos) = adg->clkout[i]); \
+ i++)
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+static u32 rsnd_adg_calculate_rbgx(unsigned long div)
+{
+ int i, ratio;
+
+ if (!div)
+ return 0;
+
+ for (i = 3; i >= 0; i--) {
+ ratio = 2 << (i * 2);
+ if (0 == (div % ratio))
+ return (u32)((i << 8) | ((div / ratio) - 1));
+ }
+
+ return ~0;
+}
static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
int id = rsnd_mod_id(mod);
int ws = id;
- if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) {
+ if (rsnd_ssi_is_pin_sharing(io)) {
switch (id) {
case 1:
case 2:
@@ -60,6 +93,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int id = rsnd_mod_id(mod);
int shift = (id % 2) ? 16 : 0;
u32 mask, val;
@@ -69,21 +105,26 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
val = val << shift;
mask = 0xffff << shift;
- rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val);
+ rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
return 0;
}
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
+static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
u32 timsel)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int is_play = rsnd_io_is_play(io);
- int id = rsnd_mod_id(mod);
+ int id = rsnd_mod_id(src_mod);
int shift = (id % 2) ? 16 : 0;
u32 mask, ws;
u32 in, out;
+ rsnd_mod_confirm_src(src_mod);
+
ws = rsnd_adg_ssi_ws_timing_gen2(io);
in = (is_play) ? timsel : ws;
@@ -95,37 +136,38 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
switch (id / 2) {
case 0:
- rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out);
break;
case 1:
- rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out);
break;
case 2:
- rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out);
break;
case 3:
- rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out);
break;
case 4:
- rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out);
break;
}
return 0;
}
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
unsigned int src_rate,
unsigned int dst_rate)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, step, ret;
u32 val, en;
@@ -134,10 +176,12 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
- adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */
- adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */
+ adg->rbga_rate_for_441khz, /* 0011: RBGA */
+ adg->rbgb_rate_for_48khz, /* 0100: RBGB */
};
+ rsnd_mod_confirm_src(src_mod);
+
min = ~0;
val = 0;
en = 0;
@@ -175,25 +219,27 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
return -EIO;
}
- ret = rsnd_adg_set_src_timsel_gen2(mod, io, val);
+ ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
if (ret < 0) {
dev_err(dev, "timsel error\n");
return ret;
}
- rsnd_mod_bset(mod, DIV_EN, en, en);
+ rsnd_mod_bset(adg_mod, DIV_EN, en, en);
dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
return 0;
}
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io)
{
u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
- return rsnd_adg_set_src_timsel_gen2(mod, io, val);
+ rsnd_mod_confirm_src(src_mod);
+
+ return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
}
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
@@ -202,6 +248,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
unsigned int dst_rate)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, shift;
u32 mask, val;
@@ -211,8 +258,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */
0, /* 011: MLBCLK (not used) */
- adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */
- adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */
+ adg->rbga_rate_for_441khz, /* 100: RBGA */
+ adg->rbgb_rate_for_48khz, /* 101: RBGB */
};
/* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
@@ -238,13 +285,13 @@ find_rate:
switch (id / 4) {
case 0:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
break;
case 1:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
break;
case 2:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
break;
}
@@ -257,12 +304,17 @@ find_rate:
return 0;
}
-static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
{
- int id = rsnd_mod_id(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ int id = rsnd_mod_id(ssi_mod);
int shift = (id % 4) * 8;
u32 mask = 0xFF << shift;
+ rsnd_mod_confirm_ssi(ssi_mod);
+
val = val << shift;
/*
@@ -274,13 +326,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
switch (id / 4) {
case 0:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val);
break;
case 1:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val);
break;
case 2:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val);
break;
}
}
@@ -326,14 +378,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
}
/*
- * find 1/6 clock from BRGA/BRGB
+ * find divided clock from BRGA/BRGB
*/
- if (rate == adg->rbga_rate_for_441khz_div_6) {
+ if (rate == adg->rbga_rate_for_441khz) {
data = 0x10;
goto found_clock;
}
- if (rate == adg->rbgb_rate_for_48khz_div_6) {
+ if (rate == adg->rbgb_rate_for_48khz) {
data = 0x20;
goto found_clock;
}
@@ -342,29 +394,60 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
found_clock:
- /* see rsnd_adg_ssi_clk_init() */
- rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr);
- rsnd_mod_write(mod, BRRA, 0x00000002); /* 1/6 */
- rsnd_mod_write(mod, BRRB, 0x00000002); /* 1/6 */
-
/*
* This "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
rsnd_adg_set_ssi_clk(mod, data);
- dev_dbg(dev, "ADG: ssi%d selects clk%d = %d",
- rsnd_mod_id(mod), i, rate);
+ dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod),
+ data, rate);
return 0;
}
-static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
+static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
+ struct rsnd_adg *adg)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ static const char * const clk_name[] = {
+ [CLKA] = "clk_a",
+ [CLKB] = "clk_b",
+ [CLKC] = "clk_c",
+ [CLKI] = "clk_i",
+ };
+ int i;
+
+ for (i = 0; i < CLKMAX; i++) {
+ clk = devm_clk_get(dev, clk_name[i]);
+ adg->clk[i] = IS_ERR(clk) ? NULL : clk;
+ }
+
+ for_each_rsnd_clk(clk, adg, i)
+ dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+}
+
+static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
+ struct rsnd_adg *adg)
{
struct clk *clk;
- unsigned long rate;
- u32 ckr;
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+ u32 ckr, rbgx, rbga, rbgb;
+ u32 rate, req_rate, div;
+ uint32_t count = 0;
+ unsigned long req_48kHz_rate, req_441kHz_rate;
int i;
+ const char *parent_clk_name = NULL;
+ static const char * const clkout_name[] = {
+ [CLKOUT] = "audio_clkout",
+ [CLKOUT1] = "audio_clkout1",
+ [CLKOUT2] = "audio_clkout2",
+ [CLKOUT3] = "audio_clkout3",
+ };
int brg_table[] = {
[CLKA] = 0x0,
[CLKB] = 0x1,
@@ -372,19 +455,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
[CLKI] = 0x2,
};
+ of_property_read_u32(np, "#clock-cells", &count);
+
+ /*
+ * ADG supports BRRA/BRRB output only
+ * this means all clkout0/1/2/3 will be same rate
+ */
+ of_property_read_u32(np, "clock-frequency", &req_rate);
+ req_48kHz_rate = 0;
+ req_441kHz_rate = 0;
+ if (0 == (req_rate % 44100))
+ req_441kHz_rate = req_rate;
+ if (0 == (req_rate % 48000))
+ req_48kHz_rate = req_rate;
+
/*
* 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()
+ * rsnd_ssi_master_clk_start()
*/
ckr = 0;
- adg->rbga_rate_for_441khz_div_6 = 0;
- adg->rbgb_rate_for_48khz_div_6 = 0;
+ rbga = 2; /* default 1/6 */
+ rbgb = 2; /* default 1/6 */
+ adg->rbga_rate_for_441khz = 0;
+ adg->rbgb_rate_for_48khz = 0;
for_each_rsnd_clk(clk, adg, i) {
rate = clk_get_rate(clk);
@@ -392,19 +490,86 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
continue;
/* RBGA */
- if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) {
- adg->rbga_rate_for_441khz_div_6 = rate / 6;
- ckr |= brg_table[i] << 20;
+ if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
+ div = 6;
+ if (req_441kHz_rate)
+ div = rate / req_441kHz_rate;
+ rbgx = rsnd_adg_calculate_rbgx(div);
+ if (BRRx_MASK(rbgx) == rbgx) {
+ rbga = rbgx;
+ adg->rbga_rate_for_441khz = rate / div;
+ ckr |= brg_table[i] << 20;
+ if (req_441kHz_rate)
+ parent_clk_name = __clk_get_name(clk);
+ }
}
/* RBGB */
- if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) {
- adg->rbgb_rate_for_48khz_div_6 = rate / 6;
- ckr |= brg_table[i] << 16;
+ if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
+ div = 6;
+ if (req_48kHz_rate)
+ div = rate / req_48kHz_rate;
+ rbgx = rsnd_adg_calculate_rbgx(div);
+ if (BRRx_MASK(rbgx) == rbgx) {
+ rbgb = rbgx;
+ adg->rbgb_rate_for_48khz = rate / div;
+ ckr |= brg_table[i] << 16;
+ if (req_48kHz_rate) {
+ parent_clk_name = __clk_get_name(clk);
+ ckr |= 0x80000000;
+ }
+ }
}
}
- adg->ckr = ckr;
+ /*
+ * ADG supports BRRA/BRRB output only.
+ * this means all clkout0/1/2/3 will be * same rate
+ */
+
+ /*
+ * for clkout
+ */
+ if (!count) {
+ clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
+ parent_clk_name,
+ (parent_clk_name) ?
+ 0 : CLK_IS_ROOT, req_rate);
+ if (!IS_ERR(clk)) {
+ adg->clkout[CLKOUT] = clk;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ }
+ }
+ /*
+ * for clkout0/1/2/3
+ */
+ else {
+ for (i = 0; i < CLKOUTMAX; i++) {
+ clk = clk_register_fixed_rate(dev, clkout_name[i],
+ parent_clk_name,
+ (parent_clk_name) ?
+ 0 : CLK_IS_ROOT,
+ req_rate);
+ if (!IS_ERR(clk)) {
+ adg->onecell.clks = adg->clkout;
+ adg->onecell.clk_num = CLKOUTMAX;
+
+ adg->clkout[i] = clk;
+
+ of_clk_add_provider(np, of_clk_src_onecell_get,
+ &adg->onecell);
+ }
+ }
+ }
+
+ rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+ rsnd_mod_write(adg_mod, BRRA, rbga);
+ rsnd_mod_write(adg_mod, BRRB, rbgb);
+
+ for_each_rsnd_clkout(clk, adg, i)
+ dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+ dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+ ckr, rbga, rbgb);
}
int rsnd_adg_probe(struct platform_device *pdev,
@@ -413,8 +578,6 @@ int rsnd_adg_probe(struct platform_device *pdev,
{
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) {
@@ -422,15 +585,16 @@ int rsnd_adg_probe(struct platform_device *pdev,
return -ENOMEM;
}
- adg->clk[CLKA] = devm_clk_get(dev, "clk_a");
- adg->clk[CLKB] = devm_clk_get(dev, "clk_b");
- adg->clk[CLKC] = devm_clk_get(dev, "clk_c");
- adg->clk[CLKI] = devm_clk_get(dev, "clk_i");
-
- for_each_rsnd_clk(clk, adg, i)
- dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+ /*
+ * ADG is special module.
+ * Use ADG mod without rsnd_mod_init() to make debug easy
+ * for rsnd_write/rsnd_read
+ */
+ adg->mod.ops = &adg_ops;
+ adg->mod.priv = priv;
- rsnd_adg_ssi_clk_init(priv, adg);
+ rsnd_adg_get_clkin(priv, adg);
+ rsnd_adg_get_clkout(priv, adg);
priv->adg = adg;
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index f3feed5ce9b6..deed48ef28b8 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = {
static const struct of_device_id rsnd_of_match[] = {
{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
+ { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
{},
};
MODULE_DEVICE_TABLE(of, rsnd_of_match);
@@ -126,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match);
#define rsnd_info_id(priv, io, name) \
((io)->info->name - priv->info->name##_info)
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
+{
+ if (mod->type != type) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_warn(dev, "%s[%d] is not your expected module\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+ }
+}
+
/*
* rsnd_mod functions
*/
@@ -288,7 +300,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
/*
* rsnd_dai functions
*/
-#define __rsnd_mod_call(mod, io, func, param...) \
+#define rsnd_mod_call(mod, io, func, param...) \
({ \
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct device *dev = rsnd_priv_to_dev(priv); \
@@ -296,24 +308,17 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \
- int called = 0; \
- if (val == __rsnd_mod_call_##func) { \
- called = 1; \
- ret = (mod)->ops->func(mod, io, param); \
- } \
+ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
mod->status = (mod->status & ~mask) + \
(add << __rsnd_mod_shift_##func); \
- dev_dbg(dev, "%s[%d] 0x%08x %s\n", \
- rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \
- called ? #func : ""); \
+ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), \
+ mod->status, call ? #func : ""); \
+ if (call) \
+ ret = (mod)->ops->func(mod, io, param); \
ret; \
})
-#define rsnd_mod_call(mod, io, func, param...) \
- (!(mod) ? -ENODEV : \
- !((mod)->ops->func) ? 0 : \
- __rsnd_mod_call(mod, io, func, param))
-
#define rsnd_dai_call(fn, io, param...) \
({ \
struct rsnd_mod *mod; \
@@ -322,9 +327,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
mod = (io)->mod[i]; \
if (!mod) \
continue; \
- ret = rsnd_mod_call(mod, io, fn, param); \
- if (ret < 0) \
- break; \
+ ret |= rsnd_mod_call(mod, io, fn, param); \
} \
ret; \
})
@@ -490,16 +493,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
break;
case SNDRV_PCM_TRIGGER_STOP:
ret = rsnd_dai_call(stop, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
- ret = rsnd_dai_call(quit, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
+ ret |= rsnd_dai_call(quit, io, priv);
- ret = rsnd_platform_call(priv, dai, stop, ssi_id);
- if (ret < 0)
- goto dai_trigger_end;
+ ret |= rsnd_platform_call(priv, dai, stop, ssi_id);
rsnd_dai_stream_quit(io);
break;
@@ -1224,20 +1221,11 @@ static int rsnd_probe(struct platform_device *pdev)
};
int ret, i;
- info = NULL;
- of_data = NULL;
- if (of_id) {
- info = devm_kzalloc(&pdev->dev,
- sizeof(struct rcar_snd_info), GFP_KERNEL);
- of_data = of_id->data;
- } else {
- info = pdev->dev.platform_data;
- }
-
- if (!info) {
- dev_err(dev, "driver needs R-Car sound information\n");
- return -ENODEV;
- }
+ info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ of_data = of_id->data;
/*
* init priv data
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c
index 05498bba5874..3cb214ab848b 100644
--- a/sound/soc/sh/rcar/ctu.c
+++ b/sound/soc/sh/rcar/ctu.c
@@ -35,7 +35,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_ctu_initialize_lock(mod);
@@ -50,7 +50,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
return 0;
}
@@ -66,7 +66,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
id = 0;
- return &((struct rsnd_ctu *)(priv->ctu) + id)->mod;
+ return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id);
}
static void rsnd_of_parse_ctu(struct platform_device *pdev,
@@ -118,10 +118,8 @@ int rsnd_ctu_probe(struct platform_device *pdev,
int i, nr, ret;
/* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv)) {
- dev_warn(dev, "CTU is not supported on Gen1\n");
- return -EINVAL;
- }
+ if (rsnd_is_gen1(priv))
+ return 0;
rsnd_of_parse_ctu(pdev, of_data, priv);
@@ -150,7 +148,7 @@ int rsnd_ctu_probe(struct platform_device *pdev,
ctu->info = &info->ctu_info[i];
- ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops,
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
clk, RSND_MOD_CTU, i);
if (ret)
return ret;
@@ -166,6 +164,6 @@ void rsnd_ctu_remove(struct platform_device *pdev,
int i;
for_each_rsnd_ctu(ctu, priv, i) {
- rsnd_mod_quit(&ctu->mod);
+ rsnd_mod_quit(rsnd_mod_get(ctu));
}
}
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index bfbb8a5e93bd..5d084d040961 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -470,7 +470,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
dev_err(dev, "DVC is selected without SRC\n");
/* use SSIU or SSI ? */
- if (is_ssi && rsnd_ssi_use_busif(io, mod))
+ if (is_ssi && rsnd_ssi_use_busif(io))
is_ssi++;
return (is_from) ?
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 57796387d482..58f690900e6d 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -153,7 +153,7 @@ static int rsnd_dvc_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_dvc_soft_reset(mod);
@@ -175,7 +175,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
return 0;
}
@@ -282,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
id = 0;
- return &((struct rsnd_dvc *)(priv->dvc) + id)->mod;
+ return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id);
}
static void rsnd_of_parse_dvc(struct platform_device *pdev,
@@ -333,10 +333,8 @@ int rsnd_dvc_probe(struct platform_device *pdev,
int i, nr, ret;
/* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv)) {
- dev_warn(dev, "CMD is not supported on Gen1\n");
- return -EINVAL;
- }
+ if (rsnd_is_gen1(priv))
+ return 0;
rsnd_of_parse_dvc(pdev, of_data, priv);
@@ -361,7 +359,7 @@ int rsnd_dvc_probe(struct platform_device *pdev,
dvc->info = &info->dvc_info[i];
- ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops,
+ ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
if (ret)
return ret;
@@ -377,6 +375,6 @@ void rsnd_dvc_remove(struct platform_device *pdev,
int i;
for_each_rsnd_dvc(dvc, priv, i) {
- rsnd_mod_quit(&dvc->mod);
+ rsnd_mod_quit(rsnd_mod_get(dvc));
}
}
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index f04d17bc6e3d..76da7620904c 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -22,13 +22,15 @@
#include "rsnd.h"
struct rsnd_gen {
- void __iomem *base[RSND_BASE_MAX];
-
struct rsnd_gen_ops *ops;
+ /* RSND_BASE_MAX base */
+ void __iomem *base[RSND_BASE_MAX];
+ phys_addr_t res[RSND_BASE_MAX];
struct regmap *regmap[RSND_BASE_MAX];
+
+ /* RSND_REG_MAX base */
struct regmap_field *regs[RSND_REG_MAX];
- phys_addr_t res[RSND_REG_MAX];
};
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
@@ -79,11 +81,11 @@ u32 rsnd_read(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return 0;
+ regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+
dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
- regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
-
return val;
}
@@ -182,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
+ /* RSND_BASE_MAX base */
gen->base[reg_id] = base;
gen->regmap[reg_id] = regmap;
gen->res[reg_id] = res->start;
@@ -198,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
if (IS_ERR(regs))
return PTR_ERR(regs);
+ /* RSND_REG_MAX base */
gen->regs[conf[i].idx] = regs;
}
diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c
index 0d5c102db6f5..953dd0be9b60 100644
--- a/sound/soc/sh/rcar/mix.c
+++ b/sound/soc/sh/rcar/mix.c
@@ -58,7 +58,7 @@ static int rsnd_mix_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_mix_soft_reset(mod);
@@ -83,7 +83,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
return 0;
}
@@ -99,7 +99,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
id = 0;
- return &((struct rsnd_mix *)(priv->mix) + id)->mod;
+ return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
}
static void rsnd_of_parse_mix(struct platform_device *pdev,
@@ -151,10 +151,8 @@ int rsnd_mix_probe(struct platform_device *pdev,
int i, nr, ret;
/* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv)) {
- dev_warn(dev, "MIX is not supported on Gen1\n");
- return -EINVAL;
- }
+ if (rsnd_is_gen1(priv))
+ return 0;
rsnd_of_parse_mix(pdev, of_data, priv);
@@ -179,7 +177,7 @@ int rsnd_mix_probe(struct platform_device *pdev,
mix->info = &info->mix_info[i];
- ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops,
+ ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
clk, RSND_MOD_MIX, i);
if (ret)
return ret;
@@ -195,6 +193,6 @@ void rsnd_mix_remove(struct platform_device *pdev,
int i;
for_each_rsnd_mix(mix, priv, i) {
- rsnd_mod_quit(&mix->mod);
+ rsnd_mod_quit(rsnd_mod_get(mix));
}
}
diff --git a/sound/soc/sh/rcar/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h
new file mode 100644
index 000000000000..d8e33d38da43
--- /dev/null
+++ b/sound/soc/sh/rcar/rcar_snd.h
@@ -0,0 +1,117 @@
+/*
+ * Renesas R-Car SRU/SCU/SSIU/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.
+ */
+
+#ifndef RCAR_SND_H
+#define RCAR_SND_H
+
+
+#define RSND_GEN1_SRU 0
+#define RSND_GEN1_ADG 1
+#define RSND_GEN1_SSI 2
+
+#define RSND_GEN2_SCU 0
+#define RSND_GEN2_ADG 1
+#define RSND_GEN2_SSIU 2
+#define RSND_GEN2_SSI 3
+
+#define RSND_BASE_MAX 4
+
+/*
+ * flags
+ *
+ * 0xAB000000
+ *
+ * A : clock sharing settings
+ * B : SSI direction
+ */
+#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
+#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
+
+#define RSND_SSI(_dma_id, _irq, _flags) \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
+#define RSND_SSI_UNUSED \
+{ .dma_id = -1, .irq = -1, .flags = 0 }
+
+struct rsnd_ssi_platform_info {
+ int dma_id;
+ int irq;
+ u32 flags;
+};
+
+#define RSND_SRC(rate, _dma_id) \
+{ .convert_rate = rate, .dma_id = _dma_id, }
+#define RSND_SRC_UNUSED \
+{ .convert_rate = 0, .dma_id = -1, }
+
+struct rsnd_src_platform_info {
+ u32 convert_rate; /* sampling rate convert */
+ int dma_id; /* for Gen2 SCU */
+ int irq;
+};
+
+/*
+ * flags
+ */
+struct rsnd_ctu_platform_info {
+ u32 flags;
+};
+
+struct rsnd_mix_platform_info {
+ u32 flags;
+};
+
+struct rsnd_dvc_platform_info {
+ u32 flags;
+};
+
+struct rsnd_dai_path_info {
+ struct rsnd_ssi_platform_info *ssi;
+ struct rsnd_src_platform_info *src;
+ struct rsnd_ctu_platform_info *ctu;
+ struct rsnd_mix_platform_info *mix;
+ struct rsnd_dvc_platform_info *dvc;
+};
+
+struct rsnd_dai_platform_info {
+ struct rsnd_dai_path_info playback;
+ struct rsnd_dai_path_info capture;
+};
+
+/*
+ * flags
+ *
+ * 0x0000000A
+ *
+ * A : generation
+ */
+#define RSND_GEN_MASK (0xF << 0)
+#define RSND_GEN1 (1 << 0) /* fixme */
+#define RSND_GEN2 (2 << 0) /* fixme */
+
+struct rcar_snd_info {
+ u32 flags;
+ struct rsnd_ssi_platform_info *ssi_info;
+ int ssi_info_nr;
+ struct rsnd_src_platform_info *src_info;
+ int src_info_nr;
+ struct rsnd_ctu_platform_info *ctu_info;
+ int ctu_info_nr;
+ struct rsnd_mix_platform_info *mix_info;
+ int mix_info_nr;
+ struct rsnd_dvc_platform_info *dvc_info;
+ int dvc_info_nr;
+ struct rsnd_dai_platform_info *dai_info;
+ int dai_info_nr;
+ int (*start)(int id);
+ int (*stop)(int id);
+};
+
+#endif
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 7a0e52b4640a..085329878525 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -21,10 +21,11 @@
#include <linux/of_irq.h>
#include <linux/sh_dma.h>
#include <linux/workqueue.h>
-#include <sound/rcar_snd.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
+#include "rcar_snd.h"
+
/*
* pseudo register
*
@@ -214,6 +215,7 @@ struct rsnd_dma {
};
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
@@ -225,8 +227,6 @@ int rsnd_dma_probe(struct platform_device *pdev,
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name);
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
-
/*
* R-Car sound mod
*/
@@ -330,8 +330,9 @@ struct rsnd_mod {
#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
-#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
-#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk)
+#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
+#define rsnd_mod_power_off(mod) clk_disable((mod)->clk)
+#define rsnd_mod_get(ip) (&(ip)->mod)
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
@@ -571,9 +572,12 @@ int rsnd_ssi_probe(struct platform_device *pdev,
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);
-int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
+
+#define rsnd_ssi_is_pin_sharing(io) \
+ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
/*
* R-Car SRC
@@ -627,4 +631,15 @@ void rsnd_dvc_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
+#ifdef DEBUG
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
+#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
+#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
+#else
+#define rsnd_mod_confirm_ssi(mssi)
+#define rsnd_mod_confirm_src(msrc)
+#define rsnd_mod_confirm_dvc(mdvc)
+#endif
+
#endif
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 89a18e102feb..261b50217c48 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -159,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
/*
* SSI_MODE1
*/
- if (rsnd_ssi_is_pin_sharing(ssi_mod)) {
+ if (rsnd_ssi_is_pin_sharing(io)) {
int shift = -1;
switch (ssi_id) {
case 1:
@@ -352,7 +352,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_src_soft_reset(mod);
@@ -373,7 +373,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv);
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
if (src->err)
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
@@ -918,11 +918,10 @@ static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, SRC_IFSVR, fsrate);
}
-static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -932,12 +931,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
*/
/*
- * Gen1 is not supported
- */
- if (rsnd_is_gen1(priv))
- return 0;
-
- /*
* SRC sync convert needs clock master
*/
if (!rsnd_rdai_is_clk_master(rdai))
@@ -975,7 +968,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = {
.start = rsnd_src_start_gen2,
.stop = rsnd_src_stop_gen2,
.hw_params = rsnd_src_hw_params,
- .pcm_new = rsnd_src_pcm_new,
+ .pcm_new = rsnd_src_pcm_new_gen2,
};
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -983,7 +976,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
id = 0;
- return &((struct rsnd_src *)(priv->src) + id)->mod;
+ return rsnd_mod_get((struct rsnd_src *)(priv->src) + id);
}
static void rsnd_of_parse_src(struct platform_device *pdev,
@@ -1043,8 +1036,10 @@ int rsnd_src_probe(struct platform_device *pdev,
int i, nr, ret;
ops = NULL;
- if (rsnd_is_gen1(priv))
+ if (rsnd_is_gen1(priv)) {
ops = &rsnd_src_gen1_ops;
+ dev_warn(dev, "Gen1 support will be removed soon\n");
+ }
if (rsnd_is_gen2(priv))
ops = &rsnd_src_gen2_ops;
if (!ops) {
@@ -1078,7 +1073,7 @@ int rsnd_src_probe(struct platform_device *pdev,
src->info = &info->src_info[i];
- ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i);
+ ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i);
if (ret)
return ret;
}
@@ -1093,6 +1088,6 @@ void rsnd_src_remove(struct platform_device *pdev,
int i;
for_each_rsnd_src(src, priv, i) {
- rsnd_mod_quit(&src->mod);
+ rsnd_mod_quit(rsnd_mod_get(src));
}
}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index d45b9a7e324e..1427ec21bd7e 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -79,7 +79,6 @@ struct rsnd_ssi {
#define rsnd_ssi_nr(priv) ((priv)->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->irq > 0)
#define rsnd_ssi_parent(ssi) ((ssi)->parent)
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
@@ -87,8 +86,9 @@ struct rsnd_ssi {
#define rsnd_ssi_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
+ struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int use_busif = 0;
@@ -128,10 +128,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
- int i, j, ret;
- int adg_clk_div_table[] = {
- 1, 6, /* see adg.c */
- };
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ int j, ret;
int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12,
};
@@ -141,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/*
* 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_init()
- */
- 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->cr_clk = FORCE | SWL_32 |
- SCKD | SWSD | CKDV(j);
-
- dev_dbg(dev, "%s[%d] outputs %u Hz\n",
- rsnd_mod_name(&ssi->mod),
- rsnd_mod_id(&ssi->mod), rate);
-
- return 0;
- }
+ 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_init()
+ */
+ main_rate = rate * 32 * 2 * ssi_clk_mul_table[j];
+
+ ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+ if (0 == ret) {
+ ssi->cr_clk = FORCE | SWL_32 |
+ SCKD | SWSD | CKDV(j);
+
+ dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+ rsnd_mod_name(mod),
+ rsnd_mod_id(mod), rate);
+
+ return 0;
}
}
@@ -172,8 +167,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
{
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
ssi->cr_clk = 0;
- rsnd_adg_ssi_clk_stop(&ssi->mod);
+ rsnd_adg_ssi_clk_stop(mod);
}
static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
@@ -182,11 +179,12 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
u32 cr_mode;
u32 cr;
if (0 == ssi->usrcnt) {
- rsnd_mod_hw_start(&ssi->mod);
+ rsnd_mod_power_on(mod);
if (rsnd_rdai_is_clk_master(rdai)) {
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -198,7 +196,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
}
}
- if (rsnd_ssi_is_dma_mode(&ssi->mod)) {
+ if (rsnd_ssi_is_dma_mode(mod)) {
cr_mode = UIEN | OIEN | /* over/under run */
DMEN; /* DMA : enable DMA */
} else {
@@ -210,24 +208,25 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
cr_mode |
EN;
- rsnd_mod_write(&ssi->mod, SSICR, cr);
+ rsnd_mod_write(mod, SSICR, cr);
/* enable WS continue */
if (rsnd_rdai_is_clk_master(rdai))
- rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+ rsnd_mod_write(mod, SSIWSR, CONT);
/* clear error status */
- rsnd_mod_write(&ssi->mod, SSISR, 0);
+ rsnd_mod_write(mod, SSISR, 0);
ssi->usrcnt++;
dev_dbg(dev, "%s[%d] hw started\n",
- rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 cr;
@@ -247,15 +246,15 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
cr = ssi->cr_own |
ssi->cr_clk;
- rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
- rsnd_ssi_status_check(&ssi->mod, DIRQ);
+ rsnd_mod_write(mod, SSICR, cr | EN);
+ rsnd_ssi_status_check(mod, DIRQ);
/*
* disable SSI,
* and, wait idle state
*/
- rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */
- rsnd_ssi_status_check(&ssi->mod, IIRQ);
+ rsnd_mod_write(mod, SSICR, cr); /* disabled all */
+ rsnd_ssi_status_check(mod, IIRQ);
if (rsnd_rdai_is_clk_master(rdai)) {
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -266,13 +265,13 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
rsnd_ssi_master_clk_stop(ssi);
}
- rsnd_mod_hw_stop(&ssi->mod);
+ rsnd_mod_power_off(mod);
ssi->chan = 0;
}
dev_dbg(dev, "%s[%d] hw stopped\n",
- rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
/*
@@ -371,7 +370,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
/* It will be removed on rsnd_ssi_hw_stop */
ssi->chan = chan;
if (ssi_parent)
- return rsnd_ssi_hw_params(&ssi_parent->mod, io,
+ return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
substream, params);
return 0;
@@ -379,12 +378,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
{
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
/* under/over flow error */
if (status & (UIRQ | OIRQ)) {
ssi->err++;
/* clear error status */
- rsnd_mod_write(&ssi->mod, SSISR, 0);
+ rsnd_mod_write(mod, SSISR, 0);
}
}
@@ -394,7 +395,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod,
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod));
+ rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io));
rsnd_ssi_hw_start(ssi, io);
@@ -554,7 +555,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
/* PIO will request IRQ again */
- devm_free_irq(dev, irq, ssi);
+ devm_free_irq(dev, irq, mod);
return 0;
}
@@ -613,7 +614,7 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
int is_play = rsnd_io_is_play(io);
char *name;
- if (rsnd_ssi_use_busif(io, mod))
+ if (rsnd_ssi_use_busif(io))
name = is_play ? "rxu" : "txu";
else
name = is_play ? "rx" : "tx";
@@ -656,10 +657,10 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
id = 0;
- return &((struct rsnd_ssi *)(priv->ssi) + id)->mod;
+ return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id);
}
-int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@@ -668,10 +669,12 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
{
- if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
+ if (!__rsnd_ssi_is_pin_sharing(mod))
return;
- switch (rsnd_mod_id(&ssi->mod)) {
+ switch (rsnd_mod_id(mod)) {
case 1:
case 2:
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
@@ -697,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
struct device *dev = &pdev->dev;
int nr, i;
- if (!of_data)
- return;
-
node = rsnd_ssi_of_node(priv);
if (!node)
return;
@@ -794,7 +794,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
else if (rsnd_ssi_pio_available(ssi))
ops = &rsnd_ssi_pio_ops;
- ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i);
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
+ RSND_MOD_SSI, i);
if (ret)
return ret;
@@ -811,6 +812,6 @@ void rsnd_ssi_remove(struct platform_device *pdev,
int i;
for_each_rsnd_ssi(ssi, priv, i) {
- rsnd_mod_quit(&ssi->mod);
+ rsnd_mod_quit(rsnd_mod_get(ssi));
}
}
diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c
index abb0d956231c..76b2ab8c2b4a 100644
--- a/sound/soc/sh/siu_dai.c
+++ b/sound/soc/sh/siu_dai.c
@@ -738,7 +738,7 @@ static int siu_probe(struct platform_device *pdev)
struct siu_info *info;
int ret;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
siu_i2s_data = info;
@@ -746,7 +746,7 @@ static int siu_probe(struct platform_device *pdev)
ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
if (ret)
- goto ereqfw;
+ return ret;
/*
* Loaded firmware is "const" - read only, but we have to modify it in
@@ -757,89 +757,52 @@ static int siu_probe(struct platform_device *pdev)
release_firmware(fw_entry);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto egetres;
- }
+ if (!res)
+ return -ENODEV;
- region = request_mem_region(res->start, resource_size(res),
- pdev->name);
+ region = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name);
if (!region) {
dev_err(&pdev->dev, "SIU region already claimed\n");
- ret = -EBUSY;
- goto ereqmemreg;
+ return -EBUSY;
}
- ret = -ENOMEM;
- info->pram = ioremap(res->start, PRAM_SIZE);
+ info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE);
if (!info->pram)
- goto emappram;
- info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
+ return -ENOMEM;
+ info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET,
+ XRAM_SIZE);
if (!info->xram)
- goto emapxram;
- info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
+ return -ENOMEM;
+ info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET,
+ YRAM_SIZE);
if (!info->yram)
- goto emapyram;
- info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
- REG_OFFSET);
+ return -ENOMEM;
+ info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET,
+ resource_size(res) - REG_OFFSET);
if (!info->reg)
- goto emapreg;
+ return -ENOMEM;
dev_set_drvdata(&pdev->dev, info);
/* register using ARRAY version so we can keep dai name */
- ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component,
- &siu_i2s_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component,
+ &siu_i2s_dai, 1);
if (ret < 0)
- goto edaiinit;
+ return ret;
- ret = snd_soc_register_platform(&pdev->dev, &siu_platform);
+ ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform);
if (ret < 0)
- goto esocregp;
+ return ret;
pm_runtime_enable(&pdev->dev);
- return ret;
-
-esocregp:
- snd_soc_unregister_component(&pdev->dev);
-edaiinit:
- iounmap(info->reg);
-emapreg:
- iounmap(info->yram);
-emapyram:
- iounmap(info->xram);
-emapxram:
- iounmap(info->pram);
-emappram:
- release_mem_region(res->start, resource_size(res));
-ereqmemreg:
-egetres:
-ereqfw:
- kfree(info);
-
- return ret;
+ return 0;
}
static int siu_remove(struct platform_device *pdev)
{
- struct siu_info *info = dev_get_drvdata(&pdev->dev);
- struct resource *res;
-
pm_runtime_disable(&pdev->dev);
-
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
-
- iounmap(info->reg);
- iounmap(info->yram);
- iounmap(info->xram);
- iounmap(info->pram);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res)
- release_mem_region(res->start, resource_size(res));
- kfree(info);
-
return 0;
}
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 025c38fbe3c0..12a9820feac1 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -612,8 +612,15 @@ static struct snd_compr_ops soc_compr_dyn_ops = {
.get_codec_caps = soc_compr_get_codec_caps
};
-/* create a new compress */
-int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
+/**
+ * snd_soc_new_compress - create a new compress.
+ *
+ * @rtd: The runtime for which we will create compress
+ * @num: the device index number (zero based - shared with normal PCMs)
+ *
+ * Return: 0 for success, else error.
+ */
+int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
@@ -703,3 +710,4 @@ compr_err:
kfree(compr);
return ret;
}
+EXPORT_SYMBOL_GPL(snd_soc_new_compress);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 6173d15236c3..24b096066a07 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1370,9 +1370,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
soc_dpcm_debugfs_add(rtd);
#endif
- if (cpu_dai->driver->compress_dai) {
+ if (cpu_dai->driver->compress_new) {
/*create compress_device"*/
- ret = soc_new_compress(rtd, num);
+ ret = cpu_dai->driver->compress_new(rtd, num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create compress %s\n",
dai_link->stream_name);
@@ -3291,13 +3291,38 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
+static int snd_soc_of_get_slot_mask(struct device_node *np,
+ const char *prop_name,
+ unsigned int *mask)
+{
+ u32 val;
+ const __be32 *of_slot_mask = of_get_property(np, prop_name, &val);
+ int i;
+
+ if (!of_slot_mask)
+ return 0;
+ val /= sizeof(u32);
+ for (i = 0; i < val; i++)
+ if (be32_to_cpup(&of_slot_mask[i]))
+ *mask |= (1 << i);
+
+ return val;
+}
+
int snd_soc_of_parse_tdm_slot(struct device_node *np,
+ unsigned int *tx_mask,
+ unsigned int *rx_mask,
unsigned int *slots,
unsigned int *slot_width)
{
u32 val;
int ret;
+ if (tx_mask)
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask);
+ if (rx_mask)
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
+
if (of_property_read_bool(np, "dai-tdm-slot-num")) {
ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
if (ret)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index ff8bda471b25..016eba10b1ec 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -509,6 +509,18 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
}
/**
+ * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a
+ * kcontrol
+ * @kcontrol: The kcontrol
+ */
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
+ struct snd_kcontrol *kcontrol)
+{
+ return dapm_kcontrol_get_wlist(kcontrol)->widgets[0];
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget);
+
+/**
* snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
* kcontrol
* @kcontrol: The kcontrol
@@ -779,7 +791,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
* 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,
+static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
int kci)
{
struct snd_soc_dapm_context *dapm = w->dapm;
@@ -810,6 +822,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
switch (w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_pga:
wname_in_long_name = true;
kcname_in_long_name = true;
break;
@@ -899,7 +912,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
continue;
if (!w->kcontrols[i]) {
- ret = dapm_create_or_share_mixmux_kcontrol(w, i);
+ ret = dapm_create_or_share_kcontrol(w, i);
if (ret < 0)
return ret;
}
@@ -952,7 +965,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
return -EINVAL;
}
- ret = dapm_create_or_share_mixmux_kcontrol(w, 0);
+ ret = dapm_create_or_share_kcontrol(w, 0);
if (ret < 0)
return ret;
@@ -967,9 +980,13 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
/* create new dapm volume control */
static int dapm_new_pga(struct snd_soc_dapm_widget *w)
{
- if (w->num_kcontrols)
- dev_err(w->dapm->dev,
- "ASoC: PGA controls not supported: '%s'\n", w->name);
+ int i, ret;
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ ret = dapm_create_or_share_kcontrol(w, i);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
@@ -3473,11 +3490,29 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ if (source->driver->ops && source->driver->ops->startup) {
+ ret = source->driver->ops->startup(&substream, source);
+ if (ret < 0) {
+ dev_err(source->dev,
+ "ASoC: startup() failed: %d\n", ret);
+ goto out;
+ }
+ source->active++;
+ }
ret = soc_dai_hw_params(&substream, params, source);
if (ret < 0)
goto out;
substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ if (sink->driver->ops && sink->driver->ops->startup) {
+ ret = sink->driver->ops->startup(&substream, sink);
+ if (ret < 0) {
+ dev_err(sink->dev,
+ "ASoC: startup() failed: %d\n", ret);
+ goto out;
+ }
+ sink->active++;
+ }
ret = soc_dai_hw_params(&substream, params, sink);
if (ret < 0)
goto out;
@@ -3497,6 +3532,18 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret);
ret = 0;
+
+ source->active--;
+ if (source->driver->ops && source->driver->ops->shutdown) {
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ source->driver->ops->shutdown(&substream, source);
+ }
+
+ sink->active--;
+ if (sink->driver->ops && sink->driver->ops->shutdown) {
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ sink->driver->ops->shutdown(&substream, sink);
+ }
break;
default:
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 05977ae1ff2a..ecd38e52285a 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -588,16 +588,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
/**
* snd_soc_limit_volume - Set new limit to an existing volume control.
*
- * @codec: where to look for the control
+ * @card: where to look for the control
* @name: Name of the control
* @max: new maximum limit
*
* Return 0 for success, else error.
*/
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
+int snd_soc_limit_volume(struct snd_soc_card *card,
const char *name, int max)
{
- struct snd_card *card = codec->component.card->snd_card;
+ struct snd_card *snd_card = card->snd_card;
struct snd_kcontrol *kctl;
struct soc_mixer_control *mc;
int found = 0;
@@ -607,7 +607,7 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec,
if (unlikely(!name || max <= 0))
return -EINVAL;
- list_for_each_entry(kctl, &card->controls, list) {
+ list_for_each_entry(kctl, &snd_card->controls, list) {
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
found = 1;
break;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 70e4b9d8bdcd..c86dc96e8986 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -34,6 +34,24 @@
#define DPCM_MAX_BE_USERS 8
+/*
+ * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
+ *
+ * Returns true if the DAI supports the indicated stream type.
+ */
+static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
+{
+ struct snd_soc_pcm_stream *codec_stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_stream = &dai->driver->playback;
+ else
+ codec_stream = &dai->driver->capture;
+
+ /* If the codec specifies any rate at all, it supports the stream. */
+ return codec_stream->rates;
+}
+
/**
* snd_soc_runtime_activate() - Increment active count for PCM runtime components
* @rtd: ASoC PCM runtime that is activated
@@ -182,9 +200,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
soc_dai->rate);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- soc_dai->rate, soc_dai->rate);
+ soc_dai->rate);
if (ret < 0) {
dev_err(soc_dai->dev,
"ASoC: Unable to apply rate constraint: %d\n",
@@ -198,9 +216,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
soc_dai->channels);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- soc_dai->channels,
soc_dai->channels);
if (ret < 0) {
dev_err(soc_dai->dev,
@@ -215,9 +232,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
soc_dai->sample_bits);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- soc_dai->sample_bits,
soc_dai->sample_bits);
if (ret < 0) {
dev_err(soc_dai->dev,
@@ -371,6 +387,20 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
/* first calculate min/max only for CODECs in the DAI link */
for (i = 0; i < rtd->num_codecs; i++) {
+
+ /*
+ * Skip CODECs which don't support the current stream type.
+ * Otherwise, since the rate, channel, and format values will
+ * zero in that case, we would have no usable settings left,
+ * causing the resulting setup to fail.
+ * At least one CODEC should match, otherwise we should have
+ * bailed out on a higher level, since there would be no
+ * CODEC to support the transfer direction in that case.
+ */
+ if (!snd_soc_dai_stream_valid(rtd->codec_dais[i],
+ substream->stream))
+ continue;
+
codec_dai_drv = rtd->codec_dais[i]->driver;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
codec_stream = &codec_dai_drv->playback;
@@ -827,6 +857,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
struct snd_pcm_hw_params codec_params;
+ /*
+ * Skip CODECs which don't support the current stream type,
+ * the idea being that if a CODEC is not used for the currently
+ * set up transfer direction, it should not need to be
+ * configured, especially since the configuration used might
+ * not even be supported by that CODEC. There may be cases
+ * however where a CODEC needs to be set up although it is
+ * actually not being used for the transfer, e.g. if a
+ * capture-only CODEC is acting as an LRCLK and/or BCLK master
+ * for the DAI link including a playback-only CODEC.
+ * If this becomes necessary, we will have to augment the
+ * machine driver setup with information on how to act, so
+ * we can do the right thing here.
+ */
+ if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
+ continue;
+
/* copy params for each codec */
codec_params = *params;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 69d01cd925ce..8d7ec80af51b 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1558,7 +1558,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos;
if (soc_tplg_check_elem_count(tplg,
- sizeof(struct snd_soc_tplg_pcm_dai), count,
+ sizeof(struct snd_soc_tplg_pcm), count,
hdr->payload_size, "PCM DAI")) {
dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n",
count);
@@ -1566,7 +1566,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
}
dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
- tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count;
+ tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count;
dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL);
if (dobj == NULL)
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
new file mode 100644
index 000000000000..84c72ec6ad73
--- /dev/null
+++ b/sound/soc/sunxi/Kconfig
@@ -0,0 +1,11 @@
+menu "Allwinner SoC Audio support"
+
+config SND_SUN4I_CODEC
+ tristate "Allwinner A10 Codec Support"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Select Y or M to add support for the Codec embedded in the Allwinner
+ A10 and affiliated SoCs.
+
+endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
new file mode 100644
index 000000000000..ea8a08c881d6
--- /dev/null
+++ b/sound/soc/sunxi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
new file mode 100644
index 000000000000..bcbf4da168b6
--- /dev/null
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright 2014 Emilio López <emilio@elopez.com.ar>
+ * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
+ * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Codec DAC register offsets and bit fields */
+#define SUN4I_CODEC_DAC_DPC (0x00)
+#define SUN4I_CODEC_DAC_DPC_EN_DA (31)
+#define SUN4I_CODEC_DAC_DPC_DVOL (12)
+#define SUN4I_CODEC_DAC_FIFOC (0x04)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29)
+#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28)
+#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26)
+#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24)
+#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21)
+#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8)
+#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6)
+#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4)
+#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
+#define SUN4I_CODEC_DAC_FIFOS (0x08)
+#define SUN4I_CODEC_DAC_TXDATA (0x0c)
+#define SUN4I_CODEC_DAC_ACTL (0x10)
+#define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
+#define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
+#define SUN4I_CODEC_DAC_ACTL_MIXEN (29)
+#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15)
+#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14)
+#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13)
+#define SUN4I_CODEC_DAC_ACTL_DACPAS (8)
+#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7)
+#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6)
+#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0)
+#define SUN4I_CODEC_DAC_TUNE (0x14)
+#define SUN4I_CODEC_DAC_DEBUG (0x18)
+
+/* Codec ADC register offsets and bit fields */
+#define SUN4I_CODEC_ADC_FIFOC (0x1c)
+#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
+#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
+#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
+#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7)
+#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6)
+#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4)
+#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
+#define SUN4I_CODEC_ADC_FIFOS (0x20)
+#define SUN4I_CODEC_ADC_RXDATA (0x24)
+#define SUN4I_CODEC_ADC_ACTL (0x28)
+#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
+#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
+#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29)
+#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28)
+#define SUN4I_CODEC_ADC_ACTL_VMICEN (27)
+#define SUN4I_CODEC_ADC_ACTL_VADCG (20)
+#define SUN4I_CODEC_ADC_ACTL_ADCIS (17)
+#define SUN4I_CODEC_ADC_ACTL_PA_EN (4)
+#define SUN4I_CODEC_ADC_ACTL_DDE (3)
+#define SUN4I_CODEC_ADC_DEBUG (0x2c)
+
+/* Other various ADC registers */
+#define SUN4I_CODEC_DAC_TXCNT (0x30)
+#define SUN4I_CODEC_ADC_RXCNT (0x34)
+#define SUN4I_CODEC_AC_SYS_VERI (0x38)
+#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c)
+
+struct sun4i_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *clk_apb;
+ struct clk *clk_module;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+};
+
+static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
+{
+ /*
+ * FIXME: according to the BSP, we might need to drive a PA
+ * GPIO high here on some boards
+ */
+
+ /* Flush TX FIFO */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+ /* Enable DAC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
+}
+
+static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
+{
+ /*
+ * FIXME: according to the BSP, we might need to drive a PA
+ * GPIO low here on some boards
+ */
+
+ /* Disable DAC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+ 0);
+}
+
+static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ sun4i_codec_start_playback(scodec);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sun4i_codec_stop_playback(scodec);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+ u32 val;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ /* Flush the TX FIFO */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+ /* Set TX FIFO Empty Trigger Level */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
+ 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
+
+ if (substream->runtime->rate > 32000)
+ /* Use 64 bits FIR filter */
+ val = 0;
+ else
+ /* Use 32 bits FIR filter */
+ val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
+
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
+ val);
+
+ /* Send zeros when we have an underrun */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
+ 0);
+
+ return 0;
+}
+
+static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
+{
+ unsigned int rate = params_rate(params);
+
+ switch (rate) {
+ case 176400:
+ case 88200:
+ case 44100:
+ case 33075:
+ case 22050:
+ case 14700:
+ case 11025:
+ case 7350:
+ return 22579200;
+
+ case 192000:
+ case 96000:
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ case 12000:
+ case 8000:
+ return 24576000;
+
+ default:
+ return 0;
+ }
+}
+
+static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
+{
+ unsigned int rate = params_rate(params);
+
+ switch (rate) {
+ case 192000:
+ case 176400:
+ return 6;
+
+ case 96000:
+ case 88200:
+ return 7;
+
+ case 48000:
+ case 44100:
+ return 0;
+
+ case 32000:
+ case 33075:
+ return 1;
+
+ case 24000:
+ case 22050:
+ return 2;
+
+ case 16000:
+ case 14700:
+ return 3;
+
+ case 12000:
+ case 11025:
+ return 4;
+
+ case 8000:
+ case 7350:
+ return 5;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sun4i_codec_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 sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+ unsigned long clk_freq;
+ int ret, hwrate;
+ u32 val;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ clk_freq = sun4i_codec_get_mod_freq(params);
+ if (!clk_freq)
+ return -EINVAL;
+
+ ret = clk_set_rate(scodec->clk_module, clk_freq);
+ if (ret)
+ return ret;
+
+ hwrate = sun4i_codec_get_hw_rate(params);
+ if (hwrate < 0)
+ return hwrate;
+
+ /* Set DAC sample rate */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
+ hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
+
+ /* Set the number of channels we want to use */
+ if (params_channels(params) == 1)
+ val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
+ else
+ val = 0;
+
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
+ val);
+
+ /* Set the number of sample bits to either 16 or 24 bits */
+ if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
+
+ /* Set TX FIFO mode to padding the LSBs with 0 */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+ 0);
+
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ } else {
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+ 0);
+
+ /* Set TX FIFO mode to repeat the MSB */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
+
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ }
+
+ return 0;
+}
+
+static int sun4i_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ /*
+ * Stop issuing DRQ when we have room for less than 16 samples
+ * in our TX FIFO
+ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
+ 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
+
+ return clk_prepare_enable(scodec->clk_module);
+}
+
+static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(scodec->clk_module);
+}
+
+static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
+ .startup = sun4i_codec_startup,
+ .shutdown = sun4i_codec_shutdown,
+ .trigger = sun4i_codec_trigger,
+ .hw_params = sun4i_codec_hw_params,
+ .prepare = sun4i_codec_prepare,
+};
+
+static struct snd_soc_dai_driver sun4i_codec_dai = {
+ .name = "Codec",
+ .ops = &sun4i_codec_dai_ops,
+ .playback = {
+ .stream_name = "Codec Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 24,
+ },
+};
+
+/*** Codec ***/
+static const struct snd_kcontrol_new sun4i_codec_pa_mute =
+ SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
+
+static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
+ SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
+ sun4i_codec_pa_volume_scale),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
+ SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
+ /* Digital parts of the DACs */
+ SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
+ SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+ NULL, 0),
+
+ /* Analog parts of the DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
+
+ /* Mixers */
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ sun4i_codec_left_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ sun4i_codec_right_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_right_mixer_controls)),
+
+ /* Global Mixer Enable */
+ SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
+
+ /* Pre-Amplifier */
+ SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
+ SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
+ sun4i_codec_pa_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
+ SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0,
+ &sun4i_codec_pa_mute),
+
+ SND_SOC_DAPM_OUTPUT("HP Right"),
+ SND_SOC_DAPM_OUTPUT("HP Left"),
+};
+
+static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
+ /* Left DAC Routes */
+ { "Left DAC", NULL, "DAC" },
+
+ /* Right DAC Routes */
+ { "Right DAC", NULL, "DAC" },
+
+ /* Right Mixer Routes */
+ { "Right Mixer", NULL, "Mixer Enable" },
+ { "Right Mixer", "Left DAC Playback Switch", "Left DAC" },
+ { "Right Mixer", "Right DAC Playback Switch", "Right DAC" },
+
+ /* Left Mixer Routes */
+ { "Left Mixer", NULL, "Mixer Enable" },
+ { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
+
+ /* Pre-Amplifier Mixer Routes */
+ { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" },
+ { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" },
+ { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" },
+ { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" },
+
+ /* PA -> HP path */
+ { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" },
+ { "HP Right", NULL, "Pre-Amplifier Mute" },
+ { "HP Left", NULL, "Pre-Amplifier Mute" },
+};
+
+static struct snd_soc_codec_driver sun4i_codec_codec = {
+ .controls = sun4i_codec_widgets,
+ .num_controls = ARRAY_SIZE(sun4i_codec_widgets),
+ .dapm_widgets = sun4i_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets),
+ .dapm_routes = sun4i_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes),
+};
+
+static const struct snd_soc_component_driver sun4i_codec_component = {
+ .name = "sun4i-codec",
+};
+
+#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_8000_192000
+#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+ snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
+ NULL);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver dummy_cpu_dai = {
+ .name = "sun4i-codec-cpu-dai",
+ .probe = sun4i_codec_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SUN4I_CODEC_RATES,
+ .formats = SUN4I_CODEC_FORMATS,
+ .sig_bits = 24,
+ },
+};
+
+static const struct regmap_config sun4i_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+static const struct of_device_id sun4i_codec_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-codec" },
+ { .compatible = "allwinner,sun7i-a20-codec" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
+
+static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
+ int *num_links)
+{
+ struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
+ GFP_KERNEL);
+ if (!link)
+ return NULL;
+
+ link->name = "cdc";
+ link->stream_name = "CDC PCM";
+ link->codec_dai_name = "Codec";
+ link->cpu_dai_name = dev_name(dev);
+ link->codec_name = dev_name(dev);
+ link->platform_name = dev_name(dev);
+ link->dai_fmt = SND_SOC_DAIFMT_I2S;
+
+ *num_links = 1;
+
+ return link;
+};
+
+static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
+{
+ struct snd_soc_card *card;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return NULL;
+
+ card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+ if (!card->dai_link)
+ return NULL;
+
+ card->dev = dev;
+ card->name = "sun4i-codec";
+
+ return card;
+};
+
+static int sun4i_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sun4i_codec *scodec;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
+ if (!scodec)
+ return -ENOMEM;
+
+ scodec->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev, "Failed to map the registers\n");
+ return PTR_ERR(base);
+ }
+
+ scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sun4i_codec_regmap_config);
+ if (IS_ERR(scodec->regmap)) {
+ dev_err(&pdev->dev, "Failed to create our regmap\n");
+ return PTR_ERR(scodec->regmap);
+ }
+
+ /* Get the clocks from the DT */
+ scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
+ if (IS_ERR(scodec->clk_apb)) {
+ dev_err(&pdev->dev, "Failed to get the APB clock\n");
+ return PTR_ERR(scodec->clk_apb);
+ }
+
+ scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
+ if (IS_ERR(scodec->clk_module)) {
+ dev_err(&pdev->dev, "Failed to get the module clock\n");
+ return PTR_ERR(scodec->clk_module);
+ }
+
+ /* Enable the bus clock */
+ if (clk_prepare_enable(scodec->clk_apb)) {
+ dev_err(&pdev->dev, "Failed to enable the APB clock\n");
+ return -EINVAL;
+ }
+
+ /* DMA configuration for TX FIFO */
+ scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
+ scodec->playback_dma_data.maxburst = 4;
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
+ &sun4i_codec_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our codec\n");
+ goto err_clk_disable;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &sun4i_codec_component,
+ &dummy_cpu_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our DAI\n");
+ goto err_unregister_codec;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
+ goto err_unregister_codec;
+ }
+
+ card = sun4i_codec_create_card(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "Failed to create our card\n");
+ goto err_unregister_codec;
+ }
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, scodec);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our card\n");
+ goto err_unregister_codec;
+ }
+
+ return 0;
+
+err_unregister_codec:
+ snd_soc_unregister_codec(&pdev->dev);
+err_clk_disable:
+ clk_disable_unprepare(scodec->clk_apb);
+ return ret;
+}
+
+static int sun4i_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+ snd_soc_unregister_codec(&pdev->dev);
+ clk_disable_unprepare(scodec->clk_apb);
+
+ return 0;
+}
+
+static struct platform_driver sun4i_codec_driver = {
+ .driver = {
+ .name = "sun4i-codec",
+ .of_match_table = sun4i_codec_of_match,
+ },
+ .probe = sun4i_codec_probe,
+ .remove = sun4i_codec_remove,
+};
+module_platform_driver(sun4i_codec_driver);
+
+MODULE_DESCRIPTION("Allwinner A10 codec driver");
+MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index 4e0c0e502ade..ba9fc099cf67 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -152,6 +152,7 @@ static const struct of_device_id snd_soc_mop500_match[] = {
{ .compatible = "stericsson,snd-soc-mop500", },
{},
};
+MODULE_DEVICE_TABLE(of, snd_soc_mop500_match);
static struct platform_driver snd_soc_mop500_driver = {
.driver = {
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index f5df08ded770..6d5698b25bd4 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -522,9 +522,9 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
slots_active = hweight32(mask);
dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- slots_active, slots_active);
+ slots_active);
break;
default:
@@ -843,6 +843,7 @@ static const struct of_device_id ux500_msp_i2s_match[] = {
{ .compatible = "stericsson,ux500-msp-i2s", },
{},
};
+MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match);
static struct platform_driver msp_i2s_driver = {
.driver = {
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ef580b43f1e3..71778ca4b26a 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -122,6 +122,7 @@ struct snd_usb_substream {
unsigned int buffer_periods; /* current periods per buffer */
unsigned int altset_idx; /* USB data format: index of alternate setting */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
+ unsigned int tx_length_quirk:1; /* add length specifier to transfers */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index e6f71894ecdc..7b1cb365ffab 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -183,13 +183,53 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
ep->retire_data_urb(ep->data_subs, urb);
}
+static void prepare_silent_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
+{
+ struct urb *urb = ctx->urb;
+ unsigned int offs = 0;
+ unsigned int extra = 0;
+ __le32 packet_length;
+ int i;
+
+ /* For tx_length_quirk, put packet length at start of packet */
+ if (ep->chip->tx_length_quirk)
+ extra = sizeof(packet_length);
+
+ for (i = 0; i < ctx->packets; ++i) {
+ unsigned int offset;
+ unsigned int length;
+ int counts;
+
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
+ length = counts * ep->stride; /* number of silent bytes */
+ offset = offs * ep->stride + extra * i;
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = length + extra;
+ if (extra) {
+ packet_length = cpu_to_le32(length);
+ memcpy(urb->transfer_buffer + offset,
+ &packet_length, sizeof(packet_length));
+ }
+ memset(urb->transfer_buffer + offset + extra,
+ ep->silence_value, length);
+ offs += counts;
+ }
+
+ urb->number_of_packets = ctx->packets;
+ urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
+}
+
/*
* Prepare a PLAYBACK urb for submission to the bus.
*/
static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *ctx)
{
- int i;
struct urb *urb = ctx->urb;
unsigned char *cp = urb->transfer_buffer;
@@ -201,24 +241,7 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
ep->prepare_data_urb(ep->data_subs, urb);
} else {
/* no data provider, so send silence */
- unsigned int offs = 0;
- for (i = 0; i < ctx->packets; ++i) {
- int counts;
-
- if (ctx->packet_size[i])
- counts = ctx->packet_size[i];
- else
- counts = snd_usb_endpoint_next_packet_size(ep);
-
- urb->iso_frame_desc[i].offset = offs * ep->stride;
- urb->iso_frame_desc[i].length = counts * ep->stride;
- offs += counts;
- }
-
- urb->number_of_packets = ctx->packets;
- urb->transfer_buffer_length = offs * ep->stride;
- memset(urb->transfer_buffer, ep->silence_value,
- offs * ep->stride);
+ prepare_silent_urb(ep, ctx);
}
break;
@@ -594,6 +617,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
unsigned int max_packs_per_period, urbs_per_period, urb_packs;
unsigned int max_urbs, i;
int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+ int tx_length_quirk = (ep->chip->tx_length_quirk &&
+ usb_pipeout(ep->pipe));
if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
/*
@@ -610,13 +635,34 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
/* assume max. frequency is 25% higher than nominal */
ep->freqmax = ep->freqn + (ep->freqn >> 2);
- maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
- >> (16 - ep->datainterval);
+ /* Round up freqmax to nearest integer in order to calculate maximum
+ * packet size, which must represent a whole number of frames.
+ * This is accomplished by adding 0x0.ffff before converting the
+ * Q16.16 format into integer.
+ * In order to accurately calculate the maximum packet size when
+ * the data interval is more than 1 (i.e. ep->datainterval > 0),
+ * multiply by the data interval prior to rounding. For instance,
+ * a freqmax of 41 kHz will result in a max packet size of 6 (5.125)
+ * frames with a data interval of 1, but 11 (10.25) frames with a
+ * data interval of 2.
+ * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the
+ * maximum datainterval value of 3, at USB full speed, higher for
+ * USB high speed, noting that ep->freqmax is in units of
+ * frames per packet in Q16.16 format.)
+ */
+ maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) *
+ (frame_bits >> 3);
+ if (tx_length_quirk)
+ maxsize += sizeof(__le32); /* Space for length descriptor */
/* but wMaxPacketSize might reduce this */
if (ep->maxpacksize && ep->maxpacksize < maxsize) {
/* whatever fits into a max. size packet */
- maxsize = ep->maxpacksize;
- ep->freqmax = (maxsize / (frame_bits >> 3))
+ unsigned int data_maxsize = maxsize = ep->maxpacksize;
+
+ if (tx_length_quirk)
+ /* Need to remove the length descriptor to calc freq */
+ data_maxsize -= sizeof(__le32);
+ ep->freqmax = (data_maxsize / (frame_bits >> 3))
<< (16 - ep->datainterval);
}
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 417ebb11cf48..7661616f3636 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -1903,11 +1903,14 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi *umidi)
hostif = &intf->altsetting[1];
intfd = get_iface_desc(hostif);
+ /* If either or both of the endpoints support interrupt transfer,
+ * then use the alternate setting
+ */
if (intfd->bNumEndpoints != 2 ||
- (get_endpoint(hostif, 0)->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
- (get_endpoint(hostif, 1)->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+ !((get_endpoint(hostif, 0)->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT ||
+ (get_endpoint(hostif, 1)->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
return;
dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n",
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index d3608c0a29f3..fe91184ce832 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -338,7 +338,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
int index = kcontrol->private_value & 0xff;
- int value = ucontrol->value.integer.value[0];
+ unsigned int value = ucontrol->value.integer.value[0];
int old_value = kcontrol->private_value >> 8;
int err;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index cdac5179db3f..9245f52d43bd 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -1383,6 +1383,56 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
subs->hwptr_done++;
}
}
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
+ int offset, int stride, unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+
+ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - subs->hwptr_done;
+ memcpy(urb->transfer_buffer + offset,
+ runtime->dma_area + subs->hwptr_done, bytes1);
+ memcpy(urb->transfer_buffer + offset + bytes1,
+ runtime->dma_area, bytes - bytes1);
+ } else {
+ memcpy(urb->transfer_buffer + offset,
+ runtime->dma_area + subs->hwptr_done, bytes);
+ }
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
+ struct urb *urb, int stride,
+ unsigned int bytes)
+{
+ __le32 packet_length;
+ int i;
+
+ /* Put __le32 length descriptor at start of each packet. */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int length = urb->iso_frame_desc[i].length;
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+
+ packet_length = cpu_to_le32(length);
+ offset += i * sizeof(packet_length);
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length += sizeof(packet_length);
+ memcpy(urb->transfer_buffer + offset,
+ &packet_length, sizeof(packet_length));
+ copy_to_urb(subs, urb, offset + sizeof(packet_length),
+ stride, length);
+ }
+ /* Adjust transfer size accordingly. */
+ bytes += urb->number_of_packets * sizeof(packet_length);
+ return bytes;
}
static void prepare_playback_urb(struct snd_usb_substream *subs,
@@ -1460,27 +1510,17 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
}
subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
} else {
/* usual PCM */
- if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
- /* err, the transferred area goes over buffer boundary. */
- unsigned int bytes1 =
- runtime->buffer_size * stride - subs->hwptr_done;
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes1);
- memcpy(urb->transfer_buffer + bytes1,
- runtime->dma_area, bytes - bytes1);
- } else {
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes);
- }
-
- subs->hwptr_done += bytes;
+ if (!subs->tx_length_quirk)
+ copy_to_urb(subs, urb, 0, stride, bytes);
+ else
+ bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
+ /* bytes is now amount of outgoing data */
}
- if (subs->hwptr_done >= runtime->buffer_size * stride)
- subs->hwptr_done -= runtime->buffer_size * stride;
-
/* update delay with exact number of samples queued */
runtime->delay = subs->last_delay;
runtime->delay += frames;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index e4756651a52c..1a1e2e4df35e 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2664,6 +2664,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
+ USB_DEVICE(0x1235, 0x000a),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Novation", */
+ /* .product_name = "Nocturn", */
+ .ifnum = 0,
+ .type = QUIRK_MIDI_RAW_BYTES
+ }
+},
+{
USB_DEVICE(0x1235, 0x000e),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "Novation", */
@@ -3182,10 +3191,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
{
/*
* ZOOM R16/24 in audio interface mode.
- * Mixer descriptors are garbage, further quirks will be needed
- * to make any of it functional, thus disabled for now.
- * Playback stream appears to start and run fine but no sound
- * is produced, so also disabled for now.
+ * Playback requires an extra four byte LE length indicator
+ * at the start of each isochronous packet. This quirk is
+ * enabled in create_standard_audio_quirk().
*/
USB_DEVICE(0x1686, 0x00dd),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -3193,14 +3201,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
- /* Mixer */
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE,
- },
- {
/* Playback */
.ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE,
},
{
/* Capture */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 00ebc0ca008e..4897ea171194 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -115,6 +115,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
struct usb_interface_descriptor *altsd;
int err;
+ if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */
+ chip->tx_length_quirk = 1;
+
alts = &iface->altsetting[0];
altsd = get_iface_desc(alts);
err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 970086015cde..8ee14f2365e7 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -92,6 +92,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
subs->direction = stream;
subs->dev = as->chip->dev;
subs->txfr_quirk = as->chip->txfr_quirk;
+ subs->tx_length_quirk = as->chip->tx_length_quirk;
subs->speed = snd_usb_get_speed(subs->dev);
subs->pkt_offset_adj = 0;
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 33a176437e2e..15a12715bd05 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -43,6 +43,7 @@ struct snd_usb_audio {
atomic_t usage_count;
wait_queue_head_t shutdown_wait;
unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
+ unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
int num_interfaces;
int num_suspended_intf;